SQL 服务器锁定行,读取和更新在 Python 中不起作用
SQL Server Lock the Row the Read and Update not working in Python
我需要在 SQL 服务器和 Python 中锁定一行,读取然后更新该行的一个字段,用于可以同时 运行 多个进程的程序时间,以避免竞争条件。
我正在使用以下代码,但在 Python 中它更新了一个 ID(即 3411)和 returns 另一个 ID(即 3071)。如果我直接在 SSMS 中 运行 相同的 SQL 代码,它会更新和 returns 相同的 Id。
import pyodbc
def read_and_update_files_queue(self):
conn = pyodbc.connect('Driver={SQL Server};'
'Server='+self.server+';'
'Database='+self.database+';'
'Trusted_Connection=yes;')
cursor = conn.cursor()
sql_query=f'''
BEGIN TRANSACTION;
DECLARE @Temp_ID AS INT
SELECT TOP 1 @Temp_ID=Id
FROM dbo.[Queue]
WITH(XLOCK, ROWLOCK)
WHERE [BQ_status] IS NULL and upload_date IS NOT NULL
Select TOP 1
Id,GCS_path,file_first_upload
FROM dbo.[Queue]
WITH(XLOCK, ROWLOCK)
WHERE Id=@Temp_ID
UPDATE dbo.[Queue] SET BQ_status='Loading'
WHERE Id=@Temp_ID
COMMIT TRANSACTION;
'''
cursor.execute(sql_query)
cursor.commit()
怎么了?
代码基于此:
In SQL Server, how can I lock a single row in a way similar to Oracle's "SELECT FOR UPDATE WAIT"?
谢谢!!
因为前两条语句不是修改数据,you need the UPDLOCK
hint, otherwise it will not hold the lock til the end of the transaction, irrespective of any other hints, including XLOCK
.
但是您可以在一条语句中完成所有操作,这里不需要 UPDLOCK
,也不需要显式事务(该语句无论如何都在事务中运行)
UPDATE TOP (1) q
SET BQ_status = 'Loading'
OUTPUT inserted.Id, inserted.GCS_path, inserted.file_first_upload
FROM dbo.[Queue] AS q WITH (SERIALIZABLE, ROWLOCK)
WHERE q.[BQ_status] IS NULL and q.upload_date IS NOT NULL;
虽然 SERIALIZABLE
有时确实只使用共享 S
锁,但这 仅 对于 SELECT
或在 DML 语句中未修改加入 tables。
在这种情况下,由于 Queue
是正在修改的 table,因此在找到正确的行时会使用 U
锁,这禁止来自其他行的 U
锁交易,这将被阻止等待(一个交易只做 SELECT
不会被阻止)。然后,当一行即将被修改时,U
锁升级为X
,被修改
所以这里不用担心竞争条件,也不需要 UPDLOCK
提示。
我需要在 SQL 服务器和 Python 中锁定一行,读取然后更新该行的一个字段,用于可以同时 运行 多个进程的程序时间,以避免竞争条件。
我正在使用以下代码,但在 Python 中它更新了一个 ID(即 3411)和 returns 另一个 ID(即 3071)。如果我直接在 SSMS 中 运行 相同的 SQL 代码,它会更新和 returns 相同的 Id。
import pyodbc
def read_and_update_files_queue(self):
conn = pyodbc.connect('Driver={SQL Server};'
'Server='+self.server+';'
'Database='+self.database+';'
'Trusted_Connection=yes;')
cursor = conn.cursor()
sql_query=f'''
BEGIN TRANSACTION;
DECLARE @Temp_ID AS INT
SELECT TOP 1 @Temp_ID=Id
FROM dbo.[Queue]
WITH(XLOCK, ROWLOCK)
WHERE [BQ_status] IS NULL and upload_date IS NOT NULL
Select TOP 1
Id,GCS_path,file_first_upload
FROM dbo.[Queue]
WITH(XLOCK, ROWLOCK)
WHERE Id=@Temp_ID
UPDATE dbo.[Queue] SET BQ_status='Loading'
WHERE Id=@Temp_ID
COMMIT TRANSACTION;
'''
cursor.execute(sql_query)
cursor.commit()
怎么了? 代码基于此: In SQL Server, how can I lock a single row in a way similar to Oracle's "SELECT FOR UPDATE WAIT"? 谢谢!!
因为前两条语句不是修改数据,you need the UPDLOCK
hint, otherwise it will not hold the lock til the end of the transaction, irrespective of any other hints, including XLOCK
.
但是您可以在一条语句中完成所有操作,这里不需要 UPDLOCK
,也不需要显式事务(该语句无论如何都在事务中运行)
UPDATE TOP (1) q
SET BQ_status = 'Loading'
OUTPUT inserted.Id, inserted.GCS_path, inserted.file_first_upload
FROM dbo.[Queue] AS q WITH (SERIALIZABLE, ROWLOCK)
WHERE q.[BQ_status] IS NULL and q.upload_date IS NOT NULL;
虽然 SERIALIZABLE
有时确实只使用共享 S
锁,但这 仅 对于 SELECT
或在 DML 语句中未修改加入 tables。
在这种情况下,由于 Queue
是正在修改的 table,因此在找到正确的行时会使用 U
锁,这禁止来自其他行的 U
锁交易,这将被阻止等待(一个交易只做 SELECT
不会被阻止)。然后,当一行即将被修改时,U
锁升级为X
,被修改
所以这里不用担心竞争条件,也不需要 UPDLOCK
提示。