Python 与 sql server 和cursor.execute with parms

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

给出下面的test3表

/****** Object:  Table [dbo].[test3]    Script Date: 11/12/2023 9:30:17 AM ******/
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test3]') AND type in (N'U'))
DROP TABLE [dbo].[test3]
GO
/****** Object:  Table [dbo].[test3]    Script Date: 11/12/2023 9:30:17 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[test3](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [column1] [varchar](10) NOT NULL
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[test3] ON 
GO
INSERT [dbo].[test3] ([id], [column1]) VALUES (1, N'aaa')
GO
INSERT [dbo].[test3] ([id], [column1]) VALUES (2, N'bbb')
GO
SET IDENTITY_INSERT [dbo].[test3] OFF
GO

问题:

sqlstatement1 返回表的所有两行。 sqlstatement2 返回表的零行。

import pyodbc

connectionString = 'DRIVER={ODBC Driver 17 for SQL Server};SERVER=7D3QJR3;DATABASE=mint2;Trusted_Connection=yes'
currentConnection = pyodbc.connect(connectionString) 

sqlStatement1 = '''
SELECT 
    id,
    column1
FROM test3
WHERE
    ISNULL(?, id) = id
ORDER BY 
    ID
    '''

sqlStatement2 = '''
SELECT 
    id,
    column1
FROM test3
WHERE
    ISNULL(?, column1) = column1
ORDER BY 
    ID
    '''

#Process sqlStatement1
sqlArgs = []
sqlArgs.append(None)
cursor = currentConnection.cursor()
cursor.execute(sqlStatement1,sqlArgs)
rows = cursor.fetchall()
print('ROWS WITH ID=NULL:' + str(len(rows)))
cursor.close()

#Process sqlStatement2
sqlArgs = []
sqlArgs.append(None)
cursor = currentConnection.cursor()
cursor.execute(sqlStatement2,sqlArgs)
rows = cursor.fetchall()
print('ROWS WITH COLUMN1=NULL:' + str(len(rows)))
cursor.close()

那么为什么它适用于 int 数据类型而不适用于 string 数据类型?

我的直觉是,当语句比较时,sp_prepexec 语句由于某种原因将位置参数 P1 创建为 varchar(1) ?到 varchar 列,并在语句 comapres ? 时将 P1 设置为 int 。到 int 列:

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@P1 int',N'
SELECT 
    id,
    column1
FROM test3
WHERE
    ISNULL(@P1, id) = id
ORDER BY 
    ID
    ',NULL
select @p1

declare @p1 int
set @p1=2
exec sp_prepexec @p1 output,N'@P1 varchar(1)',N'
SELECT 
    id,
    column1
FROM test3
WHERE
    ISNULL(@P1, column1) = column1
ORDER BY 
    ID
    ',NULL
select @p1
python sql-server odbc pyodbc
1个回答
0
投票

我的直觉是,当语句比较时,sp_prepexec 语句由于某种原因将位置参数 P1 创建为 varchar(1) ?到 varchar 列,并在语句 comapres ? 时将 P1 设置为 int 。到 int 列。

是的,这正是它的作用。它不知道参数的大小,因为你没有告诉它。这已在 GitHub 上注明。

因为您在

ISNULL
的左侧使用它,所以右侧会投射到左侧,因此查询不会给出正确的结果。

您有多种解决方案:

  • 可以使用

    setinputsizes
    设置字符串变量的类型和大小

    cursor.setinputsizes([(pyodbc.SQL_VARCHAR, 10, 0)])
    

    但这会为所有字符串变量设置它。

  • 进行显式转换。这是一个更好的选择,因为它永远不会失败。

    WHERE
        ISNULL(CAST(? AS varchar(10)), column1) = column1
    

    您也可以通过先将其设置为正确大小的变量来完成此操作。

  • 重写您的查询以不使用

    ISNULL
    ,无论如何您都应该这样做,因为它会阻止使用索引。

    • 使用
      OR
      sqlStatement2 = '''
      SELECT 
          id,
            column1
      FROM test3
      WHERE
          (? = column1 OR ? IS NULL)
      ORDER BY 
          ID;
      '''
      
      请注意,您需要传递参数两次,或者将其分配给 SQL 中的变量。
    • 或者 我认为最好的选择, 使查询动态化,因此您可以预先决定是否按该列进行过滤。
      sqlStatement2 = '''
      SELECT 
          id,
            column1
      FROM test3
      '''
      
      if someValue is not None:
          sqlStatement2 = sqlStatement2 + '''WHERE
            ? = column1
      '''
      
      sqlStatement2 = sqlStatement2 + '''ORDER BY 
          ID;
      '''
      
© www.soinside.com 2019 - 2024. All rights reserved.