PYODBC 合并 sql 不起作用:参数标记数不等于提供的参数数。错误代码“HY000”

问题描述 投票:0回答:1

使用 pyodbc,我尝试使用 insert_values 执行 SQL 合并语句,insert_values 是一个由 120 个元组组成的列表,每个元组有四个元素,在 Azure SQL DB 表中插入或更新(更新插入)值:

[('2023', 'M12', 'December', '541.442'), 
 ('2023', 'M11', 'November', '486.639'), 
 ('2023', 'M10', 'October', '468.226'), 
 ('2023', 'M09', 'September', '478.802'), 
 ('2023', 'M08', 'August', '475.411'), 
 ('2023', 'M07', 'July', '471.109')]

我不断收到参数标记和参数数量不匹配的错误。我什至不知道如何格式化它,所以一些指导会更好。我可以插入到我的数据库中,并从中选择数据,但我只想插入新信息,并根据连接列匹配时价格列不同的情况更新现有信息。

('The SQL contains 480 parameter markers, but 120 parameters were supplied', 'HY000')
import pyodbc
import json
import textwrap
   
connection_string = """credentials that work"""

# connection object
obj_pyodbc_connection = pyodbc.connect(connection_string)

# create cursor boject
obj_pyodbc_cursor = obj_pyodbc_connection.cursor()

# Define record set
with open('BLS_source_ppi_data.json') as f:
    json_data = json.load(f)
    list_BLSIndex_records = []
    for var_series in json_data['Results']['series']:
        for output in var_series['data']:
            filtered_output = {k: v for k, v in output.items() if k in ('year', 'period', 'periodName', 'value')}
            list_BLSIndex_records.append(list(filtered_output.values()))

insert_values = list_BLSIndex_records

# Define Data Types
obj_pyodbc_cursor.setinputsizes(
    [
        (pyodbc.SQL_WVARCHAR, 255, 0),
        (pyodbc.SQL_WVARCHAR, 255, 0),
        (pyodbc.SQL_WVARCHAR, 255, 0),
        (pyodbc.SQL_WVARCHAR, 255, 0)
    ]
)

# Define table name
table_name = 'z_test_BLS_API'

# Define key columns
col_transaction_year = 'transaction_year'
col_month_num = 'month_num'

# Define other columns
col_month_name = 'month_name'

# Define update columns
col_price_nt = 'price_nt'

# Define values of insert statement
vals = ','.join(["(?, ?, ?, ?)" for _ in insert_values])
# vals = ','.join(["?" for _ in insert_values])


merge_sql = textwrap.dedent("""
MERGE INTO {table_name} AS trgt
USING (
    SELECT *
    FROM (VALUES {vals}) AS s (transaction_year, month_num, month_name, price_nt)
) AS Source
ON trgt.{trans_year} = Source.{trans_year} AND trgt.{month_num} = Source.{month_num}
WHEN NOT MATCHED THEN
    INSERT (transaction_year, month_num, month_name, price_nt)
    VALUES (Source.transaction_year, Source.month_num, Source.month_name, Source.price_nt)
WHEN MATCHED AND trgt.price_nt <> Source.price_nt THEN
    UPDATE SET {update_column} = Source.{update_column};
""").format(table_name=table_name, trans_year=col_transaction_year, update_column=col_price_nt, month_num=col_month_num, vals=vals)


# Execute Upsert
try:
    # obj_pyodbc_cursor.execute(merge_sql, insert_values)
    obj_pyodbc_cursor.execute(merge_sql, *insert_values)

# Rollback insert if there are errors
except Exception as e:
    obj_pyodbc_cursor.rollback()
    print(e)
    print('transaction rolled back')

# if there are no errors, commit the insert, print 'records inserted successfully', then close connection with sql
else:
    print('records inserted successfully')
    obj_pyodbc_cursor.commit()
    obj_pyodbc_connection.close()

我已经尝试实施这个解决方案,但我尝试的任何方法都不起作用。请帮助我理解 pyodbc 需要传递什么,列出插入数据的格式,才能发挥作用。我还没有找到任何关于数据必须采用的确切格式的文档,只是使用列表或列表或元组列表将值传递到 pyodbc 游标的教程。

我也尝试过使用 (?, ?, ?, ?) 作为值:

merge_sql = textwrap.dedent("""
MERGE INTO {table_name} AS trgt
USING (
    SELECT *
    FROM (VALUES (?, ?, ?, ?)) AS s (transaction_year, month_num, month_name, price_nt)
) AS Source
ON trgt.{trans_year} = Source.{trans_year} AND trgt.{month_num} = Source.{month_num}
WHEN NOT MATCHED THEN
    INSERT (transaction_year, month_num, month_name, price_nt)
    VALUES (Source.transaction_year, Source.month_num, Source.month_name, Source.price_nt)
WHEN MATCHED AND trgt.price_nt <> Source.price_nt THEN
    UPDATE SET {update_column} = Source.{update_column};
""").format(table_name=table_name, trans_year=col_transaction_year, update_column=col_price_nt, month_num=col_month_num, vals=vals)

但是我收到以下错误:

('The SQL contains 4 parameter markers, but 120 parameters were supplied', 'HY000')
python azure pyodbc
1个回答
0
投票

PYODBC 合并 sql 不起作用:参数标记数不等于提供的参数数。错误代码“HY000”

错误是由于构造

merge
语句并将参数传递给
execute
方法造成的。该语句必须采用正确的格式并包含正确数量的参数标记
?
。由于您使用的是要插入多行的
merge
语句,因此您应该动态构造该语句的
VALUES
部分以匹配要插入/更新的行数。

以下是已存储在Azure数据库表中的数据。

[('2023', 'M12', 'December', '541.401'),
('2023', 'M11', 'November', '486.601'),
('2023', 'M10', 'October', '468.226'),
('2023', 'M09', 'September', '478.802'),
('2023', 'M08', 'August', '475.444'),
('2023', 'M07', 'July', '471.109')]

以下是仅当使用新值修改值时才会插入的数据。

[('2023', 'M12', 'December', '541.111'), 
('2023', 'M11', 'November', '486.601'), 
('2023', 'M10', 'October', '468.222'), 
('2023', 'M09', 'September', '478.802'), 
('2023', 'M08', 'August', '475.333'), 
('2023', 'M07', 'July', '471.109'),
('2023', 'M06', 'June', '385.100')]

下面是代码,如果已经存在具有相同

transaction_year
month_num
但具有不同
month_name
price_nt
的行,它将更新这些值。如果某行不存在,它将使用
merge
语句将其插入到表中。

import pyodbc

connection_string = "*****"
conn = pyodbc.connect(connection_string)
cursor = conn.cursor()

data = [('2023', 'M12', 'December', '541.111'), 
        ('2023', 'M11', 'November', '486.601'), 
        ('2023', 'M10', 'October', '468.222'), 
        ('2023', 'M09', 'September', '478.802'), 
        ('2023', 'M08', 'August', '475.333'), 
        ('2023', 'M07', 'July', '471.109'),
        ('2023', 'M06', 'June', '385.100')]

merge_query = """
MERGE INTO z_test_BLS_API AS Target
USING (
    VALUES {}
) AS Source (transaction_year, month_num, month_name, price_nt)
ON Target.transaction_year = Source.transaction_year 
AND Target.month_num = Source.month_num
WHEN MATCHED AND (Target.month_name != Source.month_name OR Target.price_nt != Source.price_nt) THEN
    UPDATE SET Target.month_name = Source.month_name, Target.price_nt = Source.price_nt
WHEN NOT MATCHED THEN
    INSERT (transaction_year, month_num, month_name, price_nt) VALUES (Source.transaction_year, Source.month_num, Source.month_name, Source.price_nt);
""".format(','.join(['(?,?,?,?)' for _ in range(len(data))]))

params = [item for sublist in data for item in sublist]

cursor.execute(merge_query, params)

conn.commit()
cursor.close()
conn.close()

月份为

June
的记录是新记录,值
541.111
是分别插入和修改的新值,如下输出所示。

输出: enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.