使用条件过滤提高查询性能

Posted

技术标签:

【中文标题】使用条件过滤提高查询性能【英文标题】:Improve performance of query with conditional filtering 【发布时间】:2018-06-17 21:45:01 【问题描述】:

假设我有一个包含 300 万行的表,该表没有 PK 也没有索引。

查询如下

SELECT SKU, Store, ColumnA, ColumnB, ColumnC
FROM myTable
WHERE (SKU IN (select * from splitString(@skus)) OR @skus IS NULL)
AND (Store IN (select * from splitString(@stores)) OR @stores IS NULL)

请考虑@sku@storeNVARCHAR(MAX),其中包含以逗号分隔的ID 列表。 SplitString 是一个函数,它将格式为 '1,2,3' 的字符串转换为 1 列 3 行的表格,如下图所示。

此模式允许我从应用程序发送参数并按 sku 或按商店或两者或无过滤。

我可以做些什么来提高这个查询的性能? - 我知道索引是一个好主意,但我真的不知道那些东西,所以对此的指导会有所帮助。 还有其他想法吗?

【问题讨论】:

尝试使用存在...此代码 WHERE (SKU IN (select * won't event work @hatchet 你能详细说明你的答案吗,我不太明白你在说什么以及为什么重要 您不能指望对具有数百万条记录的未索引表运行查询并获得可观的结果...索引您的表。 使用执行计划来优化您的查询性能和尽可能避免使用 OR 运算符,阅读更多相关信息ubitsoft.com/help_19/html/… 另外,您应该考虑表而不是创建表的函数。如果可能,请反向执行工作,这意味着您可以创建一个永久表并使用存储过程控制它,并修改您的应用程序以将所有数据插入该表中,因此应用程序只会在数据库和任何数据之间同步另一端。然后,您可以索引您的表并提高它们的性能。 【参考方案1】:

这种类型的通用搜索查询的性能往往很粗糙。

除了使用临时表来存储字符串解析结果的建议之外,您还可以做一些其他的事情:

添加索引

通常建议每个表都有一个聚集索引(尽管似乎仍有争论的空间):Will adding a clustered index to an existing table improve performance?

除此之外,您可能还想在要搜索的字段上添加索引。

在这种情况下,可能是这样的:

    SKU(仅针对 SKU 进行搜索) 商店、SKU(用于商店搜索以及商店和 SKU 的组合)

请记住,如果查询匹配太多记录,则可能不会使用这些索引。 还要记住,使索引覆盖查询可以提高性能: Why use the INCLUDE clause when creating an index?

这里是微软关于创建索引的文档的链接: https://docs.microsoft.com/en-us/sql/t-sql/statements/create-index-transact-sql

使用动态 SQL 构建查询

我需要在前面加上一个警告。请注意 SQL 注入,并确保正确编码! How to cleanse dynamic SQL in SQL Server -- prevent SQL injection

构建动态 SQL 查询可以让您编写更精简、更直接的 SQL,从而让优化器做得更好。这通常是要避免的,但我相信它适合这种特殊情况。

这是一个示例(应根据需要进行调整以考虑 SQL 注入):

DECLARE @sql VARCHAR(MAX) = '
    SELECT SKU, Store, ColumnA
    FROM myTable
    WHERE 1 = 1
';

IF @skus IS NOT NULL BEGIN
    SET @sql += ' AND SKU IN (' + @skus + ')';
END

IF @stores IS NOT NULL BEGIN
    SET @sql += ' AND Store IN (' + @stores + ')';
END

EXEC sp_executesql @sql;

【讨论】:

我按照建议添加了一些索引,但没有看到任何性能提升。我还尝试了动态 sql,这很成功!我认为参数(或参数)嗅探会阻止引擎生成最佳执行计划。非常感谢! @GabrielEspinoza 随着您的数据库变得越来越大,您越有可能从索引中看到好处。【参考方案2】:

要避免的另一件事是在 Where 子句中使用函数。这会减慢查询速度。

尝试将其放在脚本的开头,在第一个 SELECT 之前:

    SELECT skus_group INTO #skus_group 
    FROM  (SELECT item AS skus_group FROM     
    splitstring(@skus, ','))A;

然后替换你的 WHERE 子句:

    WHERE SKU IN(Select skus_group FROM #skus_group)

这通常会提高性能,因为它利用索引而不是表扫描,但由于您没有使用任何索引,我不确定您会获得多少性能提升。

【讨论】:

我确实尝试过这个,但我没有看到任何明显的改进,但是,我真的很喜欢一旦你把所有的东西都分门别类,查询会变得多么整洁 您是否在表中添加了任何主键?如果您有 300 万行并且您的表具有主键、索引等,那么您肯定会开始看到一些性能改进。【参考方案3】:

我相信这会更快:

SELECT SKU, Store, ColumnA, ColumnB, ColumnC FROM myTable WHERE @skus IS NULL AND @stores IS NULL
UNION ALL 
SELECT SKU, Store, ColumnA, ColumnB, ColumnC 
FROM myTable 
INNER JOIN (select colname AS myskus from splitString(@skus))skuses ON skuses.myskus = myTable.SKU
INNER JOIN (select colname AS mystore from splitString(@stores))stores ON stores.mystore = myTable.Store

【讨论】:

这可能是个好主意,我现在把这个发给负责这个产品的团队

以上是关于使用条件过滤提高查询性能的主要内容,如果未能解决你的问题,请参考以下文章

应该针对不同的排序和过滤条件创建哪些MongoDB索引来提高性能?

利用 SQL Server 过滤索引引提高查询语句的性能

对通用查询组件初始化组织过滤条件

带有附加左表过滤器的左连接性能

当我使用“或”条件时,为啥我的查询使用过滤而不是索引条件?

Postgresql 查询的过滤条件中的列上的字符串操作如何影响它选择的计划