1

我目前需要搜索项目表以验证用户输入的项目列表。
项目表包含每个项目的唯一主键,称为 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,但我对效率方面的“最佳实践”没有感觉。通常,我会检查执行计划,但我真的不知道从哪里开始。我将不胜感激任何和所有的建议!

4

2 回答 2

0

就个人而言,我会在我的存储过程中使用一个表值参数,然后简单地 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)
于 2012-06-26T18:15:26.313 回答
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底部的示例),另请参见左外连接与不存在

于 2012-06-26T18:48:16.200 回答