【问题标题】:SQL Server T-SQL Query OptimizationSQL Server T-SQL 查询优化
【发布时间】:2014-11-04 06:52:57
【问题描述】:

我正在尝试优化以下 T-SQL 查询:

SELECT Person.*
FROM Person
WHERE ZipCode LIKE '123%'
AND City = 'Washington'
AND NumberOfHomes in (1, 2, 3)
AND
(
    EXISTS
    (
        SELECT * FROM House
        WHERE Person.ID = House.PersonID
        AND House.Type = 'TOWNHOUSE'
        AND House.Size = 'Medium'
    )
    OR
    EXISTS
    (
        SELECT * FROM Color
        WHERE Person.ID = Color.PersonID
        AND Color.Foreground IN ('Green', 'Blue', 'Purple')
    )
)

对于优化查询的任何回应,我将不胜感激。

特别是,有没有一种方法可以将查询转换为更有效的查询,只使用单个 SELECT 语句而不使用任何内部 SELECT 语句?

谢谢!

【问题讨论】:

  • 如果没有查询的实际执行计划,就不能说太多。一个小提示,对于 EXISTS,您不需要返回所有行或列,只需从查询中返回 TOP 1 1 EXISTS(SELECT TOP 1 1 FROM House...)
  • @user2321864 你放什么都没关系。 SQL Server 不在乎,它知道它只是在寻找 1 行然后它可以短路,它知道它不返回任何数据。想要证明没关系?将* 替换为1/0

标签: sql sql-server performance optimization query-optimization


【解决方案1】:

这是查询:

SELECT p.* 
FROM Person p
WHERE p.ZipCode LIKE '123%'  AND p.City = 'Washington' AND p.NumberOfHomes in (1, 2, 3) AND
      (EXISTS (SELECT *
               FROM House h
               WHERE p.ID = h.PersonID AND h.Type = 'TOWNHOUSE' AND h.Size = 'Medium'
             ) OR 
       EXISTS (SELECT *
               FROM Color c
               WHERE p.ID = c.PersonID AND c.Foreground IN ('Green', 'Blue', 'Purple')
              )
      );

无需重写查询,您可以使用索引对其进行优化。我会推荐:

Person(City, ZipCode, NumberOfHomes, Id);
House(PersonId, Type, Size);
Color(PersonID, Foreground)

问题,不过。你确定ids in theHouseandColortables really match back toPerson.Id? Normally, they would have a column called something likePersonId`。

【讨论】:

  • 我很好奇,也许你知道答案。 EXISTS (SELECT *EXISTS (SELECT 1 的表现有什么不同吗?
  • @wdosanjos 。 . .不,编译器将这两个更改为相同的代码。通常,我会使用select 1,但我留下了select *,因为这就是OP 的措辞。
  • @wdosanjos NO。请参阅my comment above
【解决方案2】:

请试试这个:

SELECT p.*
FROM Person p
WHERE Substring(Ltrim(Rtrim(p.ZipCode)),1,3) = '123' AND p.City = 'Washington'AND 
(p.NumberOfHomes=1 or  p.NumberOfHomes=2 or p.NumberOfHomes=3))
AND
(
EXISTS
(
    SELECT 1 FROM House h
    WHERE p.ID = h.PersonID
    AND h.Type = 'TOWNHOUSE'
    AND h.Size = 'Medium'
)
OR
EXISTS
(
    SELECT 1 FROM Color c
    WHERE p.ID = c.PersonID
    AND (c.Foreground ='Green' or c.Foreground='Blue' or  c.Foreground='Purple')
)
);

这也会更好:

SELECT 
    p.*
FROM Person p
Left join House h
    On (p.Id=h.PersonID)
Left join Color c
    On (p.id=c.PersonID)
WHERE Substring(Ltrim(Rtrim(p.ZipCode)),1,3) = '123' AND p.City = 'Washington'AND 
(p.NumberOfHomes=1 or  p.NumberOfHomes=2 or p.NumberOfHomes=3)) and Isnull(h.Type,'') =   'TOWNHOUSE' AND Isnull(h.Size,'') = 'Medium' AND 
(Isnull(c.Foreground,'') ='Green' or Isnull(c.Foreground,'')='Blue' or Isnull(c.Foreground,'')='Purple') and 
(h.PersonID is not null or  c.PersonID is not null);

【讨论】:

  • ¿ 为什么是 -1?是滥用
  • 您好,这些查询并不比任何其他查询特别好。它们都显示类似的时序信息,设置如下:SET STATISTICS TIME ON & SET STATISTICS IO ON。有没有办法获得更准确的时间信息以与其他查询进行比较?
  • 与 Sam Yi 的备注相同:将 WHERE EXISTS() 转换为 LEFT OUTER JOINs 将可能返回相同的记录,双倍、三倍等...如果与 Color 或 @ 有多个匹配项987654326@表格数据。我也很惊讶 SubString(1,3) 会比 LIKE 'xyz%' 快。无论如何,将 IN (,,) 推出 OR 是由查询优化器完成的,我个人更喜欢 IN (,,) 结构的可读性。
  • 关于将 Exists() 更改为 Left 连接是正确的,我确信 substring(p.ZipCode,1,3) 或 Left(p.ZipCode,3) 会比 Like'%123 更好用' 也比 in 更有效。
【解决方案3】:

左连接和检查 null 将比进行存在检查更快。此外,如果 NumberofHomes 是整数,则执行 BETWEEN 将与 IN 相同。

SELECT p.*
FROM Person p
LEFT JOIN House h
    ON p.ID = h.PersonID
    AND h.Type = 'TOWNHOUSE'
    AND h.Size = 'Medium'
LEFT JOIN Color c
    ON p.ID = c.PersonID
    AND c.Foreground IN ('Green', 'Blue', 'Purple')
WHERE p.ZipCode LIKE '123%'
  AND p.City = 'Washington'
  AND p.NumberOfHomes BETWEEN 1 AND 3
  AND (h.PersonID is not null or c.PersonID is not null)

或者你可以试试这样的...

select t.* 
from (
    select personid from house
    where type = 'townhouse' and size = 'medium'
    union
    select personid from color
    where foreground in ('green','blue','purple')
) pid
cross apply (
    select *
    from person p
    where p.id = pid.personid
      and p.zipcode like '123%'
      and p.city = 'washington'
      and p.numberofhomes between 1 and 3
    ) t
where t.id is not null

这些盲区真的很难优化。根据您的数据分布情况,上述查询可能会为您提供更好的结果。

【讨论】:

  • 这不是等效查询。而且我看不出为什么它应该更有效。
  • 对不起...我被从桌子上拉开。
  • 您好,这些查询并不比任何其他查询特别好。它们都显示类似的时序信息,设置如下:SET STATISTICS TIME ON & SET STATISTICS IO ON。有没有办法获得更准确的时间信息以与其他查询进行比较?
  • 如果ColorHouse 表数据有多个匹配项,这些查询可能会从p.* 返回相同的记录加倍、加倍等。另外,我不知道为什么EXISTS() 的名声这么差;根据我的经验,它的性能与使用 LEFT OUTER JOIN 方法一样好,在某些情况下甚至更好。
  • @Peter 如果您更喜欢 GUI 来比较 MSSQL 上查询的行为和性能,我个人非常喜欢 SQL Sentry Plan Explorer。基本上,它与您从查询计划和探查器中获得的信息相同(恕我直言)更容易掌握演示文稿。 (PS:我绝不隶属于SqlSentry =)
【解决方案4】:

通常优化和拥有多个不同的选择语句是不同的主题,因为查询优化器 (SQL Server) 通常会采用您的 sql 语句并以它认为最有效的方式运行它。

说是的,您可以通过几种不同的方式获取语句并将它们组合成一个 sql 语句,这是一个示例。这将保留您的人员表并从符合您的条件的 House OR Color 表中获取匹配项。

<!-- language:SQL-->
SELECT *
FROM Person Left Outer Join House ON Person.ID = House.PersonID Left Outer Join Color ON
Person.ID= Color.PersonID
WHERE (ZipCode LIKE '123%'
    AND City = 'Washington'
    AND Person.NumberofHomes in (1, 2, 3) )
    AND (
        House.Type = 'TOWNHOUSE'
        AND House.Size = 'Medium'
    )
    OR(
         Color.Foreground IN ('Green', 'Blue', 'Purple')
    )

我建议您重新考虑您的模型。例如,有颜色的 PersonID 和有 numberofhomes 一样是非常可疑的(例如,这可能是根据 House 表中具有该人的 id 的计数来计算的)。还有一些其他有问题的规范化属性。不是您的问题的一部分,但我认为您可能想考虑一下。

【讨论】:

  • 数据模型是正确的,但名称是编造的,所以看起来可能有点奇怪。此外,这些查询并不比任何其他查询特别好。它们都显示类似的时序信息,设置如下:SET STATISTICS TIME ON & SET STATISTICS IO ON。有没有办法获得更准确的时间信息以与其他查询进行比较?
  • 关于数据模型。我理解,不了解用户和问题的背景或体验,我想展示一个事实,即良好的建模是关键。
  • 正如我提到的优化和拥有单个查询语句实际上并不总是一回事。因为查询优化器通常会接受查询并在它认为合适的时候执行它们(基于执行计划)。
  • 当然 IO 和时间在优化中很重要,但是,大多数 dbas 会将执行计划视为优化的最佳指标。比较查询的最佳选择是通过估计的查询计划运行它们并查看诸如表扫描之类的项目(通常被认为是坏的并且应该避免)。这不仅可以让您比较不同的 sql 脚本并查看是否优化,还可以确保您和您的 sql 脚本正确使用索引。
  • 顺便说一句,我针对未索引的表运行了您的原始查询和上面的查询,查询优化器采用了两者并以相同的方式执行(当然是所有表扫描)。然后我放置了我认为适合这些表的索引并最终得到了所有。我最终进行了 3 次索引搜索。这是 IMO 的一个很好的改进。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多