如何根据选定的值集从数据库表中有效地选择行

时间:2010-04-24 05:03:06

标签: sql performance implementation

我有一个100万行的交易表。该表具有字段名称“代码”以保留客户的ID。大约有10,000种不同的客户代码。

我有一个GUI界面允许用户从事务表中呈现报告。用户可以选择任意数量的客户进行渲染。

我首先使用IN运算符,它适用于少数客户:

SELECT * FROM TRANS_TABLE WHERE CODE IN ('...', '...', '...')

如果我选择几千个客户,我很快就会遇到问题。使用IN运算符存在限制。

另一种方法是创建一个只包含一个CODE字段的临时表,并使用INSERT语句将选定的客户代码注入临时表。然后我可以使用

SELECT A.* FROM TRANS_TABLE A INNER JOIN TEMP B ON (A.CODE=B.CODE)

这适用于大量选择。但是,临时表创建,INSERT注入和临时表的删除都会产生性能开销。

您是否了解处理这种情况的更好解决方案?

4 个答案:

答案 0 :(得分:2)

如果您使用SQL Server 2008,最快的方法通常是使用Table-Valued Parameter(TVP):

CREATE TYPE CodeTable AS TABLE
(
    Code int NOT NULL PRIMARY KEY
)

DECLARE @Codes AS CodeTable
INSERT @Codes (Code) VALUES (1)
INSERT @Codes (Code) VALUES (2)
INSERT @Codes (Code) VALUES (3)
-- Snip codes

SELECT t.*
FROM @Codes c
INNER JOIN Trans_Table t
    ON t.Code = c.Code

使用ADO.NET,您可以填充TVP directly from your code,因此您不需要生成所有这些INSERT语句 - 只需传入DataTable并且ADO.NET将处理剩下的事情。所以你可以这样编写一个存储过程:

CREATE PROCEDURE GetTransactions
    @Codes CodeTable READONLY
AS

SELECT t.*
FROM @Codes c
INNER JOIN Trans_Table t
    ON t.Code = c.Code

...只需将@Codes值作为参数传递。

答案 1 :(得分:1)

您可以生成SQL,例如

SELECT * FROM TRANS_TABLE WHERE CODE IN (?,?,?,?,?,?,?,?,?,?,?)

并在循环中重复使用它,直到您加载了所需的所有ID。优点是,如果您只需要几个ID,则数据库不需要解析所有这些子句。如果许多ID是罕见的情况,那么性能损失可能无关紧要。如果您不担心SQL解析缓存,那么您可以将in子句的大小限制为DB的实际限制,这样有时您不需要循环,有时则不需要循环。

答案 2 :(得分:0)

因为你必须以某种方式传递ID,IN应该是最快的方式。

MSDN提到:

  

在IN子句中包含极大数量的值(数千个)会占用资源并返回错误8623或8632.要解决此问题,请将项目存储在表中的IN列表中。

如果仍然可以使用IN并且查询速度很慢,则可以尝试调整索引,例如为查询使用一些覆盖索引。由聚簇索引查找随机值可能很慢,因为需要随机磁盘I / O.覆盖指数可以减少这个问题。

如果你真的通过了IN的限制并且你创建了一个临时表,我不希望创建表是一个主要问题,只要你一次插入值(当然不是数千个查询) 。选择开销最小的方法,如下所述:

http://blog.sqlauthority.com/2008/07/02/sql-server-2008-insert-multiple-records-using-one-insert-statement-use-of-row-constructor/

当然,如果你的ID中有一些静态模式,你可以选择它(比如在SP或UDF中)。如果您从数据库中获取了数千个ID,而不是来回传递它们,您可以只存储它们或使用子查询......

答案 3 :(得分:0)

也许您可以将客户代码传递给存储过程逗号分隔,并使用此处提到的拆分sql函数:http://www.devx.com/tips/Tip/20009

然后声明一个标量表,在其中插入分割值并使用IN子句。

CREATE PROCEDURE prc_dosomething (
    @CustomerCodes varchar(MAX)
)
AS

DECLARE @customercodetable table(code varchar(10)) -- or whatever length you require.
SET @customercodetable = UTILfn_Split(@CustomerCodes) -- see the article above for the split function.

-- do some magic stuff here :).