簡體   English   中英

不同行的總和

[英]SUM For Distinct Rows

給定以下表結構:

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

如何添加regions.population值的SUM以計算該國的人口? 求和時,我只需要使用每個區域的值一次,但是未分組的結果將為每個區域(該區域中的城市數)顯示多行。

示例數據:

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邏輯的解決方案。

這篇文章 可接受的解決方案似乎與我正在尋找的解決方案差不多,但是我還無法弄清楚如何將其應用於我的問題。


我的解決方案

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

注意: 我的實際查詢要復雜得多,與國家,地區或城市無關。 這是一個最小的例子來說明我的問題。

首先,您引用的其他帖子情況不一樣。 在這種情況下,連接就像[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

這樣做的原因是,在加入該區域之前將城市解析為每個區域一行,從而消除了交叉連接的情況。

離開剩下的,再增加一個加入人群的怎么樣

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演示

首先,您應該知道問題中提到的問題及其解決方案與問題及其解決方案稍有不同。 因此,沒有子查詢就不能只使用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個表: countriesregionscities列:

SQL:

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

資料:

==================================================================
|     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      |
------------------------------------------------------------------  

然后,您必須執行常規命令,才能得到以下代碼:

SQL:

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     |
-----------------------------------------------------

對於您的確切示例(問題中提到的數據),您將獲得:

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

希望所有這些都可以幫助您獲得想要的東西。

使用LEFT OUTER JOIN而不是INNER JOIN,因為如果國家/地區沒有區域,那么如果您使用INNER JOIN ,則該國家/地區不會進入結果,如果同一地區如果沒有城市,那么該國家/地區將不計入結果。

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

嘗試這個:

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 |

我在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 |
+-----------+---------+--------+------------+

如果您不想引入/更改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

您的問題很普遍。 您將所有與要查看的數據有關的表聯接在一起,然后開始考慮如何獲取該數據。 當您遇到不同的聚合時,這並不容易實現。

因此,最好加入您實際感興趣的內容。在您的情況下:國家和每個國家(總計)的地區/城市數據。 這樣可以使查詢簡單明了且易於維護。

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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM