DELETE statement deletes rows from a table.
To delete columns, see
ALTER TABLE ... DROP COLUMN.Required privileges
The user must have theDELETE and SELECT privileges on the table.
Synopsis
Parameters
Success responses
SuccessfulDELETE statements return one of the following:
Disk space usage after deletes
Deleting a row does not immediately free up the disk space. This is due to the fact that CockroachDB retains the ability to query tables historically. If disk usage is a concern, the solution is to reduce the time-to-live (TTL) for the zone by settinggc.ttlseconds to a lower value, which will cause
garbage collection to clean up deleted objects (rows, tables) more
frequently.
For instructions on how to free up disk space as quickly as possible after dropping a table, see How can I free up disk space that was used by a dropped table?
Select performance on deleted rows
Queries that scan across tables that have lots of deleted rows will have to scan over deletions that have not yet been garbage collected. Certain database usage patterns that frequently scan over and delete lots of rows will want to reduce the time-to-live values to clean up deleted rows more frequently.Sorting the output of deletes
To sort the output of aDELETE statement, use:
Force index selection for deletes
By using the explicit index annotation (also known as “index hinting”), you can override CockroachDB’s index selection and use a specific index for deleting rows of a named table.Index selection can impact performance, but does not change the result of a query.
EXPLAIN statement. To see all indexes available on a table, use SHOW INDEXES.
For examples, see Delete with index hints.
You can use the @primary alias to use the table’s primary key in your query if no secondary index explicitly named primary exists on that table.
Preserving DELETE performance over time
CockroachDB relies on multi-version concurrency control (MVCC) to process concurrent requests while guaranteeing strong consistency. As such, when you delete a row, it is not immediately removed from disk. The MVCC values for the row will remain until the garbage collection period defined by the gc.ttlseconds variable in the applicable zone configuration has passed.
This means that with the default settings, each iteration of your DELETE statement must scan over all of the rows previously marked for deletion within the defined GC TTL window. If you try to delete 10,000 rows 10 times within the GC TTL window, the 10th command will have to scan over the 90,000 rows previously marked for deletion.
To preserve performance over iterative DELETE queries, we recommend taking one of the following approaches:
- At each iteration, update the
WHEREclause to filter only the rows that have not yet been marked for deletion. For an example, see Batch-delete on an indexed filter. - At each iteration, first use a
SELECTstatement to return primary key values on rows that are not yet deleted. Rows marked for deletion will not be returned. Then, use a nestedDELETEloop over a smaller batch size, filtering on the primary key values. For an example, see Batch delete on a non-indexed column. - To iteratively delete rows in constant time, using a simple
DELETEloop, you can alter your zone configuration and changegc.ttlsecondsto a low value like 5 minutes (i.e.,300), and then run yourDELETEstatement once per GC interval.
Examples
Setup
The following examples use MovR, a fictional vehicle-sharing application, to demonstrate CockroachDB SQL statements. For more information about the MovR example application and dataset, see MovR: A Global Vehicle-sharing App. To follow along, runcockroach demo to start a temporary, in-memory cluster with the movr dataset preloaded:
Delete rows using primary key/unique columns
Using columns with the Primary Key or Unique constraints to delete rows ensures your statement is unambiguous — no two rows contain the same column value, so it’s less likely to delete data unintentionally. In this example,code is our primary key and we want to delete the row where the code equals “about_stuff_city”. Because we’re positive no other rows have that value in the code column, there’s no risk of accidentally removing another row.
Delete rows using non-unique columns
Deleting rows using non-unique columns removes every row that returnsTRUE for the WHERE clause’s a_expr. This can easily result in deleting data you didn’t intend to.
Delete rows using a table join
You can delete rows based on a table join. Use theUSING clause to specify another table.
The following example deletes all codes from promo_codes that are present in user_promo_codes:
Return deleted rows
To see which rows your statement deleted, include theRETURNING clause to retrieve them using the columns you specify.
Use all columns
By specifying*, you retrieve all columns of the delete rows.
Use specific columns
To retrieve specific columns, name them in theRETURNING clause.
Change column labels
WhenRETURNING specific columns, you can change their labels using AS.
Sort and return deleted rows
To sort and return deleted rows, use a statement like the following:Delete with index hints
Suppose you create a multi-column index on theusers table with the name and city columns.
EXPLAIN (OPT) command to see how the cost-based optimizer decides to perform the delete:
EXPLAIN statement shows that the optimizer scans the newly-created users_name_city_idx index when performing the delete. This makes sense, as you are performing a delete based on the name column.
Now suppose that instead you want to perform a delete, but using the id column instead.
users_name_city_idx index when performing the delete. Although scanning the table on this index could still be the most efficient, you may want to assess the performance difference between using users_name_city_idx and an index on the id column, as you are performing a delete with a filter on the id column.
If you provide an index hint (i.e., force the index selection) to use the primary index on the column instead, the CockroachDB will scan the users table using the primary index, on city, and id.

