SUM用于不同的行

时间:2014-12-19 16:38:57

标签: mysql sql select group-by sum

给出以下表格结构:

countries: id, name
regions: id, country_id, name, population
cities: id, region_id, name

...以及此查询...

SELECT c.name AS country, COUNT(DISTINCT r.id) AS regions, COUNT(s.id) AS cities
FROM countries AS c
JOIN regions AS r ON r.country_id = c.id
JOIN cities AS s ON s.region_id = r.id
GROUP BY c.id

如何添加SUM regions.population值来计算国家/地区的人口?我需要在求和时仅使用每个区域的值一次,但是未分组的结果对于每个区域(该区域中的城市数量)有多行。

示例数据:

mysql> SELECT * FROM countries;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | country 1 |
|  2 | country 2 |
+----+-----------+
2 rows in set (0.00 sec)

mysql> SELECT * FROM regions;
+----+------------+-----------------------+------------+
| id | country_id | name                  | population |
+----+------------+-----------------------+------------+
| 11 |          1 | region 1 in country 1 |         10 |
| 12 |          1 | region 2 in country 1 |         15 |
| 21 |          2 | region 1 in country 2 |         25 |
+----+------------+-----------------------+------------+
3 rows in set (0.00 sec)

mysql> SELECT * FROM cities;
+-----+-----------+---------------------------------+
| id  | region_id | name                            |
+-----+-----------+---------------------------------+
| 111 |        11 | City 1 in region 1 in country 1 |
| 112 |        11 | City 2 in region 1 in country 1 |
| 121 |        12 | City 1 in region 2 in country 1 |
| 211 |        21 | City 1 in region 1 in country 2 |
+-----+-----------+---------------------------------+
4 rows in set (0.00 sec)

带有示例数据的所需输出:

+-----------+---------+--------+------------+
| country   | regions | cities | population |
+-----------+---------+--------+------------+
| country 1 |       2 |      3 |         25 |
| country 2 |       1 |      1 |         25 |
+-----------+---------+--------+------------+

我更喜欢不需要更改JOIN逻辑的解决方案。

accepted solution this post似乎与我正在寻找的相邻,但我还没有弄清楚如何将其应用到我的问题中


我的解决方案

SELECT c.id AS country_id,
    c.name AS country,
    COUNT(x.region_id) AS regions,
    SUM(x.population) AS population,
    SUM(x.cities) AS cities
FROM countries AS c
LEFT JOIN (
        SELECT r.country_id,
            r.id AS region_id,
            r.population AS population,
            COUNT(s.id) AS cities
        FROM regions AS r
        LEFT JOIN cities AS s ON s.region_id = r.id
        GROUP BY r.country_id, r.id, r.population
    ) AS x ON x.country_id = c.id
GROUP BY c.id, c.name

注意: 我的实际查询要复杂得多,与国家,地区或城市无关。这是说明我的问题的最小例子。

7 个答案:

答案 0 :(得分:6)

首先,您引用的other post情况并不相同。在这种情况下,连接就像[A - >; B和A - > C],所以加权平均值(这是计算的作用)是正确的。在你的情况下,连接就像[A - >; B - > C],所以你需要一个不同的方法。

立即想到的最简单的解决方案确实涉及子查询,但不是复杂的:

SELECT 
    c.name AS country, 
    COUNT(r.id) AS regions, 
    SUM(s.city_count) AS cities,
    SUM(r.population) as population
FROM countries AS c
JOIN regions AS r ON r.country_id = c.id
JOIN 
    (select region_id, count(*) as city_count
    from cities 
    group by region_id) AS s
ON s.region_id = r.id
GROUP BY c.id

这样做的原因是它在加入区域之前将每个区域的城市解析为一行,从而消除了交叉连接情况。

答案 1 :(得分:4)

如何离开其余部分,只为人口增加一个加入

SELECT c.name AS country, 
       COUNT(distinct r.id) AS regions, 
       COUNT(s.id) AS cities, 
       pop_regs.sum as total_population
FROM countries AS c
LEFT JOIN regions AS r ON r.country_id = c.id
LEFT JOIN cities AS s ON s.region_id = r.id
left join 
(
    select country_id, sum(population) as sum 
    from regions 
    group by country_id
) pop_regs on pop_regs.country_id = c.id
GROUP BY c.id, c.name

SQLFiddle demo

答案 2 :(得分:3)

首先,您应该知道问题中提到的问题及其解决方案与您的问题及其解决方案略有不同。这就是为什么在没有子查询的情况下不能仅使用JOIN的原因。

表:

国家/地区:

===========================
|     id     |    name    |
===========================
|     1      | country 1  |
---------------------------
|     2      | country 2  |
---------------------------
|     3      | country 3  |
---------------------------
|     4      | country 4  |
---------------------------

地区:

=============================================
|    id    |country_id|   name   |population|
=============================================
|    1     |    1     | c1 - r1  |    10    |
---------------------------------------------
|    2     |    1     | c1 - r2  |    15    |
---------------------------------------------
|    3     |    1     | c1 - r3  |    15    |
---------------------------------------------
|    4     |    2     | c2 - r1  |    25    |
---------------------------------------------
|    5     |    3     | c3 - r1  |    13    |
---------------------------------------------

城市:

========================================
|     id     | region_id  |    name    |
========================================
|     1      |     1      |   city 1   |
----------------------------------------
|     2      |     1      |   city 2   |
----------------------------------------
|     3      |     2      |   city 3   |
----------------------------------------
|     4      |     2      |   city 4   |
----------------------------------------
|     5      |     2      |   city 5   |
----------------------------------------
|     6      |     3      |   city 6   |
----------------------------------------
|     7      |     3      |   city 7   |
----------------------------------------
|     8      |     4      |   city 8   |
----------------------------------------
|     9      |     4      |   city 9   |
----------------------------------------
|     10     |     4      |  city 10   |
----------------------------------------

作为一种简单的方法,您可以将countries表与一个连接regionscities表的子查询连接起来,以获得2个表:countries和{{1 } regions列:

SQL:

cities

数据:

SELECT
    r.id AS id,
    r.country_id AS country_id,
    r.name AS name,
    r.population AS population,
    COUNT(s.region_id) AS cities
FROM regions r
    /* we use left joint and not only join to get also regions without cities */
    LEFT JOIN cities s
        ON r.id = s.region_id
GROUP BY r.id

然后你必须做你正常的requet,它给你这个代码:

SQL:

==================================================================
|     id     | country_id |    name    | population |   cities   |
==================================================================
|     1      |     1      |  c1 - r1   |     10     |     2      |
------------------------------------------------------------------
|     2      |     1      |  c1 - r2   |     15     |     3      |
------------------------------------------------------------------
|     3      |     1      |  c1 - r3   |     15     |     2      |
------------------------------------------------------------------
|     4      |     2      |  c2 - r1   |     25     |     3      |
------------------------------------------------------------------
|     5      |     3      |  c3 - r1   |     13     |     0      |
------------------------------------------------------------------  

结果如下:

SELECT
    c.name AS country,
    COUNT(r.country_id) AS regions,
    /* ifnull is used here to show 0 instead of null */
    SUM(IFNULL(r.cities, 0)) AS cities,
    SUM(IFNULL(r.population, 0)) AS population
FROM countries c
    /* we use left joint and not only join to get also countries without regions */
    LEFT JOIN (
        SELECT
            /* we don't need regions.id and regions.name */
            r.country_id AS country_id,
            r.population AS population,
            COUNT(s.region_id) AS cities
        FROM regions r
            LEFT JOIN cities s
                ON r.id = s.region_id
        GROUP BY r.id
    ) r
    ON c.id = r.country_id
GROUP BY c.id

要进行比较,仅使用===================================================== | country | regions | cities | population | ===================================================== | country 1 | 3 | 7 | 40 | ----------------------------------------------------- | country 2 | 1 | 3 | 25 | ----------------------------------------------------- | country 3 | 1 | 0 | 13 | ----------------------------------------------------- | country 4 | 0 | 0 | 0 | ----------------------------------------------------- 删除没有区域的国家/地区以及拥有无城市地区的国家/地区:

JOIN

对于您的确切示例(使用您的问题中提到的数据),您将获得:

=====================================================
|  country   |  regions   |   cities   | population |
=====================================================
| country 1  |     3      |     7      |     40     |
-----------------------------------------------------
| country 2  |     1      |     3      |     25     |
-----------------------------------------------------

我希望所有这些都可以帮助你获得你想要的东西。

答案 3 :(得分:2)

使用 LEFT OUTER JOIN 代替 INNER JOIN ,因为如果国家/地区没有区域,那么如果您使用 INNER JOIN ,相同的扫描仪如果任何地区没有城市,则不计入结果。

因此,使用 LEFT OUTER JOIN 代替 INNER JOIN JOIN

试试这个:

SELECT c.name AS country, r.regions, r.population, r.cities 
FROM countries AS c 
LEFT OUTER JOIN (SELECT r.country_id, 
                        COUNT(r.id) AS regions, 
                        SUM(r.population) AS population, 
                        SUM(c.cities) AS cities
                 FROM regions AS r 
                 LEFT OUTER JOIN (SELECT c.region_id, COUNT(c.id) AS cities 
                                  FROM cities AS C
                                  GROUP BY c.region_id
                                 ) AS c ON r.id = c.region_id 
                 GROUP BY r.country_id
                ) AS r ON c.id = r.country_id;

检查SQL FIDDLE DEMO

<强>输出

| COUNTRY | REGIONS | POPULATION | CITIES |
|---------|---------|------------|--------|
|     usa |       3 |         16 |      4 |
| germany |       2 |          5 |      1 |

答案 4 :(得分:2)

我在sql中使用此查询测试了您在下面提供的同一个表

select regioncount.name as country,regioncount.regions, citycount.cities,regioncount.population    from
 (SELECT c.name,c.id,COUNT(r.id) AS regions ,SUM(r.population) as population
FROM countries AS c
JOIN regions AS r  on c.id = r.country_id GROUP BY c.id,c.name) as regioncount

join

(SELECT 
r.country_id,
    COUNT(s.id) AS cities 
FROM regions AS r
JOIN cities AS s  on r.id =s.region_id GROUP BY r.country_id) as citycount on citycount.country_id = regioncount.id

我得到了你想要的结果

+-----------+---------+--------+------------+
| country   | regions | cities | population |
+-----------+---------+--------+------------+
| country 1 |       2 |      3 |         25 |
| country 2 |       1 |      1 |         25 |
+-----------+---------+--------+------------+

答案 5 :(得分:1)

如果你不想引入/更改JOINSUBQUERY

,这是另一种方法。
SELECT 
  c.name AS country, 
  COUNT(distinct r.id) AS regions, 
  COUNT(s.id) AS cities,
  SUM(DISTINCT(((((r.id*r.id) + (r.population*r.id)))-(r.id*r.id))/r.id)) as total_population
FROM 
  countries AS c
  JOIN regions AS r ON r.country_id = c.id
  LEFT JOIN cities AS s ON s.region_id = r.id
GROUP 
  BY c.id

http://sqlfiddle.com/#!2/3dd8ba/22/0

答案 6 :(得分:1)

你的问题很常见。您加入所有与您想要查看的数据有关的表,然后开始考虑如何获取该数据。当涉及到你的情况下的不同聚合时,这并不容易实现。

更好地加入您真正感兴趣的内容。在您的情况下:每个国家/地区的国家/地区/(汇总)地区/城市数据。这使查询保持直接且易于维护。

select 
  c.name as country, 
  r.regions, 
  r.population,
  r.cities
from countries as c
join 
(
  select 
    country_id,
    count(*) as regions,
    sum(population) as population,
    sum((select count(*) from cities where cities.region_id = regions.id)) as cities
  from regions
  group by country_id
) as r on r.country_id = c.id;