使用 Python 游标在 SQL 服务器中执行多变量查询

Execute multiple variable query in SQL Server using Python cursor

我正在尝试使用 pymssql 库在 SQL 服务器中执行多个查询。

这是我的代码:

cur = conn.cursor()
cur.execute("DECLARE @begin_time datetime, @end_time datetime, @from_lsn binary(10), @to_lsn binary(10);  
             SET @begin_time = DATEADD(day, -1, GETDATE()) ;  
             SET @end_time = GETDATE(); 
             SET @from_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than or equal', @begin_time);  
             SET @to_lsn = sys.fn_cdc_map_time_to_lsn('largest less than or equal', @end_time);
             SELECT * FROM cdc.fn_cdc_get_net_changes_dbo_users(@from_lsn, @to_lsn, 'all');")
output = cur.fetchall()
print(output)
conn.close()

代码 运行 正常并获取结果,但是当我使用 Python 库计算日期并将其传递给代码时,出现错误。

示例代码

from datetime import datetime, timedelta
end_date = datetime.now()
start_date = datetime.now() + timedelta(hours=-1)

cur = conn.cursor()
query =  f"""DECLARE @begin_time datetime, @end_time datetime, @from_lsn binary(10), @to_lsn binary(10);  
                SET @begin_time = {start_date};  
                SET @end_time = {end_date}; 
                SET @from_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than or equal', @begin_time);  
                SET @to_lsn = sys.fn_cdc_map_time_to_lsn('largest less than or equal', @end_time); 
                SELECT * FROM cdc.fn_cdc_get_all_changes_dbo_users (@from_lsn, @to_lsn, 'all');"""
print(query)
cur.execute(query)
output = cur.fetchall()
print(output)
conn.close()

错误:

ProgrammingError: (102, Incorrect syntax near '11'.

DB-Lib error message 20018, severity 15:
General SQL Server error: Check messages from the SQL Server

我不确定我做错了什么。如果有人能帮助我并解释这个问题,我将不胜感激。

考虑时间变量的实际 SQL 参数化,而不是字符串插值或与 F 字符串的连接,这对于将值从应用程序层传递到后端数据库通常是不安全或不高效的。库 pymssql 支持 parameters。 Python 的 datetime.datetime 应该转换为 MSSQL 的 DATETIME.

# PREPARED STATEMENTS WITH %s PLACEHOLDERS
query = """DECLARE @begin_time datetime, @end_time datetime, @from_lsn binary(10), @to_lsn binary(10);  
           SET @begin_time = %s;  
           SET @end_time = %s; 
           SET @from_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than or equal', @begin_time);  
           SET @to_lsn = sys.fn_cdc_map_time_to_lsn('largest less than or equal', @end_time); 
           SELECT * FROM cdc.fn_cdc_get_all_changes_dbo_users (@from_lsn, @to_lsn, 'all');
        """
print(query)

# EXECUTE QUERY WITH BINDED PARAMS
cur.execute(query, [start_date, end_date])

事实上,您可以缩短查询,因为参数不需要声明:

# PREPARED STATEMENTS WITH %s PLACEHOLDERS
query = """DECLARE @from_lsn binary(10), @to_lsn binary(10);
           SET @from_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than or equal', %s);  
           SET @to_lsn = sys.fn_cdc_map_time_to_lsn('largest less than or equal', %s); 
           SELECT * FROM cdc.fn_cdc_get_all_changes_dbo_users (@from_lsn, @to_lsn, 'all');
        """

顺便说一句,请注意 pymmsql 不再是维护库。对于 Python-SQL 服务器连接,考虑 pyodbc 以获得最安全、更新的 DB-API。但请注意 pyodbc 的参数占位符是 qmarks ? 而不是 %s(与大多数 Python DB-APIs 不同)。