简体   繁体   中英

How to union aggregated queries?

I have a query

SELECT
    CntApp = COUNT(app.ApplicationID)
    ,r.RegionName
    ,d.DistrictName 
FROM dim.Application app
    JOIN dim.Geography g ON (app.ApplicationID  = g.GeographyID)   
       AND (app.CountryId = g.CountryId)
    JOIN dim.Region r   ON r.RegionID = g.RegionID  
    JOIN dim.District d ON d.DistrictId = g.DistrictID
    JOIN dim.ZIPcode z  ON g.ZIPcodeID = z.ZIPcodeID         
GROUP BY
    r.RegionName
    ,d.DistrictName

and

SELECT
    CntCon = COUNT(c.ContractID)
    ,r.RegionName
    ,d.DistrictName
FROM dim.Contract c  
    JOIN dim.Geography g ON (c.ContractID = g.GeographyID)   
       AND (c.CountryId = g.CountryId)
    JOIN dim.Region r   ON r.RegionID = g.RegionID  
    JOIN dim.District d ON d.DistrictId = g.DistrictID
    JOIN dim.ZIPcode z  ON g.ZIPcodeID = z.ZIPcodeID         
GROUP BY
    r.RegionName
    ,d.DistrictName

which I want to merge into one table, so the group by still works. The result I want to get:

CntApp | CntCon | RegionName | DistrictName
31       24       Pardubicky   Pardubice
21       16       Pardubicky   Chrudim
...

I've tried UNION ALL but got something like this instead:

CntApp | CntCon | RegionName | DistrictName
    NULL     24       Pardubicky   Pardubice
    21       NULL     Pardubicky   Pardubice
    26       NULL     Pardubicky   Chrudim
    ...

You need to join 2 subqueries. This way you will get columns of both the queries side by side as you expect.

this should work :

SELECT iq1.CntApp , iq2.CntCon, iq1.iq1.RegionName,iq1.DistrictName 
FROM
(
SELECT
    CntApp = COUNT(app.ApplicationID)
    ,r.RegionName
    ,d.DistrictName 
FROM dim.Application app
    JOIN dim.Geography g ON (app.ApplicationID  = g.GeographyID)   
       AND (app.CountryId = g.CountryId)
    JOIN dim.Region r   ON r.RegionID = g.RegionID  
    JOIN dim.District d ON d.DistrictId = g.DistrictID
    JOIN dim.ZIPcode z  ON g.ZIPcodeID = z.ZIPcodeID         
GROUP BY
    r.RegionName
    ,d.DistrictName
) iq1
inner join
(
SELECT
    CntCon = COUNT(c.ContractID)
    ,r.RegionName
    ,d.DistrictName
FROM dim.Contract c  
    JOIN dim.Geography g ON (c.ContractID = g.GeographyID)   
       AND (c.CountryId = g.CountryId)
    JOIN dim.Region r   ON r.RegionID = g.RegionID  
    JOIN dim.District d ON d.DistrictId = g.DistrictID
    JOIN dim.ZIPcode z  ON g.ZIPcodeID = z.ZIPcodeID         
GROUP BY
    r.RegionName
    ,d.DistrictName
) iq2
on
iq1.RegionName = iq2.iq1.RegionName 
and 
iq1.DistrictName = iq2.DistrictName 

UNION ALL will combine results column by column. You need to introduce fake columns and aggregate it again (or join like in the other solution):

SELECT SUM(CntApp) CntApp, SUM(CntCon) CntCon, RegionName, DistrictName FROM (

    SELECT
        CntApp = COUNT(app.ApplicationID)
        ,CntCon = 0
        ,r.RegionName
        ,d.DistrictName 
    FROM dim.Application app
        JOIN dim.Geography g ON (app.ApplicationID  = g.GeographyID)   
           AND (app.CountryId = g.CountryId)
        JOIN dim.Region r   ON r.RegionID = g.RegionID  
        JOIN dim.District d ON d.DistrictId = g.DistrictID
        JOIN dim.ZIPcode z  ON g.ZIPcodeID = z.ZIPcodeID         
    GROUP BY
        r.RegionName
        ,d.DistrictName

    UNION ALL

    SELECT
        CntApp = 0
        ,CntCon = COUNT(c.ContractID)
        ,r.RegionName
        ,d.DistrictName
    FROM dim.Contract c  
        JOIN dim.Geography g ON (c.ContractID = g.GeographyID)   
           AND (c.CountryId = g.CountryId)
        JOIN dim.Region r   ON r.RegionID = g.RegionID  
        JOIN dim.District d ON d.DistrictId = g.DistrictID
        JOIN dim.ZIPcode z  ON g.ZIPcodeID = z.ZIPcodeID         
    GROUP BY
        r.RegionName
        ,d.DistrictName
) d
GROUP BY RegionName, DistrictName

You need a FULL JOIN

SELECT coalesce(app.RegionName, c.RegionName) AS RegionName,
       coalesce(app.DistrictName, c.DistrictName) AS DistrictName,
      coalesce(app.CntApp,0) AS CntApp,
      coalesce(c.CntCon,0) AS CntCon
FROM 
    (SELECT
       CntApp = COUNT(app.ApplicationID)
       ,r.RegionName
       ,d.DistrictName 
    FROM dim.Application app
       JOIN dim.Geography g ON (app.ApplicationID  = g.GeographyID)   
         AND (app.CountryId = g.CountryId)
       JOIN dim.Region r   ON r.RegionID = g.RegionID  
       JOIN dim.District d ON d.DistrictId = g.DistrictID
       JOIN dim.ZIPcode z  ON g.ZIPcodeID = z.ZIPcodeID         
    GROUP BY
       r.RegionName
       ,d.DistrictName
    ) app
    FULL JOIN 
    (
       SELECT
       CntCon = COUNT(c.ContractID)
       ,r.RegionName
       ,d.DistrictName
    FROM dim.Contract c  
       JOIN dim.Geography g ON (c.ContractID = g.GeographyID)   
         AND (c.CountryId = g.CountryId)
       JOIN dim.Region r   ON r.RegionID = g.RegionID  
       JOIN dim.District d ON d.DistrictId = g.DistrictID
       JOIN dim.ZIPcode z  ON g.ZIPcodeID = z.ZIPcodeID         
    GROUP BY
       r.RegionName
       ,d.DistrictName
    ) c ON app.RegionName = c.RegionName AND app.DistrictName = c.DistrictName

You can probably ditch the union, and it will be safer because your results wont be affected by stray cartesian joins that might occur if bad data works its way into the g/r/d/z tables:

SELECT
 CntApp,
 CntCon,
 r.RegionName,
 d.DistrictName 
FROM 
 dim.Geography g  
 INNER JOIN dim.Region r   ON r.RegionID = g.RegionID  
 INNER JOIN dim.District d ON d.DistrictId = g.DistrictID
 INNER JOIN dim.ZIPcode z  ON g.ZIPcodeID = z.ZIPcodeID 

 LEFT JOIN (SELECT ApplicationID, CountryID, COUNT(*) CntApp FROM dim.Application GROUP BY ApplicationID, CountryID) app
   ON (app.ApplicationID  = g.GeographyID) AND (app.CountryId = g.CountryId)

 LEFT JOIN (SELECT ContractID, CountryId, COUNT(*) as CntCon FROM dim.Contract GROUP BY ContractID, CountryId) c    
   ON (c.ContractID = g.GeographyID) AND (c.CountryId = g.CountryId)

Here's a bit of education point for you though:

If you have two blocks of data (from table, query, whatever) and you want to conenct them together vertically (more rows) then you use UNION If you want to conenct them together horizontally (more columns), you use JOIN

If we have:

a,b,c
a,b,c
a,b,c

And

a,y,z
a,y,z
a,y,z

This is what you get with UNION:

a,b,c
a,b,c
a,b,c
a,y,z
a,y,z
a,y,z

And this is what you get with JOIN:

a,b,c,y,z
a,b,c,y,z
a,b,c,y,z

Remember this, is will serve you well

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM