【问题标题】: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 您有任何示例或链接可以使这些建议更清晰吗?您将上述查询的哪一部分称为“全部”查询

标签: sql sql-server tsql


【解决方案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 秒内恢复运行
  • 看看这个stackoverflow.com/a/40437749/7351346,看看OPTION (RECOMPILE)OPTION(OPTIMIZE FOR UNKNOWN)哪个更好
猜你喜欢
  • 2022-11-12
  • 2016-03-12
  • 2016-08-20
  • 2018-03-19
  • 1970-01-01
  • 2021-10-04
  • 2019-12-12
  • 1970-01-01
  • 2013-07-22
相关资源
最近更新 更多