【问题标题】:SQL - Optimizing complex paged search querysSQL - 优化复杂的分页搜索查询
【发布时间】:2011-03-04 01:14:02
【问题描述】:

我目前正在为大型数据库中的复杂搜索开发一个存储过程。因为有数千个条目,所以可以返回我想使用分页。虽然它正在工作,但我认为它太慢了。我阅读了很多关于 SQL 查询分页和优化性能的文章和文章。但大多数“优化”只对非常基本的请求有用,例如“从表 x 中提供 20-30 项”。

由于我们的世界并不是那么简单,而且还有更复杂的查询要进行,我想获得一些帮助以优化以下查询:

CREATE PROCEDURE [SearchItems]
@SAttr1 BIT = 0,
@SAttr2 BIT = 0,
@SAttr3 BIT = 0,
@Flag1 BIT = 0,
@Flag2 BIT = 0,
@Param1 VARCHAR(20),
@Param2 VARCHAR(10),
@SkipCount BIGINT,
@TakeCount BIGINT,
@SearchStrings NVARCHAR(1000)    
AS
    DECLARE @SearchStringsT TABLE(
        Val NVARCHAR(30)
    )

    INSERT INTO @SearchStringsT 
    SELECT * FROM dbo.Split(@SearchStrings,',');

WITH ResultTable AS (
    SELECT  Table1.*, ROW_NUMBER() OVER(ORDER BY Table1.ID ASC) AS [!ROWNUM!]
    FROM Table1
    INNER JOIN Table2 ON Table1.ID = Table2.FK1
    INNER JOIN Table3 ON Table2.ID = Table3.FK2
    INNER JOIN Table4 ON Table3.XX = Table4.FKX
    WHERE Table1.X1 = @Parameter1
    AND
        (@Flag1 = 0 OR Table1.X2 = 1) AND
        (@Flag2 = 0 OR Table2.X4 = @Parameter2) AND
        (@Flag3 = 0 OR EXISTS(SELECT * FROM Table5 WHERE Table5.ID = Table3.X1)) 
    AND
    (                   
        (@SAttr1 = 0 OR EXISTS(SELECT * FROM @SearchStringsT WHERE Table1.X1 LIKE Val)) OR
        (@SAttr2 = 0 OR EXISTS(SELECT * FROM @SearchStringsT WHERE Table2.X1 LIKE Val)) OR
        (@SAttr3 = 0 OR EXISTS(SELECT * FROM @SearchStringsT WHERE Table3.X1 LIKE Val)) OR
        (@SAttr4 = 0 OR EXISTS(SELECT * FROM @SearchStringsT WHERE Table4.X1 LIKE Val))
    )
)
SELECT TOP(@TakeCount) * FROM ResultTable
WHERE [!ROWNUM!] BETWEEN (@SkipCount + 1) AND (@SkipCount + @TakeCount)
RETURN

@SAttr 参数是指定是否搜索字段的位参数,@Flag 参数用于打开/关闭某些布尔表达式的检查,@SkipCount 和@TakeCount 用于分页。 @SearchString 是一个逗号分隔的搜索关键字列表,已经包含通配符。

我希望有人可以帮助我优化这一点,因为在主表中有 20.000 个条目的数据库中的单次搜索持续 800 毫秒,并且随着条目数的增加而增加。最终的应用程序需要处理超过 100.000 个条目。

非常感谢您的每一次帮助。 标记

【问题讨论】:

    标签: sql database optimization stored-procedures paging


    【解决方案1】:

    虽然我同意 Tom H. 的观点,这可能是动态 SQL 最好的情况(而且我是一个存储 proc 类型的女孩,所以我不经常这么说),但可能你不这样做' t 在您的表上有良好的索引。所有可能的搜索字段都编入索引了吗?是否所有 FK 都已编入索引?

    我的意思是 20,000 是一个很小的表,而 100,000 也是如此,所以看起来你可能还没有建立索引。

    检查您的执行计划以查看是否正在使用索引。

    【讨论】:

    • 你是对的。我现在几乎没有索引,但那是因为我将有比读取操作更多的插入操作。而且由于索引会减慢插入速度,我认为我不会从使用索引中获得整体性能优势。但我必须对此进行一些分析。
    • 现在我已将所有主键索引为聚集(用于连接)和所有 FK、“order by”或“where”列作为非聚集索引。查询速度没有显着变化。还有其他建议吗?
    【解决方案2】:

    存储过程不太擅长超泛型,因为它会阻止 SQL Server 始终使用最佳方法。最近在类似的情况下,我使用了 (gasp) 动态 SQL。我的搜索存储过程将构建 SQL 代码来执行搜索,就像你拥有它一样使用分页(使用 ROW_NUMBER() 等)。优点是如果参数表明一条信息没有在搜索中使用,那么生成的代码将忽略它。最后,它允许更好的查询计划。

    确保正确使用 sp_executesql 以防止 SQL 注入攻击。

    【讨论】:

    • 并确保首先阅读此内容sommarskog.se/dynamic_sql.html 同意您的观点,在动态 SQl 中可能会更好地完成此特定任务。
    • @HLGEM - 谢谢,我开始寻找将其发布到此处的链接,然后被一个闪亮的物体分散了注意力:)
    • 对我来说,使用存储过程的主要原因是用户权限管理和安全优势。而且你不认为一些'@Flag = 0 OR'比每次执行搜索时向数据库发送如此大的查询更快吗?
    • 向数据库发送查询几乎不需要任何资源。这是网络上的几个字节。当然,无论如何,我不是这样做的。我完全按照您的方式调用了存储过程,但是在存储过程中我构建了要运行的确切 SQL。使用 @Flag = 0 OR 逻辑不起作用,因为 SQL Server 无法提前考虑该条件 - 它仍然会创建一个查询计划,就好像可能必须检查第二个条件一样,这通常会导致表扫描.
    • @Tom H. - 感谢您提供有关查询计划的信息。是否有可能打开/关闭字段的检查?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-18
    • 2012-01-07
    相关资源
    最近更新 更多