我目前需要搜索项目表以验证用户输入的项目列表
项目表包含每个项目的唯一主键,称为ItemId(对应于用户输入的值)。
给定一个10000(一万)行的表,搜索ItemId列并确定用户输入的任何项目在表中是否存在的最有效方法是什么?
例如,给出表格:
ItemId Color Price
1000 Blue 3.00
1001 Red 4.00
1003 Green 1.25
用户输入以下内容:
1000
1001
1002
我想抛出一个错误来警告用户其中一个项目(1002)无效。我不需要专门识别无效的项目,只需要表中不存在一个或多个项目。我尝试过使用IF NOT EXISTS以及EXCEPT,但我没有在效率方面感受到“最佳实践”。通常情况下,我会检查执行计划,但我真的不知道从哪里开始。我非常感谢任何建议!
答案 0 :(得分:0)
就个人而言,我会在我的存储过程中使用Table-Valued参数,然后只需LEFT JOIN到表中并查找NULL。
CREATE TYPE ItemTableType AS TABLE
( ItemId INT);
GO
CREATE PROCEDURE dbo.usp_GetItems
@Items ItemTableType READONLY
AS
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET NOCOUNT ON;
IF EXISTS (SELECT *
FROM dbo.MyItemTable i1
LEFT JOIN @Items i2 ON i1.ItemId = @i2.ItemId
WHERE i2.ItemId IS NULL)
RAISERROR('Item does not exist', 16, 1)
ELSE
SELECT i1.*
FROM dbo.MyItemTable i1
JOIN @Items i2 on i1.ItemId = @i2.ItemId
GO
我能想到的另一种可能性是对最终结果进行计数。
执行计数:
DECLARE @CommaCount INT
SET @CommaCount = (select len(@ItemIds)-len(replace(@ItemIds,',','')))
然后当您执行SELECT时,只需将结果与计数进行比较:
SELECT *
FROM dbo.MyItemTable
WHERE ItemId IN (@ItemIds)
IF(@@ROWCOUNT != @CommaCount)
RAISERROR('Item does not exist', 16, 1)
答案 1 :(得分:0)
我同意Chris的回答,因为使用TVP是传递用户提供的值的好方法,但会做出以下更改。
使用主键声明TVP
CREATE TYPE ItemTableType AS TABLE
( ItemId INT PRIMARY KEY);
使用NOT EXISTS
代替OUTER JOIN ... NULL
。这是作为有效的反半连接实现的。 OPTION (RECOMPILE)
提示和PRIMARY KEY
应该为优化器提供足够的信息,以便为两个表的大小选择最佳的连接策略。
SELECT CASE
WHEN EXISTS (SELECT *
FROM @Items i
WHERE NOT EXISTS (SELECT *
FROM dbo.MyItemTable it
WHERE i1.ItemId = it.ItemId)) THEN 0
ELSE 1
END
OPTION (RECOMPILE)
OUTER JOIN ... NULL
可以结束所有行的外部联接,然后使用过滤器删除NOT NULL
,但效率较低(this article底部的示例),另见{{ 3}}