Sequences are the springboards that enable PostgreSQL databases to vault beyond mundane, monotonous data – automatically generating unique identifiers to give wings to everything from primary keys to version numbers and more.
But raw sequence power alone isn‘t enough when complex business needs arise. Their potentials must be mastered across on-premise, multi-region toplogies.
That mastery journey starts with understanding the pivotal setval function – able reset sequence trajectories mid-flight with surgical precision.
In this comprehensive guide, we’ll uncover all there is know about harnessing sequences via setval, including:
- Common setval use cases with examples
- Performance optimization considerations
- Sequence locking and contention caveats
- Obscuring sequence values strategies
- Multi-region distribution factors
- Sequential data type pairings
And more. Along the way, we’ll crystallize the optimal approaches to mastering PostgreSQL sequences using setval.
A Primer on PostgreSQL Sequences
For those newer to PostgreSQL, sequences are special database objects that generate sequential numbers defined by a few key parameters:
Start – Beginning value
Increment – Step increase amount
Minvalue/Maxvalue – Lower and upper boundaries
Once created, sequences can be called via handy functions that automatically supply the next number in sequence:
-
nextval – Increment and return sequences next number
-
currval – Return current value most recently generated
For example:
CREATE SEQUENCE user_id_seq START 101 INCREMENT BY 1;
SELECT nextval(‘user_id_seq‘); -- Returns 101
SELECT nextval(‘user_id_seq‘); -- Returns 102
SELECT currval(‘user_id_seq‘); -- Returns 102
This automated numbering makes sequences extremely useful for:
-
Primary keys – Unique row identifiers like id columns
-
Transaction IDs – Ordering operations that happened over time
-
Version numbers – Tracking iteration order of data changes
And various other uses where monotonically incrementing values come in handy.
However, business needs evolve. And developers often need to manually intervenesequence trajectory midstream with surgical precision.
That‘s where the setval function enters the scene – with the power to rewrite the course sequenced values take by:
-
Resetting sequences to restart counting
-
Resuming values after new data loaded
-
Handling gaps gracefully
-
Accelerating bulk inserts through caching
And more. Let‘s explore some common examples.
Restarting Numbering Sequences
A basic but incredibly useful application of setval is restarting the counting on an existing sequence. Rather than needing to drop and recreate sequences to start from scratch.
For example, say we generate monthly customer order IDs that we want to reset each new year back to 1:
CREATE SEQUENCE order_id_seq START 1;
-- Code inserts order_ids starting at 1
-- ...year end, prepare for next...
SELECT setval(‘order_id_seq‘, 1); -- Reset to 1!
setval rewinds the sequence counter back to the starting value so numbering can start fresh. This avoids recreating sequences constantly.
Benefits include:
-
Consistent data types remain intact unlike drops
-
Permissions preserved
-
Parameters stay unchanged (increment, min/max)
Making setval restart far simpler than dropping/recreating sequences when requirements pivot.
Restarting Parallel Sequences
In distributed topologies, developers often leverage not just one but multiple sequences in parallel across shard clusters or regions.
Restarting requires coordinating resets across sequences consistently to avoid mismatched values.
For example, say we shard customer data across regions using id sequences:
Region A
nextval: cust_id_seq_a -> 501
currval: cust_id_seq_a -> 500
Region B
nextval: cust_id_seq_b -> 1001
currval: cust_id_seq_b -> 1000
Resetting requires care to realign:
Reset Parallel Sequences
SELECT setval(‘cust_id_seq_a‘, 1, false);
SELECT setval(‘cust_id_seq_b‘, 501, false);
This avoids cust_id collisions by bumping the second sequence to continue values right after the first. Keeping key continuity across the distributed topology.
Handling Gaps Gracefully
Gaps in sequences occasionally happen when records get inserted and deleted later non-sequentially.
For example, records 1-10 created. But record 5 gets removed:
Sequence Values
1, 2, 3, 4, 6, 7, 8 ,9 10
Rather than just resetting values back to 1, setval can smoothly resume right after the gap:
SELECT setval(‘id_sequence, 6);
This avoids reusing primary keys IDs that already existed. And provides graceful handles when gaps emerge.

Care is still required to avoid violations from parallel sequence gaps misaligning across distributed databases.
Accelerating Bulk Inserts
Sequences also unlock optimizations for bulk data loads inserting thousands of records in batches.
Typically, each inserted row would trigger a separate nextval sequence call to retrieve the next ID. Causing performance lag:
Bulk Inserts without Sequence Optimization

By instead caching sequence values in larger blocks during data imports using PostgreSQL‘s cache parameter, trips to the sequence object are minimized:
Cached Sequence Bulk Insert

Then setval can pick up counting right where the batch left off:
-- Imported 10,000 records
SELECT setval(‘id_sequence‘, 10000);
This sequence value caching strategy during bulk inserts avoids thousands of superfluous sequence hits.Accelerating performance dramatically.
Surfacing Sequences Reveals Intensity
One subtle drawback of sequences is they surface intensity of record generation over time directly in the incrementing IDs.
Exposing trends publicly often considered private business intelligence like:
-
User signup rates allowing extrapolation of totals
-
Transaction densities indicating revenue cycles
-
Activity frequency for exploiting vulnerabilities
For example, a sequence starting at 1 and currently sitting at 50,000 reveals avg signup rates.
Strategies like obscuring sequences through hashing mitigate this:
NEXTVAL(MD5(sequence_name));
Salted hashing hinders guessing raw values sequence values shielding trends from unintended eyes.
Weighing Setval vs Application Logic
While powerful, is setval the right tool for a given sequence reset task vs custom app logic?
For example, restarting annual sequences can be achieved either via:
setval
SELECT setval(‘order_id_seq‘, 1);
Application Logic
if month == 1:
run_sql("ALTER SEQUENCE order_id_seq RESTART WITH 1")
Considerations per approach:
| setval | Application Logic | |
|---|---|---|
| Scope | Database | App layer |
| Separations | Centralized | Scattered across code |
| Flexibility | Constrained by SQL capabilities | Endless options via programming |
| Performance | Optimized | Additional network trips |
| Locking | Potential contention | Application layer controls timing |
In general, setval simplifies restarts avoiding custom code. But some edge cases may justify application logic handling more nuanced needs SQL can’t satisfy.
Sequence Lock Contention Risks
One key caveat of setval is it acquires exclusive locks on sequence objects while running. Blocking other operations targeting the same sequence.
For high volume sequences under constant barrage, setval interventions risk brutal blocking cascades.
Visualizing a Brutal Setval Locking Cascade

Strategies to mitigate lock contention risks include:
- Tune CACHE parameter – Reduce frequency of hitting sequences
- Batch sequence ops – Consolidate resets into batches
- Lookahead preallocation – Cache ahead values avoiding locks
- Parallel sequences – Shard sequence ranges across multiple objects
- Queue resets offline – Schedule setval during maintenance windows
Carefully assessing risks of sequence contention and planning ahead helps smooth setval integrations.
Multi-Region Sequence Considerations
In globally distributed PostgreSQL deployments, sequences enable uniquely identifying records across logical databases scattered geographically.
Strategies like id sharding prevent collisions:
Region A
- Sequence A – 1 to 1,000,000
- Setval offsets to ensure no gaps between regions
Region B
- Sequence B – 1,000,001 to 2,000,000
This works smoothly until restarts are needed. Resetting sequences requires planning to realign continuity across all regions concurrently:
Multi-Region Sequence Resets

Without coordination, gaps emerge rapidly causing primary key disasters. Setval equips admins to orchestrate controlled resets globally.
Sequences Breadth – Numeric vs UUID vs Custom
While the most common use case, numbered sequences are just one data type that can be sequenced in PostgreSQL.
Sequences can produce more than incrementing numbers, but also:
- UUIDs – For obscuring record ids
- Mappings – Custom data type transformations
- Geometry – Generate coordinate series
- Timescales – Sequential timestamps
For example, a UUID sequence:
CREATE SEQUENCE uid_seq AS uuid;
SELECT nextval(‘uid_seq‘);
-- 8aea0f1b-8827-4d58-9bb8-d8bc505c7f59
SELECT nextval(‘uid_seq‘);
-- 60373941-1650-4c64-8a32-f421e7bc49dc
This opens sequences to alternative data types beyond numeric, like hashes and geospatial values.
PostgreSQL sequences serve up the automatic numbering that powers everything from primary keys to version history.
And setval provides precise control to surgically reshape sequence trajectory on demand through graceful restarts, bulk optimizations, gap handling, and more.
By mastering sequences broadly alongside setval specifically, developers wield annotations that liberate data from the mundane…positioning PostgreSQL to play a transformative role in building bold applications brimming with potential.
The only limit is your imagination.


