SQL 服务器多个加入相同 table
SQL Server multiple joins on same table
我是 sql 服务器的新手 我正在使用存储过程并尝试使用相同的 table 进行多个连接,并使用第一个连接 table 进行另一个连接
例如,我正在尝试从 table A 加入 table B 和 C 以及 table D 使用 table B 加入记录,但如果有人能告诉我,我会得到错误的结果我做错了什么
这是我的查询
SELECT
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS itemNo,
ac.title,
ac.id,
ac.status,
count(comp.id) as total_comps,
count(comp_emps.id) as total_comp_emps,
count(assoc_emps.id) as total_assoc_emps,
--SUM(total_comp_emps,total_assoc_emps) total_comp_assoc_emps,
SUM((COALESCE(comp_emps.id,0))+(COALESCE(assoc_emps.id,0))) total_comp_assoc_emps,
--(total_comp_emps + total_assoc_emps) as total_comp_assoc_emps,
count(u.id) as total_users
FROM associations ac
left join companies comp on comp.assoc_id = ac.id
left join users u on u.assoc_id = ac.id
left join employees comp_emps on comp_emps.comp_id = comp.id
left join employees assoc_emps on assoc_emps.assoc_id = ac.id
where ac.title like ('%' + @search + '%') and ac.status = @status
group by ac.title, ac.status, ac.id
order by ac.title asc
并得到这个结果
公司总数为 2,用户总数为 3
有人要吗?
当您开始沿着关系图的多个路径(例如关联 -> 公司 -> 员工、关联 -> 员工和关联 -> 用户)加入表时,您会开始获得大量重复记录,这些记录是几乎无法解决。
我认为您需要的是一些相关的子查询,它们可以独立检查每条路径的记录以计算结果。
以下是一种方法。
SELECT
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS itemNo,
ac.title,
ac.id,
ac.status,
(
SELECT count(comp.id)
FROM companies comp
WHERE comp.assoc_id = ac.id
) as total_comps,
(
SELECT count(comp_emps.id)
FROM companies comp
JOIN employees comp_emps
ON comp_emps.comp_id = comp.id
WHERE comp.assoc_id = ac.id
) as total_comp_emps,
(
SELECT count(assoc_emps.id)
JOIN employees assoc_emps
WHERE assoc_emps.assoc_id = ac.id
) as total_assoc_emps,
--SUM(total_comp_emps,total_assoc_emps) total_comp_assoc_emps,
--SUM((COALESCE(comp_emps.id,0))+(COALESCE(assoc_emps.id,0))) total_comp_assoc_emps,
--(total_comp_emps + total_assoc_emps) as total_comp_assoc_emps,
(
SELECT count(u.id)
FROM users u
WHERE u.assoc_id = ac.id
) as total_users
FROM associations ac
where ac.title like ('%' + @search + '%') and ac.status = @status
order by ac.title asc
您还可以将中间计算移动到 CROSS APPLY 中,然后在最终 select 中引用这些中间结果。如果您需要重复使用某些结果,这会很有用。
SELECT
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS itemNo,
ac.title,
ac.id,
ac.status,
counts.total_comps,
counts.total_comp_emps,
counts.total_assoc_emps,
--SUM(total_comp_emps,total_assoc_emps) total_comp_assoc_emps,
--SUM((COALESCE(comp_emps.id,0))+(COALESCE(assoc_emps.id,0))) total_comp_assoc_emps,
(counts.total_comp_emps + counts.total_assoc_emps) as total_comp_assoc_emps,
counts.total_users
FROM associations ac
CROSS APPLY (
SELECT
(
SELECT count(comp.id)
FROM companies comp
WHERE comp.assoc_id = ac.id
) as total_comps,
(
SELECT count(comp_emps.id)
FROM companies comp
JOIN employees comp_emps
ON comp_emps.comp_id = comp.id
WHERE comp.assoc_id = ac.id
) as total_comp_emps,
(
SELECT count(assoc_emps.id)
FROM employees assoc_emps
WHERE assoc_emps.assoc_id = ac.id
) as total_assoc_emps,
(
SELECT count(u.id)
FROM users u
WHERE u.assoc_id = ac.id
) as total_users
) counts
where ac.title like ('%' + @search + '%') and ac.status = @status
order by ac.title asc
我不清楚你total_comp_assoc_emps的意图,但上面只是添加了两个中间值。如果员工可以直接或通过公司加入协会,并且您不想重复计算他们,则 total_comp_assoc_emps 计算会稍微复杂一些。
以下收集使用来自两个来源的 ID,并对每个不同的 ID 计数一次。
(
SELECT count(distinct id)
FROM (
SELECT comp_emps.id
FROM companies comp
JOIN employees comp_emps
ON comp_emps.comp_id = comp.id
WHERE comp.assoc_id = ac.id
UNION
SELECT assoc_emps.id
FROM employees assoc_emps
WHERE assoc_emps.assoc_id = ac.id
) emp_ids
) as total_assoc_emps,
您正在加入协会的用户和公司。如果有 2 个公司和 3 个用户,您将得到 6 行。然后你数 count(comp.id)
和 count(u.id)
。连接行中的两个 ID 都不为空,因此它们的结果等于 count(*)
即 6.
处理多个聚合时,建议在加入之前聚合您的数据。毕竟你想加入公司的数量和用户的数量到你的协会。这些数字是聚合结果。
至于员工,这甚至会变得更复杂一些,因为员工可以直接或通过公司与协会相关联。因此,如果有 5 名员工与公司相关联,5 名员工与协会相关联,那么您可能会得到总共 8 名员工,以防两人为公司工作并且也直接与协会相关联。这可能最好在 SQL Server 中的横向外部连接 aka OUTER APPLY
中解决。
好吧,来自一些特定的关联,您也可以将所有外部联接设为横向。
select
row_number() over(order by (select 1)) as itemno,
a*, c.*, e.*, u.*
from associations a
outer apply
(
select count(*) as total_companies
from companies
where companies.assoc_id = a.id
) c
outer apply
(
select
count(*) as total_employees,
count(case when employees.assoc_id = a.id then 1 end) as association_employees,
count(case when employees.comp_id = c.id then 1 end) as company_employees
from employees
where employees.assoc_id = a.id
or employees.comp_id = c.id
) e
outer apply
(
select count(*) as total_users
from users
where users.assoc_id = a.id
) u
where a.title like ('%' + @search + '%') and a.status = @status
order by a.title;
这比连接所有单行然后计算不同的 ID 要好得多,因为如果你这样做,你会创建一个可能巨大的中间结果,DBMS 必须排序并查看以获得不同的计数。那可能会非常昂贵。
我是 sql 服务器的新手 我正在使用存储过程并尝试使用相同的 table 进行多个连接,并使用第一个连接 table 进行另一个连接 例如,我正在尝试从 table A 加入 table B 和 C 以及 table D 使用 table B 加入记录,但如果有人能告诉我,我会得到错误的结果我做错了什么
这是我的查询
SELECT
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS itemNo,
ac.title,
ac.id,
ac.status,
count(comp.id) as total_comps,
count(comp_emps.id) as total_comp_emps,
count(assoc_emps.id) as total_assoc_emps,
--SUM(total_comp_emps,total_assoc_emps) total_comp_assoc_emps,
SUM((COALESCE(comp_emps.id,0))+(COALESCE(assoc_emps.id,0))) total_comp_assoc_emps,
--(total_comp_emps + total_assoc_emps) as total_comp_assoc_emps,
count(u.id) as total_users
FROM associations ac
left join companies comp on comp.assoc_id = ac.id
left join users u on u.assoc_id = ac.id
left join employees comp_emps on comp_emps.comp_id = comp.id
left join employees assoc_emps on assoc_emps.assoc_id = ac.id
where ac.title like ('%' + @search + '%') and ac.status = @status
group by ac.title, ac.status, ac.id
order by ac.title asc
并得到这个结果
公司总数为 2,用户总数为 3 有人要吗?
当您开始沿着关系图的多个路径(例如关联 -> 公司 -> 员工、关联 -> 员工和关联 -> 用户)加入表时,您会开始获得大量重复记录,这些记录是几乎无法解决。
我认为您需要的是一些相关的子查询,它们可以独立检查每条路径的记录以计算结果。
以下是一种方法。
SELECT
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS itemNo,
ac.title,
ac.id,
ac.status,
(
SELECT count(comp.id)
FROM companies comp
WHERE comp.assoc_id = ac.id
) as total_comps,
(
SELECT count(comp_emps.id)
FROM companies comp
JOIN employees comp_emps
ON comp_emps.comp_id = comp.id
WHERE comp.assoc_id = ac.id
) as total_comp_emps,
(
SELECT count(assoc_emps.id)
JOIN employees assoc_emps
WHERE assoc_emps.assoc_id = ac.id
) as total_assoc_emps,
--SUM(total_comp_emps,total_assoc_emps) total_comp_assoc_emps,
--SUM((COALESCE(comp_emps.id,0))+(COALESCE(assoc_emps.id,0))) total_comp_assoc_emps,
--(total_comp_emps + total_assoc_emps) as total_comp_assoc_emps,
(
SELECT count(u.id)
FROM users u
WHERE u.assoc_id = ac.id
) as total_users
FROM associations ac
where ac.title like ('%' + @search + '%') and ac.status = @status
order by ac.title asc
您还可以将中间计算移动到 CROSS APPLY 中,然后在最终 select 中引用这些中间结果。如果您需要重复使用某些结果,这会很有用。
SELECT
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS itemNo,
ac.title,
ac.id,
ac.status,
counts.total_comps,
counts.total_comp_emps,
counts.total_assoc_emps,
--SUM(total_comp_emps,total_assoc_emps) total_comp_assoc_emps,
--SUM((COALESCE(comp_emps.id,0))+(COALESCE(assoc_emps.id,0))) total_comp_assoc_emps,
(counts.total_comp_emps + counts.total_assoc_emps) as total_comp_assoc_emps,
counts.total_users
FROM associations ac
CROSS APPLY (
SELECT
(
SELECT count(comp.id)
FROM companies comp
WHERE comp.assoc_id = ac.id
) as total_comps,
(
SELECT count(comp_emps.id)
FROM companies comp
JOIN employees comp_emps
ON comp_emps.comp_id = comp.id
WHERE comp.assoc_id = ac.id
) as total_comp_emps,
(
SELECT count(assoc_emps.id)
FROM employees assoc_emps
WHERE assoc_emps.assoc_id = ac.id
) as total_assoc_emps,
(
SELECT count(u.id)
FROM users u
WHERE u.assoc_id = ac.id
) as total_users
) counts
where ac.title like ('%' + @search + '%') and ac.status = @status
order by ac.title asc
我不清楚你total_comp_assoc_emps的意图,但上面只是添加了两个中间值。如果员工可以直接或通过公司加入协会,并且您不想重复计算他们,则 total_comp_assoc_emps 计算会稍微复杂一些。
以下收集使用来自两个来源的 ID,并对每个不同的 ID 计数一次。
(
SELECT count(distinct id)
FROM (
SELECT comp_emps.id
FROM companies comp
JOIN employees comp_emps
ON comp_emps.comp_id = comp.id
WHERE comp.assoc_id = ac.id
UNION
SELECT assoc_emps.id
FROM employees assoc_emps
WHERE assoc_emps.assoc_id = ac.id
) emp_ids
) as total_assoc_emps,
您正在加入协会的用户和公司。如果有 2 个公司和 3 个用户,您将得到 6 行。然后你数 count(comp.id)
和 count(u.id)
。连接行中的两个 ID 都不为空,因此它们的结果等于 count(*)
即 6.
处理多个聚合时,建议在加入之前聚合您的数据。毕竟你想加入公司的数量和用户的数量到你的协会。这些数字是聚合结果。
至于员工,这甚至会变得更复杂一些,因为员工可以直接或通过公司与协会相关联。因此,如果有 5 名员工与公司相关联,5 名员工与协会相关联,那么您可能会得到总共 8 名员工,以防两人为公司工作并且也直接与协会相关联。这可能最好在 SQL Server 中的横向外部连接 aka OUTER APPLY
中解决。
好吧,来自一些特定的关联,您也可以将所有外部联接设为横向。
select
row_number() over(order by (select 1)) as itemno,
a*, c.*, e.*, u.*
from associations a
outer apply
(
select count(*) as total_companies
from companies
where companies.assoc_id = a.id
) c
outer apply
(
select
count(*) as total_employees,
count(case when employees.assoc_id = a.id then 1 end) as association_employees,
count(case when employees.comp_id = c.id then 1 end) as company_employees
from employees
where employees.assoc_id = a.id
or employees.comp_id = c.id
) e
outer apply
(
select count(*) as total_users
from users
where users.assoc_id = a.id
) u
where a.title like ('%' + @search + '%') and a.status = @status
order by a.title;
这比连接所有单行然后计算不同的 ID 要好得多,因为如果你这样做,你会创建一个可能巨大的中间结果,DBMS 必须排序并查看以获得不同的计数。那可能会非常昂贵。