SQL PIVOT Rows Into Matrix With Empty Column Results - sql

I was needing to get my data into a format that works smoothly for kendo grid (example). This example is actually a hack to try to color individual chart bars which isn't available by default. If you format your data correctly you can stack the bars in groups and you are able to color each group.
My data needed to be structured like this with the 1st column names as column headers
names CompletedAllCourses HasExpiredCourses HasNotTakenCourses HasDueCourses
-------------------------------------------------------------------------------------------
CompletedAllCourses 12 NULL NULL NULL
HasDueCourses NULL NULL NULL 4
HasExpiredCourses NULL 8 NULL NULL
HasNotTakenCourses NULL NULL 24 NULL
This is what I had to start with GroupedStats Table
CompletedAllCourses 12
HasDueCourses 4
HasExpiredCourses 8
HasNotTakenCourses 24
I tried the following query from an example I found online.
SELECT * FROM GroupedStats
PIVOT
(
MAX(cnt) FOR cat IN (CompletedAllCourses,
HasExpiredCourses, HasNotTakenCourses, HasDueCourses)
) p
This was the result.
CompletedAllCourses HasExpiredCourses HasNotTakenCourses HasDueCourses
------------------- ----------------- ------------------ -------------
12 8 24 4
I figured out one way and posted it as the answer.

This will give you the required result but it requires hardcoded literals in CASE:
SELECT cat,
CASE cat WHEN 'CompletedAllCourses' THEN CompletedAllCourses ELSE NULL END AS CompletedAllCourses,
CASE cat WHEN 'HasExpiredCourses' THEN HasExpiredCourses ELSE NULL END AS HasExpiredCourses,
CASE cat WHEN 'HasNotTakenCourses' THEN HasNotTakenCourses ELSE NULL END AS HasNotTakenCourses,
CASE cat WHEN 'HasDueCourses' THEN HasDueCourses ELSE NULL END AS HasDueCourses
FROM GroupedStats
JOIN
(
SELECT * FROM GroupedStats
PIVOT
(
MAX(cnt) FOR cat IN (CompletedAllCourses,
HasExpiredCourses, HasNotTakenCourses, HasDueCourses)
) p
) X
ON 1 = 1
SQL Fiddle Demo

PIVOT Example to save the day
I moved this part of this post into the answer because it was ultimately what worked for me.
Need another column to fix things up or things collapse into one result.
select * from
(select cat as names, cnt, cat FROM GroupedStats) x
PIVOT
(
MAX(cnt) FOR cat IN (CompletedAllCourses, HasExpiredCourses,
HasNotTakenCourses, HasDueCourses)
) p
[sql fiddle] (http://sqlfiddle.com/#!6/8b706/3)
And I get the format I wanted PROBLEM SOLVED! Please comment if you can add to the explanation.
names CompletedAllCourses HasExpiredCourses HasNotTakenCourses HasDueCourses
---------------------------------------------------------------------------------------
CompletedAllCourses 12 NULL NULL NULL
HasDueCourses NULL NULL NULL 4
HasExpiredCourses NULL 8 NULL NULL
HasNotTakenCourses NULL NULL 24 NULL

Related

How to sum non-null values from multiple columns (workaround for CASE WHEN limitation) Postgresql

So I essentially want to work around the fact that CASE WHEN stops executing when it finds its first TRUE return.
I'd like to sum every instance of a non-null value between multiple columns, and group these based on my ID. Example table:
id
input1
input2
input3
1
a
null
k
2
null
null
b
3
null
null
null
4
q
null
r
5
x
p
j
6
null
y
q
I would like the output of my function to be:
id
total_inputs
1
2
2
1
3
0
4
2
5
3
6
2
Any work arounds? Is a custom function in order to create a count of unique or non-null entries across multiple columns, grouped by row?
I know I can create a CTE and assign 1's to each non-null column but that seems tedious (my data set has 39 inputs) - and I'd like to have a reusable function I could use again in the future.
You could use a simple aggregation as the following:
Select id,
Count(input1) + Count(input2) + Count(input3) As total_inputs
From table_name
Group By id
Order By id
Noting that Count(inputX) = 0, where inputX is null.
See a demo.
We can simply use:
select ID,
case when input1 is not null then 1 else 0 end
+ case when input2 is not null then 1 else 0 end
+ ...
+ case when input39 is not null then 1 else 0 end as total_inputs
from ...
No need to group by if you want every row (or count, we are not aggregating rows - that is what COUNT()..GROUP BY is for), or CTE.
Also, for some PostgreSQL versions, there is a num_nulls function to count null parameters:
select
, 32-num_nulls(input1, input2, input3, ..., input32)

SQL literal value that is alternative to NULL

Are there other special literal values besides NULL in SQL / PostgresQL?
NULL is nice in that we can interpret NULL as the concept of "nothing" (i.e. missing, not available, not asked, not answered, etc.), and data columns of any type can have NULL values.
I would like another value that I can interpret as representing another concept (here the idea of "everything"), in the same result set.
Is there another special value that I can return in a query, which like NULL doesn't type conflict?
Basically anything that doesn't throw ERROR: For 'UNION', types varchar and numeric are inconsistent in this toy query:
select 1 as numeral, 'one' as name UNION ALL
select 2 as numeral, 'two' as name UNION ALL
select NULL as numeral, NULL as name UNION ALL
select -999 as numeral, -999 as name UNION ALL -- type conflict
select '?' as numeral, 'x' as name -- type conflict
Here,
-999 doesn't work as its type conflicts with varchar columns
'~' doesn't work as its type conflicts with numeric columns
NULL doesn't work as it needs
More specifically here's my actual case, counting combinations of values and also include "Overall" rows in the same query. Generally I won't know or control the types of columns A, B, C in advance. And A, B, or C might also have NULL values which I would would still want to count separately.
SELECT A, COUNT(*) FROM table GROUP BY 1
UNION ALL
SELECT ?, COUNT(*) FROM table GROUP BY 1
and get a result set like:
A
COUNT
NULL
2
1
3
2
5
3
10
(all)
20
SELECT B, COUNT(*) FROM table GROUP BY 1
UNION ALL
SELECT ?, COUNT(*) FROM table GROUP BY 1
and get a result set like:
B
COUNT
NULL
2
'Circle'
3
'Line'
5
'Triangle'
10
(all)
20
You can use function CAST to convert the format to VARCHAR to be considered as string.
NOTE: Thanks to the comments above, I should completely rephrase this question as "How to COUNT/GROUP BY with ROLLUP using multiple columns of mixed/arbitrary/unknown types, and differentiate true NULL values from ROLLUP placeholders?"
The correct answer I believe is provided by #a_horse_with_no_name: use ROLLUP with GROUPING.
Below is is just me drafting that more completely with a revised example:
This toy example has an integer and a string
WITH table AS (
select 1 as numeral, 'one' as name UNION ALL
select 2 as numeral, 'two' as name UNION ALL
select 2 as numeral, 'two' as name UNION ALL
select NULL as numeral, NULL as name UNION ALL
select NULL as numeral, NULL as name UNION ALL
select NULL as numeral, NULL as name
)
select name, numeral, COUNT(*), GROUPING_ID()
FROM table
GROUP BY ROLLUP(1,2)
ORDER BY GROUPING_ID, name, numeral ;
It returns the following result:
numeral
name
count
grouping_id
note
NULL
NULL
3
0
both are true NULLs as grouping is 0
1
one
1
0
2
two
2
0
NULL
NULL
3
1
first is a true NULL, second is a ROLLUP
1
NULL
1
1
2
NULL
2
1
NULL
NULL
6
3
both NULLs are ROLLUPs

How to update a value in column based on value from other table in SQL server?

I have two tables match_score and match_condition
I want to update table match_condition and set values of innings, first_4, etc.
Here's the example in which I am trying to update table match_condition with innings value from match_score whenever both of them is not matched
UPDATE a
SET a.innings = b.innings
FROM match_condition a
INNER JOIN (SELECT TOP 1 * FROM match_score order by time_stamp desc ) b
ON a.matchid = b.matchid
WHERE a.innings != b.innings
match_score table
matchid time_stamp overs ball run team_score wicket innings
50821 2021-04-22 10:52:58.640 20 6 1 137 0 1
50821 2021-04-22 10:52:58.637 20 5 1 136 0 1
50821 2021-04-22 10:52:58.633 20 4 0 135 1 1
match_condition table
matchid seriesid match_time innings powerplay_over first_50 first_100 first_150 first_200 first_4 first_6 first_wicket
50820 2780 2021-04-19 18:44:12.360 NULL NULL NULL NULL NULL NULL NULL NULL NULL
50821 2780 2021-04-20 10:00:06.827 NULL NULL NULL NULL NULL NULL NULL NULL NULL
I made changes according to answers here , but still its showing 0 row affected
If value innings in match_condition will never be -1 this query will finish your job:
UPDATE
match_condition
SET
match_condition.innings = a.innings
FROM (
SELECT TOP 1 innings, matchid FROM match_score ORDER BY time_stamp DESC) a
WHERE
match_condition.matchid = a.matchid
AND
ISNULL(match_condition.innings, -1) <> a.innings
If you think that value can be somehow -1, just change in ISNULL operator value to sam 'unreal' number.
Here is DB FIDDLE demo
You can do it by using a subquery
UPDATE match_condition
SET match_condition.innings =
(
SELECT
CASE
WHEN match_condition.innings != b.innings THEN match_condition.innings
ELSE b.innings
END
FROM match_score b
WHERE match_condition.matchid = b.matchid
)
The issue with your query would appear to be that != returns not-true if either value is NULL. It is not appropriate.
You could fix your query as written. However, that would only work for one match in the second table. I would instead suggest apply:
UPDATE mc
SET mc.innings = ms.innings
FROM match_condition mc CROSS APPLY
(SELECT TOP 1 ms.*
FROM match_score ms
WHERE ms.matchid = mc.matchid
ORDER BY time_stamp DESC
) ms;
If you wanted to check on the innings value, you could use:
WHERE mc.innings IS NULL
Also note the use of meaningful table aliases. Do not use arbitrary letters for represent tables. Use table abbreviations!

How to make Second Field Null if First Field has a Value in Oracle SQL

I have two calculated Columns in ORACLE (Ex3 and EX12). So wherever BOTH fields have a value (ex: ID 118189AC) id like to keep the value in EX3 but turn the EX12 into a NULL. Any suggestions?
SELECT *
FROM
(SELECT ID,
CASE
WHEN PORT IN ('CAP')
THEN 3
ELSE NULL
END AS EX3,
CASE
WHEN PORT NOT IN ('TEST1', 'TEST2', 'TEST3')
THEN 12
ELSE NULL
END AS EX12
FROM LAN.DETAILS)
WHERE (EX3 IS NOT NULL OR EX12 IS NOT NULL)
ORDER BY ID ASC
ID EX3 EX12
118051PU 12
118052PU 12
118189AC 3 12
118397PU 12
118451AC 3 12
119343AC 3 12
119387PU 12
119484PU 12
119772PU 12
119997PU 12
120320AC 3 12
I think you just want a case expression:
select id, ex3,
(case when ex3 is not null and ex12 is not null then null else ex12 end) as ex12
. . .
NVL2(a, b, c) returns b if a is not null, c otherwise.
In your case: NVL2(ex3, null, ex12) will return null when ex3 is not null. Otherwise it will return ex12.
https://docs.oracle.com/cd/B12037_01/server.101/b10759/functions096.htm
I ended up creating a subquery in the WHERE clause that resricted the ID's that were being returned for EX3. Thanks Everyone

Can I issue a 'select exists' that checks for ALL specified fields?

Say that I have a database:
TITLE | RUNTIME | EPISODES
-------------------------------------------
The X-Files 42 202
Fringe NULL 100
Seinfeld 21 NULL
I want to issue a statement like SELECT EXISTS(SELECT title,runtime,episodes FROM shows); that will return 1 if all three of those fields are present (as for The X-Files) but 0 if any of them are empty/null (as with Fringe and Seinfeld).
Is this possible using SQL alone?
I would suggest just doing:
select t.*,
(case when title is not null and runtime is not null and episodes is not null
then 1 else 0 end) as HasAllThree
from table t;
The EXISTS function checks if rows exist, not columns. You can add a WHERE clause to meet your business objectives with the EXISTS and a CASE.
SELECT
CASE WHEN EXISTS(
SELECT * FROM shows
WHERE title IS NOT NULL
AND runtime IS NOT NULL
AND episodes IS NOT NULL
) THEN 1 ELSE 0 END