- To bulk-insert data into an existing table, batch multiple rows in one multi-row
INSERTstatement and do not include theINSERTstatements within a transaction. Experimentally determine the optimal batch size for your application by monitoring the performance for different batch sizes (10 rows, 100 rows, 1000 rows).
You can also use the
IMPORT INTO statement to bulk-insert CSV data into an existing
table.-
Use
gen_random_uuid(): Generates a UUIDv4 withUUIDdata type. -
Use
uuid_v4(): Generates a UUIDv4 withBYTESdata type. -
Use
unique_rowid(): Generates a globally uniqueINTdata type For performance reasons, if you are going to use UUIDs, Cockroach Labs strongly recommends using UUIDv4 as defined by RFC 4122. This is the format generated by thegen_random_uuid()anduuid_v4()built-in functions. Other types of UUID are largely untested with CockroachDB and will require performance testing to avoid hotspots.
Use gen_random_uuid()
To use the UUID column with the gen_random_uuid() function as the default value:
Use uuid_v4()
Alternatively, you can use the BYTES column with the uuid_v4() function as the default value:
Use unique_rowid()
If it is important for generated IDs to be stored in the same key-value range, you can use an integer type with the unique_rowid() function as the default value, either explicitly or via the SERIAL pseudo-type:
unique_rowid() function generates a default value from the timestamp and ID of the node executing the insert. Such time-ordered values are likely to be globally unique except in cases where a very large number of IDs (100,000+) are generated per node per second. Also, there can be gaps and the order is not completely guaranteed.
To understand the differences between the UUID and unique_rowid() options, see the SQL FAQs. For further background on UUIDs, see What is a UUID, and Why Should You Care?.
Sequential numbers can be generated in CockroachDB using the unique_rowid() built-in function or using SQL sequences. However, note the following considerations:
- Unless you need roughly-ordered numbers, use
UUIDvalues instead. See the previous FAQ for details. - Sequences produce unique values. However, not all values are guaranteed to be produced (e.g., when a transaction is canceled after it consumes a value) and the values may be slightly reordered (e.g., when a transaction that consumes a lower sequence number commits after a transaction that consumes a higher number).
- For maximum performance, avoid using sequences or
unique_rowid()to generate row IDs or indexed columns. Values generated in these ways are logically close to each other and can cause contention on a few data ranges during inserts. Instead, preferUUIDidentifiers. - We discourage indexing on sequential keys. If a table must be indexed on sequential keys, use hash-sharded indexes. Hash-sharded indexes distribute sequential traffic uniformly across ranges, eliminating single-range hotspots and improving write performance on sequentially-keyed indexes at a small cost to read performance.
Most use cases that ask for a strong time-based write ordering can be solved with other, more distribution-friendly solutions instead. For example, CockroachDB’s time travel queries (
AS OF SYSTEM TIME) support the following:
- Paginating through all the changes to a table or dataset
- Determining the order of changes to data over time
- Determining the state of data at some point in the past
- Determining the changes to data between two points of time
unique_rowid(), described in the previous FAQ entries, also provide an approximate time ordering.
However, if your application absolutely requires strong time-based write ordering, it is possible to create a strictly monotonic counter in CockroachDB that increases over time as follows:
- Initially:
CREATE TABLE cnt(val INT PRIMARY KEY); INSERT INTO cnt(val) VALUES(1); - In each transaction:
INSERT INTO cnt(val) SELECT max(val)+1 FROM cnt RETURNING val;
INSERT transactions to conflict with each other and effectively force the transactions to commit one at a time throughout the cluster, which in turn guarantees the values generated in this way are strictly increasing over time without gaps. The caveat is that performance is severely limited as a result.
If you find yourself interested in this problem, please contact us and describe your situation. We would be glad to help you find alternative solutions and possibly extend CockroachDB to better match your needs.
There’s no function in CockroachDB for returning last inserted values, but you can use the RETURNING clause of the INSERT statement.
For example, this is how you’d use RETURNING to return a value auto-generated via unique_rowid() or SERIAL:
JSONB data type is supported.
To see which indexes CockroachDB is using for a given query, you can use the EXPLAIN statement, which will print out the query plan, including any indexes that are being used:
UUID.
When an ORDER BY clause is not used in a query, rows are processed or returned in a non-deterministic order. “Non-deterministic” means that the actual order can depend on the logical plan, the order of data on disk, the topology of the CockroachDB cluster, and is generally variable over time.
In CockroachDB, all INTs are represented with 64 bits of precision, but JavaScript numbers only have 53 bits of precision. This means that large integers stored in CockroachDB are not exactly representable as JavaScript numbers. For example, JavaScript will round the integer 235191684988928001 to the nearest representable value, 235191684988928000. Notice that the last digit is different. This is particularly problematic when using the unique_rowid() function, since unique_rowid() nearly always returns integers that require more than 53 bits of precision to represent.
To avoid this loss of precision, Node’s pg driver will, by default, return all CockroachDB INTs as strings.
idString, you can simply use idString directly, even where an INT type is expected. The string will automatically be coerced into a CockroachDB INT.
INTs in JavaScript, you will need to use a big integer library like Long.js. Do not use the built-in parseInt function.
INSERT/UPSERT/UPDATE/DELETE statements translate to key-value operations with minimal overhead (single digit percent slowdowns). For example, the following UPSERT to add or replace a row in the table would translate into a single key-value Put operation:

