Given the following test3 table
/****** 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
Problem:
The sqlstatement1 returns all two rows of the table. The sqlstatement2 returns zero rows of the table.
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()
So why does it work with an int data type but not a string data type?
My gut is that the sp_prepexec statement is creating the positional parameter P1 as varchar(1) for some reason when the statement compares ? to a varchar column and sets P1 to and int when the statement comapres ? to a int column:
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
vs
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
1条答案
按热度按时间m4pnthwp1#
My gut is that the sp_prepexec statement is creating the positional parameter P1 as varchar(1) for some reason when the statement compares ? to a varchar column and sets P1 to and int when the statement comapres ? to a int column.
Yes that is exactly what it does. It has no knowledge of how large to make the parameter, because you haven't told it. This has been noted on GitHub .
Because you are using it on the left side of an
ISNULL
, the right side is casted to the left side, hence the query is not giving the correct results.You have a number of solutions:
setinputsizes
to set the type and size of a string parameter:You can also do this by setting it to a variable of the correct size first.
Rewrite your query to not use
ISNULL
, which you should do anyway, because it prevents the use of indexes.Either use an
OR
Note that you need to pass the parameter twice, or assign it to a variable within the SQL.
Note that in these last two options, it's probably still ideal to set the variable size.