0

我有一个存储过程,它使用一些逻辑之间带回大块数据。我声明中的 PostFamilyTags 表有大约 150 万行。下面的 sql 语句运行很慢。

SELECT TOP(100)*  FROM
    (SELECT ROW_NUMBER() 
        OVER(ORDER BY p.date  DESC) as NUM,    
        m.postfamilymediaID, 
        m.postfamilyID, 
        p.blogID,
        p.userID, 
        p.BlogPostID, 
        m.postfamilymediatypeID as Type, 
        p.Title, 
        m.Address, 
        m.AddressEncoded, 
        m.ThumbNailAddress, 
        p.Date, 
        p.Summary, 
        p.Url, 
        m.ThumbNailIndex, 
        m.ThumbNailHeight, 
        m.ThumbNailWidth, 
        m.ThumbNailHeightAlt, 
        m.ThumbNailWidthAlt, 
        m.ItemName, 
        m.id3Title, 
        m.id3SubTitle, 
        m.id3ContributingArtists, 
        m.id3AlbumArtist, 
        m.id3Album, 
        m.id3Year, 
        m.id3Genre, 
        m.id3Length, 
        m.IsPublic      
FROM         
        PostFamilyMedia as m
        inner join 
        PostFamily as p on m.postfamilyID = p.postfamilyID 
        inner join 
        PostFamilyTags as pt on p.postfamilyID = pt.postfamilyID 
        inner join --Tags
        Tags as t on pt.tagID = t.tagID 
        Where t.TagLevel = 1 and t.Tag = 'Electronic' 
) AS a  WHERE NUM >= (100 + 1)  AND NUM <= (100 + 100)

但是当我去掉中间逻辑时,它的效果很好。

SELECT TOP(100)
        m.postfamilymediaID, 
        m.postfamilyID, 
        p.blogID,
        p.userID, 
        p.BlogPostID, 
        m.postfamilymediatypeID as Type, 
        p.Title, 
        m.Address, 
        m.AddressEncoded, 
        m.ThumbNailAddress, 
        p.Date, 
        p.Summary, 
        p.Url, 
        m.ThumbNailIndex, 
        m.ThumbNailHeight, 
        m.ThumbNailWidth, 
        m.ThumbNailHeightAlt, 
        m.ThumbNailWidthAlt, 
        m.ItemName, 
        m.id3Title, 
        m.id3SubTitle, 
        m.id3ContributingArtists, 
        m.id3AlbumArtist, 
        m.id3Album, 
        m.id3Year, 
        m.id3Genre, 
        m.id3Length, 
        m.IsPublic      
FROM         
        PostFamilyMedia as m
        inner join 
        PostFamily as p on m.postfamilyID = p.postfamilyID 
        inner join 
        PostFamilyTags as pt on p.postfamilyID = pt.postfamilyID 
        inner join --Tags
        Tags as t on pt.tagID = t.tagID 
        Where t.TagLevel = 1 and t.Tag = 'Electronic' 

谁能帮我让我的第一个 sql 语句运行得更快?

4

3 回答 3

1

由于您没有从任一标签表中选择任何列,因此您可以采用不同的方式编写此代码:

Select Top(100)
  *
From (
  Select
    Row_Number() Over (Order By p.date Desc) as Num,
    m.postfamilymediaID,
    m.postfamilyID,
    m.postfamilymediatypeID as Type, 
    p.Title, 
    p.Date -- and the rest of the fields
  From
    PostFamilyMedia As m
      Inner Join 
    PostFamily As p On m.postfamilyID = p.postfamilyID 
  Where
    Exists (
      Select 
        'x'
      From 
        Tags As t 
          Inner Join
        PostFamilyTags As pt 
          On pt.tagID = t.tagID 
      Where
        t.TagLevel = 1 and
        t.Tag = 'Electronic' And
        p.postfamilyID = pt.postfamilyID
      )
  ) As a
Where
  Num >= (100 + 1) And Num <= (100 + 100);

http://sqlfiddle.com/#!3/e073d/1的一些非常基本的测试中,以这种方式编写它将在日期列上使用覆盖索引,而您现在拥有它的方式却没有。这是否适用于更大的容量(以及它是否能提高性能)需要测试。

另外,我认为PostFamilyTags (PostFamilyID, TagID)是独一无二的。这种查询对您定义唯一索引的顺序很敏感。找出哪个最好的最简单方法是创建两者并查看优化器选择的内容。它看起来TagID, PostFamilyID最适合小批量。

如果Tags (TagLevel, Tag)是唯一的,您可能会在单独的查询中读取唯一的 TagID,然后从主查询中删除标签。由于标签相对于其他表可能很小,我不认为这会产生太大影响。

Declare @TagID int
Select
  @TagID = TagID
From
  Tags
Where
  TagLevel = 1 And
  Tag = 'Electronic'

Select Top(100)
  *
From (
  Select
    Row_Number() Over (Order By p.date Desc) as Num,
    m.postfamilymediaID,
    m.postfamilyID,
    m.postfamilymediatypeID as Type, 
    p.Title, 
    p.Date -- and the rest of the fields...
  From
    PostFamilyMedia As m
      Inner Join 
    PostFamily As p On m.postfamilyID = p.postfamilyID 
  Where
    Exists (
      Select 
        'x'
      From 
        PostFamilyTags As pt 
      Where
        p.postfamilyID = pt.postfamilyID And
        pt.TagID = @TagID
      )
  ) As a
Where
  Num >= (100 + 1) And Num <= (100 + 100);

当您PostFamilyMedia为每个匹配选择所有匹配项时,您可以通过将第一个表聚集在而不是其主键PostFamily上来加快速度。PostFamilyID, PostFamilyMediaID包含第二列是为了使其独一无二。SQL Server 会将隐藏的唯一标识符值添加到非唯一聚集索引中。权衡是,如果您的应用程序的另一部分选择单独的记录,它会稍微慢一些。您可以使用另一个覆盖索引来加快速度,但代价是插入和更新速度变慢。

于 2013-01-13T02:10:38.463 回答
0

问题可能是您认为“快”与“慢”的区别。当查询可能返回数百万行时,通常将第一行的时间用作查询的长度。但是,您需要考虑最后一行的时间。

通过添加row_number函数,SQL 需要在返回任何行之前生成整个结果集。它看起来更慢,但在测量整个结果集的时间时并没有真正慢(只是因为row_number())。

您可以通过row_number()在子查询中执行以下操作来加快速度:

from (select row_number() order by pdate) . . .
      from PostFamily p
     )

然后在on子句中包含条件。

明智地使用索引可能会有所帮助。你在表的索引中有日期吗?但是,我不确定这会有所帮助。

于 2013-01-13T00:16:30.297 回答
0

直接回答您的问题;我使第一个查询更快的方法是索引 table.column(order):**PostFamily.date DESC**

我这么说的原因是您似乎需要对数据进行分页(基于 [Num])。正如人们指出的那样,该ROW_NUMBER()操作需要读取所有符合条件的行。但是,它们不仅需要阅读,还需要排序。排序非常昂贵,尤其是在大型数据集上。我希望我所描述的索引会对此有所帮助。

为了解释这两个查询的区别,我可以提供一个类比。给定以下列表:

Mike
Susan
Andrew
Felicity
George
Laura
Ben
Robert
Julia
Jim
Kath
  1. 给我前三个名字
  2. 按字母倒序给我前 3 个名字

这两项任务中哪一项会花费您更多的工作?

于 2013-01-13T05:29:23.683 回答