通过多个分组连接多个表
Posted
技术标签:
【中文标题】通过多个分组连接多个表【英文标题】:Join multiple tables by multiple grouping 【发布时间】:2016-02-27 10:21:32 【问题描述】:我们有一个传球控制系统,每个传球动作都存储在Event
表中的MSSQL Server
中。我们想根据它们的关系将多个表与Event
表连接起来,如下图所示。但是,我不确定我使用的分组方法是否正确,因为查询需要很多时间。您能否澄清一下哦,如何通过多个分组加入这些表格?这是我使用的JOIN
子句:
SELECT t.CardNo, t.EventTime, t1.EmployeeName,
t1.Status, t2.EventCH, t3.DoorName, t4.JobName, t5.DeptName
FROM Event t
LEFT JOIN Employee AS t1 ON t.EmployeeID = t1.ID
LEFT JOIN EventType AS t2 ON t.EventTypeID = t2.ID
LEFT JOIN Door AS t3 ON t.DoorID = t3.ID
LEFT JOIN Job AS t4 ON t1.JobID = t4.ID
LEFT JOIN Department AS t5 ON t1.DepartmentID = t5.ID
ORDER BY t.EventID Desc
更新:以下发布执行计划:
【问题讨论】:
这个查询对我来说是正确的。你有什么问题? 耗时过长。实际上,我在第一秒就看到了结果(通过将滚动条拉到下方,在 SQL Server Management Studio 中的查询下方逐步检索结果)。但是查询需要 29 秒,对于 8 列的 125000 条记录来说似乎很高。您确定查询中有任何逻辑错误吗?我不确定分组顺序是否正确(也许最好将左 3 个表和右 3 个表分组,然后加入这 2 个组)。有什么想法吗? 您在这些表上设置了任何索引吗?鉴于您拥有 100K+ 记录和如此多的连接,这对我来说听起来并非不可能。EventType
- 内部联接,Door
- 我猜也有内部联接,Employee
+dep+job - 至少是一个带有内部联接的子查询,但如果事件仅在员工上下文中发生- 那么查询中的所有连接都必须是inner
。并查看您的实际执行计划。
尝试将LEFT
替换为INNER
,观察执行计划将如何变化。为每个外键列建立索引 - 再次查看执行计划。尝试使用左连接和索引 - 再次查看计划。
【参考方案1】:
您是否尝试过创建两个 CTE 来对连接进行分组?
因此,在一个 CTE 中为 Employee、Department 和 Job 创建一个联接。 对于另一个 CTE,为 Event、Eventtype 和 Door 创建一个连接。然后最后,使用 Employee ID 和 ID 加入两个 CTE。
将连接的表聚合在一起可能比一次性完成连接更快。对了,每张表的唯一性约束如何?
;WITH a AS
(
SELECT
t1.ID
,t1.EmployeeName
,t1.Status
,t4.JobName
,t5.DeptName
FROM
Employee t1
LEFT JOIN Job t4
ON t1.JobID = t4.ID
LEFT JOIN Department t5
ON t1.DepartmentID = t5.ID
)
,b AS
(
SELECT
t.EmployeeID
,t.CardNo
,t.EventTime
,t2.EventCH
,t3.DoorName
FROM
[Event] t
LEFT JOIN EventType t2
ON t.EventTypeID = t2.ID
LEFT JOIN Door t3
ON t.DoorID = t3.ID
)
SELECT
*
FROM
b
LEFT JOIN a
ON b.EmployeeID = a.ID
【讨论】:
这正是我在上面的评论中提出的问题(“......也许最好将左 3 个表和右 3 个表分组,然后加入这 2 个组”)。那么,您能否根据上述建议通过添加更新后的查询来更新您的答案? 另一方面,我不明白你的意思是“......每个表的唯一约束如何?”。请您再解释一下好吗? 我已经更新了我的评论,试试看 :)。在进行连接时,请始终确保您设置了唯一的列,即确保 ID 在 Employee 表中是唯一的,等等,否则当您连接到另一个表时,您可能会引入重复项。 这正是我所寻找的,通过这种 CTE 方法,查询时间从 30 秒缩短到 4 秒 :) 非常感谢... 很高兴听到:),顺便说一下,刚刚看了代码,b 应该加入到 a 中。我已经给你换了。从您的原始帖子中看到,事件表是您要加入的那个。【参考方案2】:您问题中的查询不代表查询计划中的查询。特别是有条件
RIGHT(t.cardno, 8) = RIGHT(t1.cardno, 8)
函数的使用排除了计划索引的使用。
我的建议是为每个表添加一个虚拟列,对其进行索引,然后在连接中使用它:
alter table tEvent add cardno8 as RIGHT(cardno, 8);
alter table tEmployee add cardno8 as RIGHT(cardno, 8);
create index idx_tEvent_cardno8 tEvent(cardno8);
create index idx_tEmployee_cardno8 tEmployee(cardno8);
这应该有助于提高性能,如果您将比较更改为:
on t.cardno8 = t1.cardno8
此外,您的查询没有WHERE
子句,因此它(可能)正在处理大量数据。
【讨论】:
实际上,为了简洁起见,我修改了查询,但索引还有另一个问题。通常我应该使用 PK-FK 来加入和 Employee 表,但由于 Event 表中的 EmployeeID 中没有数据,我必须使用 CardNo 字段(也是 RIGHT 方法,因为数字不相等)。另一方面,在尝试创建索引时,我遇到了您的代码错误。所以,我有两个关于这个问题的问题。你能澄清一下吗? 1) 我们是否应该使用索引这种情况,即当我们必须使用除 PK-FK 之外的字段(即 CardNo)来连接表时?如果是这样,你能检查上面的代码吗? 2) 修改table,加入cardno8,这个虚拟列会不会有问题?谢谢...以上是关于通过多个分组连接多个表的主要内容,如果未能解决你的问题,请参考以下文章