How to write a crosstab which returns empty row results

Started by David Goldsmithover 3 years ago6 messagesgeneral
Jump to latest
#1David Goldsmith
d.l.goldsmith@gmail.com

Hi. New subscriber and intermediate level SQL writer here, still pretty new
to Postgresql (but I don't know how to do the following in TSQL either).

I've figured out how to write a crosstab query I need; the problem is that
the number of row results should be 72, but I'm only getting 41. I'm
pretty sure this is because there actually isn't any data matching my where
constraints for the 31 missing row header values, but I nevertheless need
them in my result, with NULLs where there is no data. How do I force
"empty rows" to be included in my query output? (I've tried LEFT JOINing to
the row header results, and using CASE statements; but due to my
unfamiliarity w/ using crosstab, I'm not sure if I've used those correctly
in the current context; so if using either or both of those is part of the
solution, please do more than simply saying "use a Left join" or "use a
case statement," i.e., furnish an example, please.

Thanks!

#2Brad White
b55white@gmail.com
In reply to: David Goldsmith (#1)
Re: How to write a crosstab which returns empty row results

On 12/24/2022 9:03 PM, David Goldsmith wrote:

How do I force "empty rows" to be included in my query output? (I've
tried LEFT JOINing to the row header results, and using CASE
statements; but due to my unfamiliarity w/ using crosstab, I'm not
sure if I've used those correctly in the current context;

Can you give us a head start by showing the query you have now that is
not working.

Extra points if you give simple create/populate statements that
demonstrate the problem.

Hope that helps,
Brad.

#3David Goldsmith
d.l.goldsmith@gmail.com
In reply to: Brad White (#2)
Re: How to write a crosstab which returns empty row results

Here you go:

SELECT *

FROM crosstab(

'SELECT s.s_n AS Pop

, ad.a_d_y::text AS Yr

, ad.s_a_qty::text --for some Pop all of
these are null for every Yr

FROM st AS s

JOIN s_d_s AS sds ON s.s_id = sds.s_id

JOIN a_d_d AS ad ON sds.sds_id = ad.sds_id

JOIN d_t_l AS dtl ON dtl.dtl_id = sds.dtl_id

WHERE dtl.dtl_id =
''3edcb910-fc0c-49e0-be93-a93e98cb12bb''

AND s.s_id IN (

''9adfe0ee-af21-4ec7-a466-c89bbfa0f750''

,
''8714b2e3-d7ba-4494-a3ed-99c6d3aa2a9c''

,
''45ecb932-ece9-43ce-8095-54181f33419e''

,
''fa934121-67ed-4d10-84b0-c8f36a52544b''

,
''b7d5e226-e036-43c2-bd27-d9ae06a87541''

)

ORDER BY 1,2',

'SELECT DISTINCT a_d_y FROM a_d_d WHERE a_d_y BETWEEN 2017
AND 2021 ORDER BY 1')

AS final_result(Pop TEXT,

"2017" TEXT,

"2018" TEXT,

"2019" TEXT,

"2020" TEXT,

"2021" TEXT

);

A row for each one of the matching s.s_id values should be displayed, even
if all the ad.s_a_qty values for that Yr are NULL; right now, the query
works, but it only returns matching rows for which at least one year has a
non-NULL ad.s_a_qty.

Thanks in advance for your help.

On Sat, Dec 24, 2022 at 7:25 PM Brad White <b55white@gmail.com> wrote:

Show quoted text

On 12/24/2022 9:03 PM, David Goldsmith wrote:

How do I force "empty rows" to be included in my query output? (I've
tried LEFT JOINing to the row header results, and using CASE
statements; but due to my unfamiliarity w/ using crosstab, I'm not
sure if I've used those correctly in the current context;

Can you give us a head start by showing the query you have now that is
not working.

Extra points if you give simple create/populate statements that
demonstrate the problem.

Hope that helps,
Brad.

#4David G. Johnston
david.g.johnston@gmail.com
In reply to: David Goldsmith (#3)
Re: How to write a crosstab which returns empty row results

On Sun, Dec 25, 2022 at 12:25 AM David Goldsmith <d.l.goldsmith@gmail.com>
wrote:

(

''9adfe0ee-af21-4ec7-a466-c89bbfa0f750''

,
''8714b2e3-d7ba-4494-a3ed-99c6d3aa2a9c''

,
''45ecb932-ece9-43ce-8095-54181f33419e''

,
''fa934121-67ed-4d10-84b0-c8f36a52544b''

,
''b7d5e226-e036-43c2-bd27-d9ae06a87541''

)

This is basically your issue - specifying the items you want as individual
items in an IN construct instead of making them into a set (in this case an
array so the set is compactified into a single value):

Something like:

WITH sids (sid_array) AS (
SELECT ARRAY[
'.........',
'........'.
etc...
]
)
SELECT usids.sid, ct.*
FROM (SELECT unnest(sid_array) AS sid FROM sids) AS usids (sid)
LEFT JOIN crosstab( format($$SELECT ... s.s_id = ANY(%L) ...$$, (SELECT
sids.sid_array::text FROM sids) ) AS ct ( pop text, YYYY text, etc... ) ON
usids.sid = ct.pop

David J.

#5David Goldsmith
d.l.goldsmith@gmail.com
In reply to: David G. Johnston (#4)
Re: How to write a crosstab which returns empty row results

Thanks, David. There's a lot of stuff i'm not familiar with in there: I
don't suppose you know of a tutorial they covers this?

On Sun, Dec 25, 2022 at 8:46 AM David G. Johnston <
david.g.johnston@gmail.com> wrote:

Show quoted text

On Sun, Dec 25, 2022 at 12:25 AM David Goldsmith <d.l.goldsmith@gmail.com>
wrote:

(

''9adfe0ee-af21-4ec7-a466-c89bbfa0f750''

,
''8714b2e3-d7ba-4494-a3ed-99c6d3aa2a9c''

,
''45ecb932-ece9-43ce-8095-54181f33419e''

,
''fa934121-67ed-4d10-84b0-c8f36a52544b''

,
''b7d5e226-e036-43c2-bd27-d9ae06a87541''

)

This is basically your issue - specifying the items you want as individual
items in an IN construct instead of making them into a set (in this case an
array so the set is compactified into a single value):

Something like:

WITH sids (sid_array) AS (
SELECT ARRAY[
'.........',
'........'.
etc...
]
)
SELECT usids.sid, ct.*
FROM (SELECT unnest(sid_array) AS sid FROM sids) AS usids (sid)
LEFT JOIN crosstab( format($$SELECT ... s.s_id = ANY(%L) ...$$, (SELECT
sids.sid_array::text FROM sids) ) AS ct ( pop text, YYYY text, etc... ) ON
usids.sid = ct.pop

David J.

#6David Goldsmith
d.l.goldsmith@gmail.com
In reply to: David Goldsmith (#5)
Re: How to write a crosstab which returns empty row results

*That* covers this.

On Sun, Dec 25, 2022 at 8:56 AM David Goldsmith <d.l.goldsmith@gmail.com>
wrote:

Show quoted text

Thanks, David. There's a lot of stuff i'm not familiar with in there: I
don't suppose you know of a tutorial they covers this?

On Sun, Dec 25, 2022 at 8:46 AM David G. Johnston <
david.g.johnston@gmail.com> wrote:

On Sun, Dec 25, 2022 at 12:25 AM David Goldsmith <d.l.goldsmith@gmail.com>
wrote:

(

''9adfe0ee-af21-4ec7-a466-c89bbfa0f750''

,
''8714b2e3-d7ba-4494-a3ed-99c6d3aa2a9c''

,
''45ecb932-ece9-43ce-8095-54181f33419e''

,
''fa934121-67ed-4d10-84b0-c8f36a52544b''

,
''b7d5e226-e036-43c2-bd27-d9ae06a87541''

)

This is basically your issue - specifying the items you want as
individual items in an IN construct instead of making them into a set (in
this case an array so the set is compactified into a single value):

Something like:

WITH sids (sid_array) AS (
SELECT ARRAY[
'.........',
'........'.
etc...
]
)
SELECT usids.sid, ct.*
FROM (SELECT unnest(sid_array) AS sid FROM sids) AS usids (sid)
LEFT JOIN crosstab( format($$SELECT ... s.s_id = ANY(%L) ...$$, (SELECT
sids.sid_array::text FROM sids) ) AS ct ( pop text, YYYY text, etc... ) ON
usids.sid = ct.pop

David J.