Skip to main content
replaced http://dba.stackexchange.com/ with https://dba.stackexchange.com/
Source Link

SaddamSaddam has a smart solution, but it carries some weaknesses. Imagine a source named 'Fresno, CA' (with comma in the string). split_part() would be fooled by the separator character in the string ...

Saddam has a smart solution, but it carries some weaknesses. Imagine a source named 'Fresno, CA' (with comma in the string). split_part() would be fooled by the separator character in the string ...

Saddam has a smart solution, but it carries some weaknesses. Imagine a source named 'Fresno, CA' (with comma in the string). split_part() would be fooled by the separator character in the string ...

added 20 characters in body
Source Link
Erwin Brandstetter
  • 182.1k
  • 28
  • 457
  • 620
CREATE TEMP TABLE defso (def numeric, so varchar);  -- once per session!

SELECT country_code
     , country_name
     , (def2014d14).def AS deflator_2014  -- note the parentheses!
     , (def2014d14).so  AS source_2014
     , (def2015d15).def AS deflator_2015
     , (def2015d15).so  AS source_2015
     , (def2016d16).def AS deflator_2016
     , (def2014d16).so  AS source_2016
FROM   crosstab (
    'SELECT country_code, country_name, year, (deflator, source)::defso
     FROM   deflator
     ORDER  BY 1'
  , 'SELECT generate_series(2014, 2016)::int2'
   ) AS ct (country_code int2
          , country_name text
          , def2014d14 defso
          , def2015d15 defso
          , def2016d16 defso
   );
SELECT country_code, country_name
     , d14.deflator AS deflator_2014
     , d14.source   AS source_2014
     , d15.deflator AS deflator_2015
     , d15.source   AS source_2015
     , d16.deflator AS deflator_2016
     , d16.source   AS source_2016
FROM        (SELECT * FROM deflator WHERE year = int2 '2014') d14
FULL   JOIN deflator(SELECT d15* USINGFROM (country_code,deflator country_name)
FULLWHERE year = JOINint2 deflator'2015') d16d15 USING (country_code, country_name)
WHEREFULL  d14.year =JOIN int2(SELECT '2014'
AND* FROM deflator WHERE d15.year = int2 '2015'
AND'2016') d16 USING (country_code, d16.yearcountry_name)
ORDER = int2BY '2016';country_code;

Using FULL [OUTER] JOIN since we can't assume a row for every combination of (country_code, year), this. This way we get the same result as with the crosstab query above.

CREATE TEMP TABLE defso (def numeric, so varchar);  -- once per session!

SELECT country_code
     , country_name
     , (def2014).def AS deflator_2014  -- note the parentheses!
     , (def2014).so  AS source_2014
     , (def2015).def AS deflator_2015
     , (def2015).so  AS source_2015
     , (def2016).def AS deflator_2016
     , (def2014).so  AS source_2016
FROM   crosstab (
    'SELECT country_code, country_name, year, (deflator, source)::defso
     FROM   deflator
     ORDER  BY 1'
  , 'SELECT generate_series(2014, 2016)::int2'
   ) AS ct (country_code int2
          , country_name text
          , def2014 defso
          , def2015 defso
          , def2016 defso
   );
SELECT country_code, country_name
     , d14.deflator AS deflator_2014
     , d14.source   AS source_2014
     , d15.deflator AS deflator_2015
     , d15.source   AS source_2015
     , d16.deflator AS deflator_2016
     , d16.source   AS source_2016
FROM        deflator d14
FULL   JOIN deflator d15 USING (country_code, country_name)
FULL   JOIN deflator d16 USING (country_code, country_name)
WHERE  d14.year = int2 '2014'
AND    d15.year = int2 '2015'
AND    d16.year = int2 '2016';

Using FULL [OUTER] JOIN since we can't assume a row for every combination of (country_code, year), this way get the same result as with the crosstab query above.

CREATE TEMP TABLE defso (def numeric, so varchar);  -- once per session!

SELECT country_code
     , country_name
     , (d14).def AS deflator_2014  -- note the parentheses!
     , (d14).so  AS source_2014
     , (d15).def AS deflator_2015
     , (d15).so  AS source_2015
     , (d16).def AS deflator_2016
     , (d16).so  AS source_2016
FROM   crosstab (
    'SELECT country_code, country_name, year, (deflator, source)::defso
     FROM   deflator
     ORDER  BY 1'
  , 'SELECT generate_series(2014, 2016)::int2'
   ) AS ct (country_code int2
          , country_name text
          , d14 defso
          , d15 defso
          , d16 defso
   );
SELECT country_code, country_name
     , d14.deflator AS deflator_2014
     , d14.source   AS source_2014
     , d15.deflator AS deflator_2015
     , d15.source   AS source_2015
     , d16.deflator AS deflator_2016
     , d16.source   AS source_2016
FROM        (SELECT * FROM deflator WHERE year = int2 '2014') d14
FULL   JOIN (SELECT * FROM deflator WHERE year = int2 '2015') d15 USING (country_code, country_name)
FULL   JOIN (SELECT * FROM deflator WHERE year = int2 '2016') d16 USING (country_code, country_name)
ORDER  BY country_code;

Using FULL [OUTER] JOIN since we can't assume a row for every combination of (country_code, year). This way we get the same result as with the crosstab query above.

add simple alternative
Source Link
Erwin Brandstetter
  • 182.1k
  • 28
  • 457
  • 620
CREATE TEMP TABLE defso (def int2numeric, so varchar);  -- once per session!

SELECT country_code
     , country_name
     , (def2014).def AS deflator_2014  -- note the parentheses!
     , (def2014).so  AS source_2014
     , (def2015).def AS deflator_2015
     , (def2015).so  AS source_2015
     , (def2016).def AS deflator_2016
     , (def2014).so  AS source_2016
FROM   crosstab (
    'SELECT country_code, country_name, year, (deflator, source)::defso
     FROM   deflator
     ORDER  BY 1'
  , 'SELECT generate_series(2014, 2016)::int2'
   ) AS ct (country_code int2
          , country_name text
          , def2014 defso
          , def2015 defso
          , def2016 defso
   );

I also removed the unnecessary CTE and simplified a bit.


While dealing with only a hand full of years, you can do without crosstab() and use self-joins:

SELECT country_code, country_name
     , d14.deflator AS deflator_2014
     , d14.source   AS source_2014
     , d15.deflator AS deflator_2015
     , d15.source   AS source_2015
     , d16.deflator AS deflator_2016
     , d16.source   AS source_2016
FROM        deflator d14
FULL   JOIN deflator d15 USING (country_code, country_name)
FULL   JOIN deflator d16 USING (country_code, country_name)
WHERE  d14.year = int2 '2014'
AND    d15.year = int2 '2015'
AND    d16.year = int2 '2016';

Using FULL [OUTER] JOIN since we can't assume a row for every combination of (country_code, year), this way get the same result as with the crosstab query above.

Including country_name in the join condition seems redundant, but if we don't, we have to use COALESCE(d14.country_name, d15.country_name, d16.country_name) AS country_name to defend against missing rows. This functionally dependent value shouldn't be in the table to begin with. Should be in a country table in a properly normalized schema.

CREATE TEMP TABLE defso (def int2, so varchar);  -- once per session!

SELECT country_code
     , country_name
     , (def2014).def AS deflator_2014  -- note the parentheses!
     , (def2014).so  AS source_2014
     , (def2015).def AS deflator_2015
     , (def2015).so  AS source_2015
     , (def2016).def AS deflator_2016
     , (def2014).so  AS source_2016
FROM   crosstab (
    'SELECT country_code, country_name, year, (deflator, source)::defso
     FROM   deflator
     ORDER  BY 1'
  , 'SELECT generate_series(2014, 2016)::int2'
   ) AS ct (country_code int2
          , country_name text
          , def2014 defso
          , def2015 defso
          , def2016 defso
   );

I also removed the unnecessary CTE and simplified a bit.

CREATE TEMP TABLE defso (def numeric, so varchar);  -- once per session!

SELECT country_code
     , country_name
     , (def2014).def AS deflator_2014  -- note the parentheses!
     , (def2014).so  AS source_2014
     , (def2015).def AS deflator_2015
     , (def2015).so  AS source_2015
     , (def2016).def AS deflator_2016
     , (def2014).so  AS source_2016
FROM   crosstab (
    'SELECT country_code, country_name, year, (deflator, source)::defso
     FROM   deflator
     ORDER  BY 1'
  , 'SELECT generate_series(2014, 2016)::int2'
   ) AS ct (country_code int2
          , country_name text
          , def2014 defso
          , def2015 defso
          , def2016 defso
   );

I also removed the unnecessary CTE and simplified a bit.


While dealing with only a hand full of years, you can do without crosstab() and use self-joins:

SELECT country_code, country_name
     , d14.deflator AS deflator_2014
     , d14.source   AS source_2014
     , d15.deflator AS deflator_2015
     , d15.source   AS source_2015
     , d16.deflator AS deflator_2016
     , d16.source   AS source_2016
FROM        deflator d14
FULL   JOIN deflator d15 USING (country_code, country_name)
FULL   JOIN deflator d16 USING (country_code, country_name)
WHERE  d14.year = int2 '2014'
AND    d15.year = int2 '2015'
AND    d16.year = int2 '2016';

Using FULL [OUTER] JOIN since we can't assume a row for every combination of (country_code, year), this way get the same result as with the crosstab query above.

Including country_name in the join condition seems redundant, but if we don't, we have to use COALESCE(d14.country_name, d15.country_name, d16.country_name) AS country_name to defend against missing rows. This functionally dependent value shouldn't be in the table to begin with. Should be in a country table in a properly normalized schema.

Source Link
Erwin Brandstetter
  • 182.1k
  • 28
  • 457
  • 620
Loading