如何在 MySQL 中连接 3 个表?

How can I join 3 tables in MySQL?

我有这 4 tables:

Suppliers(id_sup, name, city)
Companies( id_co, name, city)
Products(id_p, name, city)
Deliveries (id_sup, id_co, id_p)

在一个查询中,我需要获取所有城市的列表(无重复项),并为每个城市显示可以在该城市找到的供应商、公司和产品的数量。

oracle SQL 中,我会使用一些 Full OUTER JOIN。还有什么选择?

这是加入 table 中的 2 个的建议解决方案:

SELECT city                                          
     , COUNT(DISTINCT id_sup) Suppliers                   
     , COUNT(DISTINCT id_co) Companies                    
FROM ( SELECT COALESCE(s.city,c.city) city     
              , id_sup                           
              , id_co                            
         FROM Suppliers AS s                   
            LEFT OUTER JOIN Companies AS c ON c.city = s.city                  
         UNION                                   
         SELECT COALESCE(s.city,c.city) city     
              , id_sup                           
              , id_co                            
         FROM Suppliers AS s                   
            RIGHT OUTER JOIN Companies AS c ON c.city = s.city ) AS union_query

GROUP BY city 

如何将最终的 table Products 添加到混音中?

使用 UNION 从所有 3 个表中获取所有城市,然后将 LEFT 连接到最终聚合的 3 个表的结果:

select t.city,
  count(distinct s.id_sup) counter_suppliers,
  count(distinct c.id_co) counter_companies,
  count(distinct p.id_p) counter_products
from (
  select city from suppliers union  
  select city from companies union
  select city from products 
) t
left join suppliers s on s.city = t.city
left join companies c on c.city = t.city
left join products p on p.city = t.city
group by t.city

查看简化版 demo.

要获取所有城市,因为我们没有城市 table 作为维度,我们可以从三个 [=35] 的每个 city 列中获取域=]s,并将它们与 UNION 集合运算符组合:

SELECT cs.city
  FROM suppliers cs
 GROUP BY cs.city 

 UNION

SELECT cc.city 
  FROM companies cc
 GROUP BY cc.city 

 UNION

SELECT cp.city
  FROM products cp
 GROUP BY cp.city

这应该让我们得到一个列表,其中包含出现在三个 table 中的不同 city 值。

我们可以采用该集合,并对单个 table 进行外连接操作。但这有可能产生交叉产品……如果有三个供应商与一个城市相关,四个公司与同一个城市相关,我们将生成十二行的结果集。

要解决这个问题,我们可以获得 DISTINCT 主键值的计数。

或者,我们可以在内联视图中预先聚合结果,为每个城市返回一行。这样就避免了半笛卡尔积的问题。

让我们在另一个查询的行视图中引用上面的查询。我们将其命名为 ci。 (如果我们有一个维度 table city,我们可以引用它,)

像这样:

SELECT ci.city 
     , IFNULL(np.cnt_,0)   AS cnt_products
     , IFNULL(nc.cnt_,0)   AS cnt_companies
     , IFNULL(ns.cnt_,0)   AS cnt_suppliers

  FROM ( /* inline view query */ ) ci

  LEFT
  JOIN ( SELECT p.city
              , COUNT(1) AS cnt_
           FROM products p
          GROUP BY p.city
       ) np
    ON np.city = ci.city

  LEFT
  JOIN ( SELECT c.city
              , COUNT(1) AS cnt_
           FROM companies c
          GROUP BY c.city
       ) nc
    ON nc.city = ci.city

  LEFT
  JOIN ( SELECT s.city
              , COUNT(1) AS cnt_
           FROM suppliers s
          GROUP BY s.city
       ) ns
    ON ns.city = ci.city

 ORDER BY ci.city

(代替 /* inline view query */,使用第一个查询中的 SQL 文本,生成 city 的不同列表。)

只需使用 union allgroup by:

select city, sum(is_supplier), sum(is_company),
       sum(is_product), sum(is_delivery)
from ((select city, 1 as is_suppler, 0 as is_company, 0 as is_product, 0 as is_delivery
       from suppliers
      ) union all
      (select city, 0 as is_suppler, 1 as is_company, 0 as is_product, 0 as is_delivery
       from companies
      ) union all
      (select city, 0 as is_suppler, 0 as is_company, 1 as is_product, 0 as is_delivery
       from products
      ) union all
      (select city, 0 as is_suppler, 0 as is_company, 0 as is_product, 1 as is_delivery
       from deliveries
      ) 
     ) c
group by city;

或者,在 MySQL 中更简单:

select city, sum(which = 'supplier'), sum(which = 'company'),
       sum(which = 'product'), sum(which = 'delivery')
from ((select city, 'suppler' as which from suppliers
      ) union all
      (select city, 'company' as which from companies
      ) union all
      (select city, 'product' as which from products
      ) union all
      (select city, 'delivery' as which from deliveries
      ) 
     ) c
group by city;