我有一种情况,我必须多次加入一个表。由于某些值不可用,因此大多数都需要保留连接。多次加入时如何克服查询性能不佳?
[Project]:ProjectId Guid,Name VARCHAR(MAX)。
[UDF]:EntityId Guid,EntityType Char(1),UDFCode Guid,UDFName varchar(20)
[UDFDetail]:UDFCode Guid,描述VARCHAR(MAX)
关系:
[Project] .ProjectId - [UDF] .EntityId
[UDFDetail] .UDFCode - [UDF] .UDFCode
UDF表根据UDFName列保存项目的自定义字段。但是,这些字段的值存储在UDFDetail的Description描述中。 我有很多Project的自定义列,它们存储在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个不同的字段?
答案 0 :(得分:1)
评论太长了:
您的选择是pivot
,显式聚合(使用条件函数)或join
。如果您设置了适当的索引,join
可能是最快的方法。
正确的索引是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
方法。
答案 1 :(得分:0)
你可以尝试这个非常奇怪的装置(它看起来不漂亮,但它只做一组外连接)。中间结果是一个非常“宽”和“长”的数据集,然后我们可以通过聚合“压缩”(例如,对于每个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秒,但它仍然是值得一试。