用于百分比计算的查询参数存在语法错误

Syntax error with query parameter for percentage calculation

我有一个运行良好的 pyodbc 查询,但我正在尝试应用更新以将新的计算字段添加到结果中。工作查询是:

cursor = cnxn.cursor()
cursor.execute("SELECT RestaurantLocation.ID, Restaurant.DisplayName FROM 
    Restaurant INNER JOIN RestaurantLocation ON Restaurant.ID = 
    RestaurantLocation.Restaurant_ID WHERE (RestaurantLocation.Active = 1)")
row = cursor.fetchall()

columns = [column[0] for column in cursor.description]
for ID,DisplayName in row:
    DisplayName = DisplayName.replace(" ","")
    cursor = cnxn.cursor()
    cursor.execute("SET NOCOUNT ON; SELECT ScheduledService.ServiceDate, 
    Restaurant.DisplayName AS RestaurantName, InvoiceLineItem.Invoice_ID, 
    ScheduledRestaurantService.Location_ID, ScheduledService.Service_ID, 
    Service.DisplayName AS ServiceName, InvoiceLineItem.Description, 
    InvoiceLineItem.Amount AS 'Subtotal' INTO #Subtotal FROM 
    ScheduledService INNER JOIN Invoice ON ScheduledService.Invoice_ID = 
    Invoice.ID INNER JOIN InvoiceLineItem ON Invoice.ID = 
    InvoiceLineItem.Invoice_ID INNER JOIN Payment ON Invoice.ID = 
    Payment.RelatedInvoice_ID INNER JOIN ScheduledRestaurantService ON 
    ScheduledService.ID = ScheduledRestaurantService.ID INNER JOIN 
    RMenuAllocatedScheduleSlot ON ScheduledRestaurantService.TimeSlot_ID = 
    RMenuAllocatedScheduleSlot.ID INNER JOIN [User] ON 
    ScheduledService.RelatedUser_ID = [User].ID INNER JOIN 
    RestaurantLocation ON ScheduledRestaurantService.Location_ID = 
    RestaurantLocation.ID INNER JOIN Restaurant ON 
    RestaurantLocation.Restaurant_ID = Restaurant.ID INNER JOIN Service ON 
    ScheduledService.Service_ID = Service.ID WHERE 
    (ScheduledService.ServiceDate BETWEEN ? AND ?) AND (NOT 
    (ScheduledService.Status_ID = 6)) AND (ScheduledService.Service_ID IN 
    (17, 18, 23, 24, 25)) AND (InvoiceLineItem.Description IN ('Subtotal')) 
    AND (ScheduledRestaurantService.Location_ID = ?) SELECT 
    #Subtotal.Invoice_ID, InvoiceLineItem.Amount AS 'SalesTax' INTO 
    #SalesTax FROM #Subtotal LEFT OUTER JOIN InvoiceLineItem ON 
    #Subtotal.Invoice_ID = InvoiceLineItem.Invoice_ID WHERE 
    (InvoiceLineItem.Description IN ('Sales Tax')) SELECT 
    #Subtotal.Invoice_ID, CASE WHEN #Subtotal.Service_ID IN (17,23,25,26) 
    THEN 0.00 ELSE InvoiceLineItem.Amount END AS 'Tip' INTO #Tip FROM 
    #Subtotal LEFT OUTER JOIN InvoiceLineItem ON #Subtotal.Invoice_ID = 
    InvoiceLineItem.Invoice_ID WHERE (InvoiceLineItem.Description IN 
    ('Tip')) Select #Subtotal.ServiceDate, #Subtotal.RestaurantName, 
    #Subtotal.Invoice_ID, #Subtotal.Location_ID, #Subtotal.Service_ID, 
    #Subtotal.Subtotal, #SalesTax.SalesTax, #Tip.Tip Into #Final From 
    #Subtotal LEFT OUTER JOIN #SalesTax ON #Subtotal.Invoice_ID = 
    #SalesTax.Invoice_ID LEFT OUTER JOIN #Tip ON #Subtotal.Invoice_ID = 
    #Tip.Invoice_ID SET NOCOUNT OFF; Select * From #Final Order By 
    #Final.Service_ID, #Final.ServiceDate Drop Table #Subtotal Drop Table 
    #SalesTax Drop Table #Tip Drop Table #Final", (firstDay,lastDay,ID))
    rows = cursor.fetchall()

一切正常。第二个查询的结果基于第一个查询中创建的唯一 ID。

但是当我尝试向第二个查询添加一个新的计算字段时,我从 SQL Db 中得到一个 "Incorrect Syntax Error"。这是更新后的查询:

cursor = cnxn.cursor()
cursor.execute("SELECT RestaurantLocation.ID, Restaurant.DisplayName FROM 
    Restaurant INNER JOIN RestaurantLocation ON Restaurant.ID = 
    RestaurantLocation.Restaurant_ID WHERE (RestaurantLocation.Active = 1)")
row = cursor.fetchall()

columns = [column[0] for column in cursor.description]
for ID,DisplayName in row:
    DisplayName = DisplayName.replace(" ","")
    cursor = cnxn.cursor()
    print(firstDay,lastDay,ID)
    if ID == 13:
        percentage = 0.18
    else:
        if ID == 25:
            percentage = 0.18
        else:
            percentage = 0.20
    cursor.execute("SET NOCOUNT ON; SELECT ScheduledService.ServiceDate, 
        Restaurant.DisplayName AS RestaurantName, 
        InvoiceLineItem.Invoice_ID, ScheduledRestaurantService.Location_ID, 
        ScheduledService.Service_ID, Service.DisplayName AS ServiceName, 
        InvoiceLineItem.Description, InvoiceLineItem.Amount AS 'Subtotal' 
        INTO #Subtotal FROM ScheduledService INNER JOIN Invoice ON 
        ScheduledService.Invoice_ID = Invoice.ID INNER JOIN InvoiceLineItem 
        ON Invoice.ID = InvoiceLineItem.Invoice_ID INNER JOIN Payment ON 
        Invoice.ID = Payment.RelatedInvoice_ID INNER JOIN 
        ScheduledRestaurantService ON ScheduledService.ID =                                                 
        ScheduledRestaurantService.ID INNER JOIN RMenuAllocatedScheduleSlot 
        ON ScheduledRestaurantService.TimeSlot_ID = 
        RMenuAllocatedScheduleSlot.ID INNER JOIN [User] ON 
        ScheduledService.RelatedUser_ID = [User].ID INNER JOIN 
        RestaurantLocation ON ScheduledRestaurantService.Location_ID = 
        RestaurantLocation.ID INNER JOIN Restaurant ON 
        RestaurantLocation.Restaurant_ID = Restaurant.ID INNER JOIN Service 
        ON ScheduledService.Service_ID = Service.ID WHERE 
        (ScheduledService.ServiceDate BETWEEN ? AND ?) AND (NOT 
        (ScheduledService.Status_ID = 6)) AND (ScheduledService.Service_ID 
        IN (17, 18, 23, 24, 25)) AND (InvoiceLineItem.Description IN 
        ('Subtotal')) AND (ScheduledRestaurantService.Location_ID = ?) 
        SELECT #Subtotal.Invoice_ID, InvoiceLineItem.Amount AS 'SalesTax' 
        INTO #SalesTax FROM #Subtotal LEFT OUTER JOIN InvoiceLineItem ON 
        #Subtotal.Invoice_ID = InvoiceLineItem.Invoice_ID WHERE 
        (InvoiceLineItem.Description IN ('Sales Tax')) SELECT 
        #Subtotal.Invoice_ID, CASE WHEN #Subtotal.Service_ID IN 
        (17,23,25,26) THEN 0.00 ELSE InvoiceLineItem.Amount END AS 'Tip' 
        INTO #Tip FROM #Subtotal LEFT OUTER JOIN InvoiceLineItem ON 
        #Subtotal.Invoice_ID = InvoiceLineItem.Invoice_ID WHERE 
        (InvoiceLineItem.Description IN ('Tip')) Select 
        #Subtotal.ServiceDate, #Subtotal.RestaurantName, 
        #Subtotal.Invoice_ID, #Subtotal.Location_ID, #Subtotal.Service_ID, 
        #Subtotal.Subtotal, #SalesTax.SalesTax, #Subtotal.Subtotal * .? AS 
        Discount, #Tip.Tip Into #Final From #Subtotal LEFT OUTER JOIN 
        #SalesTax ON #Subtotal.Invoice_ID = #SalesTax.Invoice_ID LEFT OUTER 
        JOIN #Tip ON #Subtotal.Invoice_ID = #Tip.Invoice_ID SET NOCOUNT OFF; 
        Select * From #Final Order By #Final.Service_ID, #Final.ServiceDate 
        Drop Table #Subtotal Drop Table #SalesTax Drop Table #Tip Drop Table 
        #Final", (firstDay,lastDay,ID,percentage))
    rows = cursor.fetchall()

错误是由我分配给 "percentage" 的值引发的。我首先尝试使用 int() 来解决这个问题,所以我的 IF 语句变成了:

if ID == 13:
    percentage = int(0.18)
else:
    if ID == 25:
        percentage = int(0.18)
    else:
        percentage = int(0.20)

我的错误是无法识别 int() 是不使用小数位进行转换的。所以我更新了 IF 语句以使用 float():

if ID == 13:
        percentage = float(0.18)
    else:
        if ID == 25:
            percentage = float(0.18)
        else:
            percentage = float(0.20)

在使用 float() 设置 "percentage" 后,我从 SQL 得到了完全相同的错误。我尝试了上述 IF 语句的多种变体,但它们都导致了完全相同的错误。

我做错了什么?

错误是第四个参数占位符前的句点(小数点)引起的:

... #Subtotal.Subtotal * .? AS Discount

这导致 pyodbc 使用

这样的查询调用 sp_prepexec
SELECT ... #Subtotal.Subtotal * .@P1 AS Discount FROM #Subtotal

这是无效语法。

参数占位符应始终独立出现,切勿依赖定界符或命令文本的其他元素来修改其行为。在这种情况下,

sql = "SELECT id, Subtotal, #Subtotal.Subtotal * ? AS Discount FROM #Subtotal"
params = (Decimal('0.05'),)
crsr.execute(sql, params)

发送

exec sp_prepexec @p1 output,N'@P1 numeric(3,3)',N'SELECT id, Subtotal, #Subtotal.Subtotal * @P1 AS Discount FROM #Subtotal',50

并且执行无误。