如何在MySQL中连接3个表格?

huangapple go评论75阅读模式
英文:

How can I join 3 tables in MySQL?

问题

我有这4个

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

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

Oracle SQL中,我可能会使用一些完整的OUTER JOIN。有什么替代方法吗?

这是连接其中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 

如何将最终的表Products添加到这个混合中呢?

英文:

I have these 4 tables:

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

in one query, I need to get a list of all cities (no duplicates) and for each, show the number of suppliers, companies, and products that can be found in that city.

In oracle SQL I would have used some Full OUTER JOIN. what's the alternative?

This is a suggested solution for joining 2 of the tables:

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 

How to add the final table Products to the mix?

答案1

得分: 1

使用UNION从所有三个表中获取所有城市,并左连接到结果中的三个表,最终进行聚合:

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。<br/>

英文:

With UNION get all the cities from all 3 tables and LEFT join to the results the 3 tables to finally aggregate:

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

See a simplified demo.<br/>

答案2

得分: 0

为了获取所有城市,因为我们没有城市表作为维度,我们可以从每个三个表中的city列中获取域,并使用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

这样我们可以得到在这三个表中出现的不同city值的列表。

我们可以使用这个集合,并对各个表进行外连接操作。但这有可能生成交叉积...如果有三个供应商与一个城市相关联,同时有四家公司与同一个城市相关联,我们将生成一个包含十二行的结果集。

为了解决这个问题,我们可以获取不同主键值的计数。

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

让我们在另一个查询中引用上面的查询,将其别名为ci。(如果我们有一个维度表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 ( /* 内联视图查询 */ ) 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

(在/* 内联视图查询 */的位置,使用第一个查询的SQL文本,以生成city的不同列表。)

英文:

to get all cities, since we don't have a city table as a dimension, we can get the domain from each of the city columns from each of the three tables, and combine them with UNION set operator:

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

That should get us a list of distinct city values that appear in the three tables.

We could take that set, and do outer join operations to the individual tables. But that has the potential to generate cross product... if there are three suppliers related to a city and four companies related to the same city, we would generate a resultset of twelve rows.

To fix that, we could get a count of DISTINCT primary key values.

Or, we can pre-aggregate the results in inline views, returning a single row per city. That avoids the problem of semi-cartesian products.

Let's reference the query above in an line view in another query. We will alias it ci. (If we had a dimension table city, we could reference that,)

Something like this:

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

(In place of /* inline view query */, use the SQL text from the first query, to generate the distinct list of city.)

答案3

得分: 0

只使用 union allgroup by

select city, sum(is_supplier), sum(is_company),
       sum(is_product), sum(is_delivery)
from ((select city, 1 as is_supplier, 0 as is_company, 0 as is_product, 0 as is_delivery
       from suppliers
      ) union all
      (select city, 0 as is_supplier, 1 as is_company, 0 as is_product, 0 as is_delivery
       from companies
      ) union all
      (select city, 0 as is_supplier, 0 as is_company, 1 as is_product, 0 as is_delivery
       from products
      ) union all
      (select city, 0 as is_supplier, 0 as is_company, 0 as is_product, 1 as is_delivery
       from deliveries
      ) 
     ) c
group by city;
 
或者,在MySQL中更简单的写法

```sql
select city, sum(which = 'supplier'), sum(which = 'company'),
       sum(which = 'product'), sum(which = 'delivery')
from ((select city, 'supplier' 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;
英文:

Just use union all and group 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;

Or, even more simply in MySQL:

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

huangapple
  • 本文由 发表于 2020年1月4日 01:17:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/59582684.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定