【问题标题】:Sql server which is better "Rank" or "Left join with own table" to filter latest recordsSql server 哪个更好“Rank”或“Left join with own table”来过滤最新记录
【发布时间】:2014-08-26 15:03:37
【问题描述】:

我在数据库中有以下表格,大约有 1000 万条记录(未来可能会增加一倍):

create table PropertyOwners (
    [Key] int not null primary key,
    PropertyKey int not null, 
    BoughtDate DateTime, 
    OwnerKey int not null
)
go

上表包含所有者在特定时间拥有的所有财产,我想获取当前拥有超过一定数量财产的所有者,比如说一次超过 1000 个财产。我编写了两个不同的查询,一个使用“Rank”,另一个使用“Left join with own table”。

使用排名(大约需要 4 秒):

select OwnerKey, COUNT(1) PropertyCount 
from (
    select PropertyKey, OwnerKey, BoughtDate,
        RANK() over (partition by PropertyKey order by BoughtDate desc) as [Rank]
    from dbo.PropertyOwners 
) result
where [Rank]=1
group by OwnerKey
having COUNT(1)>1000

对同一张表使用左连接(大约需要 10 秒):

select OwnerKey, COUNT(1) PropertyCount 
from (
    select po.PropertyKey, po.OwnerKey, po.BoughtDate
    from dbo.PropertyOwners po
    left join dbo.PropertyOwners lo on lo.PropertyKey = po.PropertyKey
    and lo.BoughtDate > po.BoughtDate
    where lo.PropertyKey is null
) result
group by OwnerKey
having COUNT(1)>1000

这两个查询时间都是不可接受的,因为要花这么多时间,谁能帮我重写查询。我的表有以下索引:

CREATE NONCLUSTERED INDEX [IX_PropertyKey_BounghtDate] ON [dbo].[PropertyOwners] 
(
    [PropertyKey] ASC,
    [BoughtDate] DESC
)
INCLUDE ( [OwnerKey]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

【问题讨论】:

  • 查看您的查询计划。它可能根本没有使用您的索引。此外,您的左连接查询看起来很奇怪。请解释关系表的实际含义 - 进行自联接似乎没有意义。以及为什么当您拥有 PropertyKey 和 OwnerKey 时会有一个 [Key] 字段 - 它们的组合应该是您的主键(除非您有所有权的开始/结束日期)......
  • 看起来您在一组相当独特的字段上构建了一个非聚集索引,考虑到聚合,此查询可能不会被此查询使用。 PropertyKey 上的非聚集索引可能会提高性能,但执行计划应该为您提供所需的所有洞察力。分析/窗口函数通常比在查询中引入额外的连接更快。
  • 我们可以忽略[Key]字段,PropertyKey、OnwerKey、BoughtDate的组合是唯一的,可以复合PK。我检查了执行计划,查询使用的是“索引扫描”而不是“索引搜索”。我已经尽力尝试不同的索引组合但没有成功,这就是我在这里提出问题的原因。左连接查询是选择每个属性的记录,并使用 BoughtDate 检查为每个属性获取最新的记录。

标签: sql sql-server sql-server-2008


【解决方案1】:

您可以将其重写为(这可能会提高性能)

select OwnerKey, COUNT(1) PropertyCount 
from (
    select PropertyKey, MAX( BoughtDate) BoughtDate
    from dbo.PropertyOwners 
    Group by PropertyKey
) result INNER JOIN dbo.PropertyOwners po ON po.PropertyKey=result.PropertyKey and PO.boughtDate=result.boughtdate
group by OwnerKey
having COUNT(1)>1000

【讨论】:

    【解决方案2】:

    你有大量的数据,还有很多需要计算。 This Aaron Bertrand 的分析并不是您的问题,但它可能会对您有所帮助。

    根据您的支持索引,我建议尝试not exists 方法:

    select OwnerKey, count(*) as PropertyCount
    from PropertyOwners po
    where not exists (select 1
                      from PropertyOwners po2
                      where po2.PropertyKey = po.PropertyKey and
                            po2.BoughtDate > po.BoughtDate
                     )
    group by OwnerKey
    having count(*) > 1000;
    

    如果您无法让查询以足够快的速度运行,您可能需要升级硬件或使用触发器来使汇总表保持最新状态。

    【讨论】:

      【解决方案3】:

      分组永远不会很快。如果您运行查询的次数足够多,您可以查看 SQL Server 将建议的索引;谷歌诊断查询涉及sys.dm_db_index_usage_stats,他们会有所帮助。

      之前已经建议的另一个选项是构建一个汇总表。更轻量级的解决方案将是索引视图,但您必须了解创建它时将发挥作用的含义。

      【讨论】:

        猜你喜欢
        • 2013-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多