我有一个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注入和临时表的删除都会产生性能开销。
您是否了解处理这种情况的更好解决方案?
答案 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的限制并且你创建了一个临时表,我不希望创建表是一个主要问题,只要你一次插入值(当然不是数千个查询) 。选择开销最小的方法,如下所述:
当然,如果你的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 :).