以另一种方式提高动态 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 查询性能或过滤记录的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server-聚焦过滤索引提高查询性能

为啥我能够有条件地以一种方式调用钩子,而不能以另一种方式调用?

sql查询性能调试,用SET STATISTICS IO和SET STATISTICS TIME---解释比较详细

如何提高子查询的性能或 sql 中子查询的替代方案

以另一种方式在 VIM 窗口之间导航

在数组中的一个值之后,以另一种方式开始计算