-- ANTI-PATTERN: SENDING AN UPDATE WITHOUT THE PRIMARY KEY COLUMN-- IN THE WHERE CLAUSEUPDATESingersSETFirstName="Marcel"WHEREFirstName="Marc"ANDLastName="Richards";
为使该更新更加高效,请在 WHERE 子句中添加 SingerId 列。SingerId 列是 Singers 表的唯一主键列:
-- ANTI-PATTERN: SENDING AN UPDATE THAT MUST SCAN THE ENTIRE TABLEUPDATESingersSETFirstName="Marcel"WHEREFirstName="Marc"ANDLastName="Richards"
如果 FirstName 或 LastName 上没有索引,您需要扫描整个表来查找目标歌手。如果您不想添加二级索引来提高更新效率,请在 WHERE 子句中添加 SingerId 列。
[[["易于理解","easyToUnderstand","thumb-up"],["解决了我的问题","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["很难理解","hardToUnderstand","thumb-down"],["信息或示例代码不正确","incorrectInformationOrSampleCode","thumb-down"],["没有我需要的信息/示例","missingTheInformationSamplesINeed","thumb-down"],["翻译问题","translationIssue","thumb-down"],["其他","otherDown","thumb-down"]],["最后更新时间 (UTC):2025-08-11。"],[],[],null,["# Data Manipulation Language best practices\n\nThis page describes best practices for using Data Manipulation Language (DML)\nand Partitioned DML for GoogleSQL-dialect databases and PostgreSQL-dialect databases.\n\nUse a `WHERE` clause to reduce the scope of locks\n-------------------------------------------------\n\n\nYou execute DML statements inside read-write transactions. When Spanner reads data, it\nacquires shared read locks on limited portions of the row ranges that you read. Specifically, it\nacquires these locks only on the columns you access. The locks can include data that does not\nsatisfy the filter condition of the `WHERE` clause.\n\n\nWhen Spanner modifies data using DML statements, it acquires exclusive locks on the\nspecific data that you are modifying. In addition, it acquires shared locks in the same way as\nwhen you read data. If your request includes large row ranges, or an entire table, the shared\nlocks might prevent other transactions from making progress in parallel.\n\n\nTo modify data as efficiently as possible, use a `WHERE` clause that enables\nSpanner to read only the necessary rows. You can achieve this goal with a filter on the\nprimary key, or on the key of a secondary index. The `WHERE` clause limits the scope of\nthe shared locks and enables Spanner to process the update more efficiently.\n\n\nFor example, suppose that one of the musicians in the `Singers` table changes their\nfirst name, and you need to update the name in your database. You could execute the following DML\nstatement, but it forces Spanner to scan the entire table and acquires shared locks that\ncover the entire table. As a result, Spanner must read more data than necessary, and\nconcurrent transactions cannot modify the data in parallel: \n\n -- ANTI-PATTERN: SENDING AN UPDATE WITHOUT THE PRIMARY KEY COLUMN\n -- IN THE WHERE CLAUSE\n\n UPDATE Singers SET FirstName = \"Marcel\"\n WHERE FirstName = \"Marc\" AND LastName = \"Richards\";\n\n\nTo make the update more efficient, include the `SingerId` column in the\n`WHERE` clause. The `SingerId` column is the only primary key column for\nthe `Singers` table: \n\n -- ANTI-PATTERN: SENDING AN UPDATE THAT MUST SCAN THE ENTIRE TABLE\n\n UPDATE Singers SET FirstName = \"Marcel\"\n WHERE FirstName = \"Marc\" AND LastName = \"Richards\"\n\n\nIf there is no index on `FirstName` or `LastName`, you need to\nscan the entire table to find the target singers. If you don't want to add a secondary\nindex to make the update more efficient, then include the `SingerId` column\nin the `WHERE` clause.\n\n\nThe `SingerId` column is the only primary key column for the\n`Singers` table. To find it, run `SELECT` in a separate,\nread-only transaction prior to the update transaction: \n\n\n SELECT SingerId\n FROM Singers\n WHERE FirstName = \"Marc\" AND LastName = \"Richards\"\n\n -- Recommended: Including a seekable filter in the where clause\n\n UPDATE Singers SET FirstName = \"Marcel\"\n WHERE SingerId = 1;\n\n\u003cbr /\u003e\n\nAvoid using DML statements and mutations in the same transaction\n----------------------------------------------------------------\n\nSpanner buffers insertions, updates, and deletions performed\nusing DML statements on the server-side, and the results are visible to\nsubsequent SQL and DML statements within the same transaction. This behavior is\ndifferent from the [Mutation API](/spanner/docs/modify-mutation-api), where\nSpanner buffers the mutations on the client-side and sends the\nmutations server-side as part of the commit operation. As a result, mutations in\nthe commit request aren't visible to SQL or DML statements within the same\ntransaction.\n\nAvoid using both DML statements and mutations in the same transaction. If you\nuse both in the same transaction, you need to account for the order of execution\nin your client library code. If a transaction contains both DML statements and\nmutations in the same request, Spanner executes the DML\nstatements before the mutations.\n\nFor operations that are only supported using mutations, you might want to\ncombine DML statements and mutations in the same transaction---for example,\n[`insert_or_update`](/spanner/docs/reference/rpc/google.spanner.v1#google.spanner.v1.Mutation).\n\nIf you use both, the buffer writes only at the very end of the transaction.\n\nUse the `PENDING_COMMIT_TIMESTAMP` function to write commit timestamps\n----------------------------------------------------------------------\n\n### GoogleSQL\n\n\nYou use the [PENDING_COMMIT_TIMESTAMP](/spanner/docs/reference/standard-sql/timestamp_functions#pending_commit_timestamp) function to write the commit\ntimestamp in a DML statement. Spanner selects the commit timestamp when the transaction\ncommits.\n\n| **Note:** After you call the `PENDING_COMMIT_TIMESTAMP` function, the table and any derived index is unreadable to any future SQL statements in the transaction. Because of this, the change stream can't extract the previous value for the column that has a pending commit timestamp, if the coloumn is modified again later in the same transaction. You must write commit timestamps as the last statement in a transaction to prevent the possibility of trying to read the table. If you try to read the table, then Spanner produces an error.\n\n\u003cbr /\u003e\n\n### PostgreSQL\n\n\nYou use the `SPANNER.PENDING_COMMIT_TIMESTAMP()` function to write the commit\ntimestamp in a DML statement. Spanner selects the commit timestamp when the transaction\ncommits.\n\n| **Note:** After you call the `SPANNER.PENDING_COMMIT_TIMESTAMP()` function, the table and any derived index is unreadable to any subsequent SQL statements in the transaction. You must write commit timestamps as the last statement in a transaction to prevent the possibility of trying to read the table. If you try to read the table, then Spanner returns an error.\n\n\u003cbr /\u003e\n\nPartitioned DML and date and timestamp functions\n------------------------------------------------\n\nPartitioned DML uses one or more transactions that might run and commit at\ndifferent times. If you use the [date](/spanner/docs/reference/standard-sql/date_functions) or\n[timestamp](/spanner/docs/reference/standard-sql/timestamp_functions) functions, the modified rows might\ncontain different values.\n\nImprove latency with Batch DML\n------------------------------\n\nTo reduce latency, use [batch DML](/spanner/docs/dml-tasks#use-batch) to send\nmultiple DML statements to Spanner within a single client-server\nround trip.\n\nBatch DML can apply optimizations to groups of statements within a batch to\nenable faster and more efficient data updates.\n\n- **Execute writes with a single request**\n\n Spanner automatically optimizes contiguous groups of similar\n `INSERT`, `UPDATE`, or `DELETE` batched statements that have different\n parameter values, if they don't violate data dependencies.\n\n For example, consider a scenario where you want to insert a large set of new\n rows into a table called `Albums`. To let Spanner optimize\n all the required `INSERT` statements into a single, efficient server-side\n action, begin by writing an appropriate DML statement that uses SQL query\n parameters: \n\n INSERT INTO Albums (SingerId, AlbumId, AlbumTitle) VALUES (@Singer, @Album, @Title);\n\n Then, send Spanner a DML batch that invokes this statement\n repeatedly and contiguously, with the repetitions differing only in the\n values you bind to the statement's three query parameters.\n Spanner optimizes these structurally identical DML\n statements into a single server-side operation before executing it.\n- **Execute writes in parallel**\n\n Spanner automatically optimizes contiguous groups of DML\n statements by executing in parallel when doing so doesn't violate data\n dependencies. This optimization brings performance benefits to a wider set\n of batched DML statements because it can apply to a mix of DML statement\n types (`INSERT`, `UPDATE` and `DELETE`) and to both parameterized or\n non-parameterized DML statements.\n\n For example, our sample schema has the tables `Singers`, `Albums`, and\n `Accounts`. `Albums` is interleaved within `Singers` and stores information\n about albums for `Singers`. The following contiguous group of statements\n writes new rows to multiple tables and doesn't have complex data\n dependencies. \n\n INSERT INTO Singers (SingerId, Name) VALUES(1, \"John Doe\");\n INSERT INTO Singers (SingerId, Name) VALUES(2, \"Marcel Richards\");\n INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (1, 10001, \"Album 1\");\n INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (1, 10002, \"Album 2\");\n INSERT INTO Albums(SingerId, AlbumId, AlbumTitle) VALUES (2, 10001, \"Album 1\");\n UPDATE Accounts SET Balance = 100 WHERE AccountId = @AccountId;\n\n Spanner optimizes this group of DML statements by executing\n the statements in parallel. The writes are applied in order of the\n statements in the batch and maintains batch DML semantics if a statement\n fails during execution."]]