【问题标题】:Alternative for joining two tables multiple times多次连接两个表的替代方法
【发布时间】:2014-10-01 22:09:42
【问题描述】:

我有一种情况,我必须多次加入一个表。它们中的大多数需要左连接,因为有些值不可用。如何克服多次join时查询性能不佳的问题?

情景

表格

[项目]:ProjectId Guid,名称 VARCHAR(MAX)。
[UDF]:EntityId Guid、EntityType Char(1)、UDFCode Guid、UDFName varchar(20)
[UDFDetail]:UDFCode Guid,描述 VARCHAR(MAX)

关系:
[项目].ProjectId - [UDF].EntityId
[UDFDetail].UDFCode - [UDF].UDFCode

UDF 表根据 UDFName 列保存项目的自定义字段。但是,这些字段的值存储在 UDFDetail 的描述列中。 我有很多项目的自定义列,它们存储在 UDF 表中。

例如,要获取项目的两个字段,我执行以下选择:

SELECT 
    p.Name ProjectName,
    ud1.Description Field1, 
    ud1.UDFCode Field1Id,
    ud2.Description Field2,
    ud2.UDFCode Field2Id
FROM
    Project p
    LEFT JOIN UDF u1 ON
        u1.EntityId = p.ProjectId AND u1.ItemName='Field1'
    LEFT JOIN UDFDetail ud1 ON
        ud1.UDFCode = u1.UDFCode
    LEFT JOIN UDF u2 ON
        u2.EntityId = p.ProjectId AND u2.ItemName='Field2'
    LEFT JOIN UDFDetail ud2 ON
        ud2.UDFCode = u2.UDFCode

问题

想象一下上面的选择,但加入了大约 15 个字段。在我的查询中,我已经有大约 10 个字段并且性能不是很好。运行大约需要 20 秒。我对这些表有很好的索引,所以查看执行计划,它只进行索引查找而没有任何查找。关于联接,它需要左联接,因为该特定项目可能不存在字段 1。

问题

是否有更高效的方式来检索数据? 在这样的架构中,您将如何进行查询以检索一个项目的 10 个不同字段?

【问题讨论】:

  • 将 UDF 保存为 XML 是否为时已晚?这是一个更好的方法。
  • 是的。因为 db 模式来自第三方。所以我无法修改主要结构。我可以在架构中添加自己的表,但我需要保持默认结构不变。

标签: sql sql-server join sql-server-2008-r2 database-performance


【解决方案1】:

您的选择是pivot、显式聚合(带有条件函数)或joins。如果您设置了适当的索引,joins 可能是最快的方法。

正确的索引是UDF(EntityId, ItemName, UdfCode)

您可以通过运行如下查询来测试 group by 是否更快:

SELECT count(*)
FROM p LEFT JOIN
     UDF u1
     ON u1.EntityId = p.ProjectId LEFT JOIN
     UDFDetail ud1 
     ON ud1.UDFCode = u1.UDFCode;

如果运行速度足够快,那么您可以考虑使用group by 方法。

【讨论】:

  • 花了 20 秒。不仅仅是单独做左连接。实际上几乎一样。
  • pivot 可能会更快。我怀疑它可以更好地利用索引进行优化。但是,当您有两个联接时,这可能没有什么不同。
  • 我的索引很好。该操作是每个数据检索操作的索引查找。
【解决方案2】:

您可以尝试这个非常奇怪的装置(它看起来并不漂亮,但它执行一组外部连接)。中间结果是一个非常“宽”和“长”的数据集,然后我们可以通过聚合“压缩”它(例如,对于每个 ProjectName,每个 Field1 列将有 N 个结果、N-1 个 NULL 和 1 个非空结果,然后使用简单的 MAX 聚合进行选择)[N 是字段数]。

select ProjectName, max(Field1) as Field1, max(Field1Id) as Field1Id, max(Field2) as Field2, max(Field2Id) as Field2Id
from (
    select
        p.Name as ProjectName,
        case when u.UDFName='Field1' then ud.Description else NULL end as Field1,
        case when u.UDFName='Field1' then ud.UDFCode else NULL end as Field1Id,
        case when u.UDFName='Field2' then ud.Description else NULL end as Field2,
        case when u.UDFName='Field2' then ud.UDFCode else NULL end as Field2Id
    from Project p
        left join UDF u on p.ProjectId=u.EntityId
        left join UDFDetail ud on u.UDFCode=ud.UDFCode
) tmp
group by ProjectName

查询实际上可以在没有内部查询的情况下重写,但这应该不会有很大的不同:),看看 Gordon Linoff 的建议和你的回答,实际上也可能只需要大约 20 秒,但它仍然是值得一试。

【讨论】:

    猜你喜欢
    • 2021-11-08
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 2020-07-27
    • 1970-01-01
    • 2013-06-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多