以另一种方式提高动态 SQL 查询性能或过滤记录
Posted
技术标签:
【中文标题】以另一种方式提高动态 SQL 查询性能或过滤记录【英文标题】:Improve dynamic SQL query performance or filter records another way 【发布时间】:2013-02-19 15:21:26 【问题描述】:预赛:
我们的应用程序可以从附加的客户端 SQL Server 2005 或 2008 数据库中读取数据,但除了使用临时表之外,不会对其进行任何更改。我们可以在自己的服务器上的数据库中创建表。
该解决方案必须在 SQL Server 2005 中运行。
架构:
这是模式的简化概念。
Group - 定义一组位置的特征
位置 - 定义一个地理位置的特征。它链接到 Group 表。
GroupCondition - 链接到一个组。它定义了适用于属于该组的位置子集的度量。
GroupConditionCriteria - 链接到 GroupCondition 表。它为 where 子句中的单个短语命名属性、值、关系运算符和布尔运算符。命名属性是 Location 表的所有字段。有一个序号。 GroupConditionCriteria 中的多行必须以正确的顺序串在一起以形成完整的过滤条件。此筛选条件隐式限制为属于与 GroupCondition 关联的组的那些位置。满足过滤条件的位置记录为“包含”,不满足的为“排除”。
目标:
我们现有的许多查询都从位置表中获取属性。我们想加入一些东西(表、临时表、查询、CTE、openquery、UDF 等),这将为我们提供那些“包含”的位置的 GroupCondition 信息。 (一个位置可以包含在多个规则中,但这是一个单独的问题。)
我想要的架构是:
CREATE TABLE #LocationConditions
(
[PolicyID] int NOT NULL,
[LocID] int NOT NULL,
[CONDITIONID] int NOT NULL,
[Satisfies Condition] bit NOT NULL,
[Included] smallint NOT NULL
)
PolicyID 标识组,LocID 标识位置,CONDITIONID 标识GroupCondition,如果过滤器包含位置记录,[满足条件]为1。 (包括从不同的规则表派生而来,强制覆盖过滤条件。对于本次讨论并不重要。)
问题规模:
到目前为止,我尽最大努力可以创建这样一个表,但速度很慢。对于我正在测试的当前数据库,有 50,000 个位置受到潜在匹配规则 (GroupConditions) 的影响(包括或排除)。执行时间为 4 分钟。如果我们定期刷新并使用永久表,这可能是可行的,但我希望更快。
我尝试了什么:
我使用了一系列 CTE,其中一个是递归的,将过滤条件的几个部分连接成一个大的过滤条件。作为这种情况的一个例子:
(STATECODE = 'TX' AND COUNTY = 'Harris County') OR STATECODE = 'FL'
过滤条件中可以提到一到五个字段,并且可以使用任意数量的括号对它们进行分组。支持的运算符有 lt、le、gt、ge、=、、AND 和 OR。
一旦我有了条件,它仍然是一个文本字符串,所以我创建了一个插入语句(必须动态执行):
insert into LocationConditions
SELECT
1896,
390063,
38,
case when (STATECODE = 'TX' AND COUNTY = 'Harris County') OR STATECODE = 'FL' then 1
else 0
end,
1
FROM Location loc
WHERE loc.LocID = 390063
我首先将插入语句添加到它们自己的名为#InsertStatements 的临时表中,然后使用游标循环它们。我使用 EXEC 执行每个插入。
CREATE TABLE #InsertStatements
(
[Insert Statement] nvarchar(4000) NOT NULL
)
-- Skipping over Lots of complicated CTE's to add to #InsertStatements
DECLARE @InsertCmd nvarchar(4000)
DECLARE InsertCursor CURSOR FAST_FORWARD
FOR
SELECT [Insert Statement]
FROM #InsertStatements
OPEN InsertCursor
FETCH NEXT FROM InsertCursor
INTO @InsertCmd
WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @InsertCmd
EXEC(@InsertCmd)
FETCH NEXT FROM InsertCursor
INTO @InsertCmd
END
CLOSE InsertCursor
DEALLOCATE InsertCursor
SELECT *
FROM #LocationConditions
ORDER BY PolicyID, LocID
您可以想象,执行 50,000 次动态 SQL 插入很慢。如何加快速度?
【问题讨论】:
Location 表中有多少可过滤列? 【参考方案1】:您必须单独插入每一行吗?你不能用
insert into LocationConditions
SELECT
PolicyID,
LocID,
CONDITIONID,
case when (STATECODE = 'TX' AND COUNTY = 'Harris County') OR STATECODE = 'FL' then 1
else 0
end,
Included
FROM Location loc
?你没有展示你是如何创建插入语句的,所以我无法判断它是否依赖于每一行。
【讨论】:
不,我不必在单独的语句中插入每一行。好点子。我不想展示 CTE,因为它们非常复杂并且使用专有信息。我可以找到一种方法来为每个组创建一个插入,而不是每个位置一个插入。我会尝试看看它的表现如何。 快 100 倍!现在我的临时表填充需要 2 秒。以上是关于以另一种方式提高动态 SQL 查询性能或过滤记录的主要内容,如果未能解决你的问题,请参考以下文章
为啥我能够有条件地以一种方式调用钩子,而不能以另一种方式调用?