正确的查询不再适用于SPROC

时间:2016-10-13 08:16:47

标签: sql sql-server

在测试使用存储过程的应用程序时,我遇到了一个实际上已经修复过的错误。调试存储过程时,我发现中断的查询如下:

SET @LastSold = (SELECT last_sold_date
                   FROM   movement.dbo.dv_store_items
                   WHERE  Cast(store_number AS INT) = @Store
                          AND vendor_number = @Vendor
                          AND upc = @UPC
                          AND store_number <> 'CMPNY');

我收到的错误是:

Conversion failed when converting the varchar value 'CMPNY' to data type int

这个查询的奇怪之处在于,当我使用相同的标准在存储过程之外运行相同的查询时,它完全正常。更奇怪的是,这个存储过程工作得很好,直到我在表move.bo.dv_store_items上创建索引:

CREATE NONCLUSTERED INDEX [NCIX1] ON [dbo].[dv_store_items]
(
[upc] ASC,
[vendor_number] ASC,
[store_number] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, 
DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
ON [PRIMARY]
GO

老老实实地,我不知道这有什么意义,所以任何帮助都会非常非常感激

3 个答案:

答案 0 :(得分:1)

确保您不会遇到转换问题(alpha到整数,例如&#39; CMPY&#39;)的唯一万无一失的方法是使用TRY_CAST。如果无法进行演员表演(即,从&#39; CMPY&#39;到INT),这将返回NULL。可在SQL Server 2012 +中使用。

SET @LastSold = (
       SELECT 
           last_sold_date
       FROM
           movement.dbo.dv_store_items
       WHERE
           TRY_CAST(store_number AS INT)=@Store -- ensure you only select from actual integers
           AND vendor_number = @Vendor
           AND upc = @UPC
);

答案 1 :(得分:0)

问题在于我列出的索引:

CREATE NONCLUSTERED INDEX [NCIX1] ON [dbo].[dv_store_items]
(
[upc] ASC,
[vendor_number] ASC,
[store_number] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, 
DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
ON [PRIMARY]
GO 

应该是:

CREATE NONCLUSTERED INDEX [NCIX1] ON [dbo].[dv_store_items]
(
    [store_number] ASC
)
INCLUDE (   [upc],
    [last_sold_date],
    [vendor_number]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
    SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
ON [PRIMARY]
GO

正如Allan S. Hansen所说,原因是“WHERE中的操作顺序在两个版本中发生了变化”。

此外,TT。在评论中提出了一个很好的观点。通过完全过滤掉'CMPNY'值可以进一步改善这个查询,使我的WHERE子句中的那部分无关紧要:

CREATE NONCLUSTERED INDEX [NCIX1] ON [dbo].[dv_store_items]
(
    [store_number] ASC
)
INCLUDE (   [upc],
    [last_sold_date],
    [vendor_number])
WHERE store_number <> 'CMPNY'
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
ON [PRIMARY]
GO

答案 2 :(得分:0)

您的代码仍然容易出错,因为查询优化器的未来行为是不可预测的。 转换前检查数据。快速演示

declare @store_number varchar(100) = 'CMPNY';
declare @store int = 1;

SELECT 1 WHERE CASE @store_number WHEN 'CMPNY' THEN -@store ELSE cast( @store_number AS INT) END = @store;
-- no result, no error

set @store_number = cast(@store  as varchar(100));

SELECT 1 WHERE CASE @store_number WHEN 'CMPNY' THEN -@store ELSE cast( @store_number AS INT) END = @store;
-- 1 row