使用横向连接搜索非常慢

Posted

技术标签:

【中文标题】使用横向连接搜索非常慢【英文标题】:Searching with a lateral join extremely slow 【发布时间】:2020-07-01 14:21:22 【问题描述】:

我目前正在我们的一个网格中实现搜索功能,这样做时,我的查询从 1 秒或更短的时间运行到大约 16 秒的运行时间(我添加了建议的索引等)。此查询返回的数据将是父母的 id、父母的姓名、最近的孩子的名字和最近的孩子的姓。以下简化查询运行速度极慢(超过 15 秒),但运行正常:

SELECT 
   p.id
  ,p.name
  ,c.firstname
  ,c.lastname
FROM Parents p
CROSS APPLY (
  SELECT TOP (1) 
     c.firstname
    ,c.lastname
  FROM Children c
  WHERE c.ParentId = p.Id
  ORDER BY c.datecreated DESC
  ) i
INNER JOIN Users s
  ON p.UserId = u.Id
WHERE (
    @search IS NOT NULL
    AND @search != ''
    AND (
      @search = c.firstname
      OR @search = c.lastname
      OR p.name = @search
      )
    OR @search IS NULL
    OR @search = ''
    )

以下查询(注意不包括名字和姓氏搜索)运行速度很快,但缺少所需的功能:

SELECT 
   p.id
  ,p.name
  ,c.firstname
  ,c.lastname
FROM Parents p
CROSS APPLY (
  SELECT TOP (1) 
     c.firstname
    ,c.lastname
  FROM Children c
  WHERE c.ParentId = p.Id
  ORDER BY c.datecreated DESC
  ) i
INNER JOIN Users s
  ON p.UserId = u.Id
WHERE (
    @search IS NOT NULL
    AND @search != ''
    AND p.name = @search
    )
  OR (@search IS NULL)
  OR (@search = '')

如何优化我的搜索查询以使其快速运行?在实践中,我的查询中有许多其他联接和更多活动,但是我认为这是问题所在,因为当我注释掉名字和姓氏搜索时,我的查询运行得非常快。

感谢所有帮助。

编辑:我也尝试过如下所示的内部连接和子查询,但是这产生的结果比最初显示的横向连接尝试更糟糕(执行时间约为 25 秒)

SELECT
  p.id, 
  p.name,
  c.firstname,
  c.lastname
from Parents P
INNER JOIN children c 
ON c.ParentId = p.Id
INNER JOIN Users s
ON p.UserId = s.Id
WHERE c.datecreated = (
select max(c1.datecreated) from children c1 where c1.ParentId = c.ParentId
)
and @search IS NOT NULL
AND @search != ''
AND (
  @search = c.firstname
  OR @search = c.lastname
  OR p.name = @search
  )
OR @search IS NULL
OR @search = ''
)

【问题讨论】:

第一个查询应该做什么?返回最近创建的 Children 记录具有匹配名称的父母(这是查询所做的)或返回至少有一个具有匹配名称的孩子的父母(这似乎是您想要的)。 返回的数据应该是所有父母的姓名和ID以及他们最近孩子的名字和姓氏。那么孩子的名字和姓氏应该是可搜索的。 @BaconBits 这更有意义吗? 使用“catch all”查询通常会生成一个相当糟糕的计划,因为优化器会在提供@search 时生成一个计划,但相同的计划在任何其他情况下都不起作用案例。常见的解决方案是使用动态 SQL 来完全避免多个 OR。我还建议先过滤名称,然后检查孩子是否是最新的。 @EzLo 您有任何示例或链接可以使这些建议更清晰吗?您将上述查询的哪一部分称为“全部”查询 推荐阅读:Dynamic Search Conditions in T-SQL. 【参考方案1】:

仅基于您问题的这一部分,我相信您遇到了参数嗅探问题。

我的查询从 1 秒或更短时间运行到大约 16 秒运行

请在查询末尾添加OPTION (RECOMPILE),看看会发生什么(但请调查此问题的含义并了解 CPU 后果)。也可以看OPTIMIZE FOR UNKNOWN


盲从,你可以尝试一些想法

将 1 次查找减少到 Children,即 WHERE c.datecreated = (select max(c1.datecreated) from children c1 where c1.ParentId = c.ParentId) 通过减少 @Search 逻辑来清理它
SELECT p.id,
       p.name,
       c.firstname,
       c.lastname
FROM Parents P
    INNER JOIN
    (
        select c1.firstname,
               c1.lastname,
               c1.ParentId,
               row_number() OVER (PARTITION BY c1.ParentId ORDER BY c1.datecreated DESC) as RN
        from children c1
    ) as c
        ON c.ParentId = p.Id
           AND c.RN = 1 --/*Get the Latest First,Lastname based on datecreated*/
    INNER JOIN Users s
        ON p.UserId = s.Id
WHERE 1 = 1
      AND (
              c.firstname = @search
              OR c.lastname = @search
              OR p.name = @search
              -- if @search is either NULL / '' it will return
              OR NULLIF(@search, '') IS NULL
          )
--OPTION (RECOMPILE) /*Uncomment this and see does it improve*/

另外请注意,您可能会遇到参数嗅探问题,即因为 @search 是一个查询参数,它看起来可能会有很大差异。

您可以尝试在查询末尾添加OPTION RECOMPILE 看看是否有影响

如果您还没有尝试过,也可以尝试一下。

更新表的统计信息 碎片整理/重建索引 查看实施 ola.hellengren 维护计划

最后,为了获得更多帮助,您可以使用 https://www.brentozar.com/pastetheplan/ 粘贴查询计划

【讨论】:

OPTION (RECOMPILE) 成功了 - 查询在 1 秒内恢复运行 看看这个***.com/a/40437749/7351346,看看OPTION (RECOMPILE)OPTION(OPTIMIZE FOR UNKNOWN)哪个更好

以上是关于使用横向连接搜索非常慢的主要内容,如果未能解决你的问题,请参考以下文章

MS Access 中通过 ODBC 连接 MS SQL 表的查询非常慢

Hive 连接查询非常慢

PerformanceCounter 在连接远程服务器时非常慢

中小型表上的左连接非常慢

PHP:远程 MySQL 连接非常慢

mysql:存储函数调用+左连接=非常非常慢