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 必须排序并查看以获得不同的计数。那可能会非常昂贵。