1 of these 2 request apparently equivalent is not working - sql

I try to understand how PIVOT table works
These 2 requests with pivot table seem equivalent:
I only write
tablename.column1, ...........column2 instead of tablename.*
You can find the requests here:
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=a5c3aacdaebe599bb050295caf3512b6
with
a as
(
select
a1.column_value a, a2.column_value b , cos(a1.r) c
from
(select column_value, rownum r from table(sys.odcinumberlist(1,2,3,4,5))) a1 ,
(select column_value, rownum r from table(sys.odcivarchar2list('a','b','a','b','a'))) a2
where
a1.r = a2.r)
select a.a,a.b,a.c from a --a.a,a.b
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
ORA-00904: "A"."C": invalid identifier
with
a as
(
select
a1.column_value a, a2.column_value b , cos(a1.r) c
from
(select column_value, rownum r from table(sys.odcinumberlist(1,2,3,4,5))) a1 ,
(select column_value, rownum r from table(sys.odcivarchar2list('a','b','a','b','a'))) a2
where
a1.r = a2.r)
select * from a
PIVOT
(
COUNT(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
intended result

When you do a PIVOT, Oracle will name the resulting columns just like their original values.
You can see this behavior when you do your select * that is working :
with
a as
(
select
a1.column_value a, a2.column_value b , cos(a1.r) c
from
(select column_value, rownum r from table(sys.odcinumberlist(1,2,3,4,5))) a1 ,
(select column_value, rownum r from table(sys.odcivarchar2list('a','b','a','b','a'))) a2
where
a1.r = a2.r)
select * from a
PIVOT
(
COUNT(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
result is
C 'a' 'b'
-.65364362086361191463916818309775038145 0 1
.5403023058681397174009366074429766037354 1 0
-.98999249660044545727157279473126130238 1 0
.2836621854632262644666391715135573083265 1 0
-.41614683654714238699756822950076218977 0 1
Your columns headings have been turned by Oracle into the exact values you've got in the IN clause, including the surrounding quotes.
So to refer them in your SELECT clause, you should use double quotes like this:
select "'a'","'b'", c from a
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
An alternative is to alias your values directly in the IN clause
select val_a, val_b, c from a --a.a,a.b
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a' val_a, 'b' val_b )
)
VAL_A VAL_B C
0 1 -.65364362086361191463916818309775038145
1 0 .5403023058681397174009366074429766037354
1 0 -.98999249660044545727157279473126130238
1 0 .2836621854632262644666391715135573083265
0 1 -.41614683654714238699756822950076218977
And finally, you had another mistake in your initial approach:
select a.a,a.b,a.c from a --a.a,a.b
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
)
in this query a refers to your initial a CTE. When you do a.a,a.b,a.c, Oracle doesn't know what you are referencing because of the PIVOT that comes afterwards.
You should properly alias the PIVOT results if you want to refer to it in the SELECT clause :
select pa."'a'",pa."'b'", pa.c from a
PIVOT
(
count(a.a)--,sum(a.c)
FOR b IN ('a', 'b')
) pa

Related

How to unnest and pivot two columns in BigQuery

Say I have a BQ table containing the following information
id
test.name
test.score
1
a
5
b
7
2
a
8
c
3
Where test is nested. How would I pivot test into the following table?
id
a
b
c
1
5
7
2
8
3
I cannot pivot test directly, as I get the following error message at pivot(test): Table-valued function not found. Previous questions (1, 2) don't deal with nested columns or are outdated.
The following query looks like a useful first step:
select a.id, t
from `table` as a,
unnest(test) as t
However, this just provides me with:
id
test.name
test.score
1
a
5
1
b
7
2
a
8
2
c
3
Conditional aggregation is a good approach. If your tables are large, you might find that this has the best performance:
select t.id,
(select max(tt.score) from unnest(t.score) tt where tt.name = 'a') as a,
(select max(tt.score) from unnest(t.score) tt where tt.name = 'b') as b,
(select max(tt.score) from unnest(t.score) tt where tt.name = 'c') as c
from `table` t;
The reason I recommend this is because it avoids the outer aggregation. The unnest() happens without shuffling the data around -- and I have found that this is a big win in terms of performance.
One option could be using conditional aggregation
select id,
max(case when test.name='a' then test.score end) as a,
max(case when test.name='b' then test.score end) as b,
max(case when test.name='c' then test.score end) as c
from
(
select a.id, t
from `table` as a,
unnest(test) as t
)A group by id
Below is generic/dynamic way to handle your case
EXECUTE IMMEDIATE (
SELECT """
SELECT id, """ ||
STRING_AGG("""MAX(IF(name = '""" || name || """', score, NULL)) AS """ || name, ', ')
|| """
FROM `project.dataset.table` t, t.test
GROUP BY id
"""
FROM (
SELECT DISTINCT name
FROM `project.dataset.table` t, t.test
ORDER BY name
)
);
If to apply to sample data from your question - output is
Row id a b c
1 1 5 7 null
2 2 8 null 3

How to select columns of data in BigQuery that has all NULL values

How to select columns of data in BigQuery that has all NULL values
A B C
NULL 1 NULL
NULL NULL NULL
NULL 2 NULL
NULL 3 NULL
I want to retrieve columns A and C. Please can you help!!
Expanding on my comment on Mikhail's answer, this is what I had in mind. It doesn't require generating a query string, which could be quite long if you have a large number of columns. It compares the count of null values for each column name to the total number of rows in the table to decide if the column should be included in the result.
#standardSQL
WITH `project.dataset.table` AS (
SELECT NULL A, 1 B, NULL C UNION ALL
SELECT NULL, NULL, NULL UNION ALL
SELECT NULL, 2, NULL UNION ALL
SELECT NULL, 3, NULL
)
SELECT null_column
FROM `project.dataset.table` AS t,
UNNEST(REGEXP_EXTRACT_ALL(
TO_JSON_STRING(t),
r'\"([a-zA-Z0-9\_]+)\":null')
) AS null_column
GROUP BY null_column
HAVING COUNT(*) = (SELECT COUNT(*) FROM `project.dataset.table`);
Below is for BigQuery StandardSQL
Simple option:
#standardSQL
WITH `project.dataset.table` AS (
SELECT NULL A, 1 B, NULL C UNION ALL
SELECT NULL, NULL, NULL UNION ALL
SELECT NULL, 2, NULL UNION ALL
SELECT NULL, 3, NULL
)
SELECT COUNT(A) A, COUNT(B) B, COUNT(C) C
FROM `project.dataset.table`
it returns below where 0(zero) indicates that respective column has all NULLs
A B C
0 3 0
If this is "not enough" - below is more "sophisticated" version:
#standardSQL
WITH `project.dataset.table` AS (
SELECT NULL A, 1 B, NULL C UNION ALL
SELECT NULL, NULL, NULL UNION ALL
SELECT NULL, 2, NULL UNION ALL
SELECT NULL, 3, NULL
)
SELECT SPLIT(y, ':')[OFFSET(0)] column
FROM (
SELECT REGEXP_REPLACE(TO_JSON_STRING(t), r'[{}"]', '') x
FROM (
SELECT COUNT(A) A, COUNT(B) B, COUNT(C) C
FROM `project.dataset.table`
) t
), UNNEST(SPLIT(x)) y
WHERE CAST(SPLIT(y, ':')[OFFSET(1)] AS INT64) = 0
it returns result as below - enlisting only columns with all NULLs
column
A
C
Note: for your real table - just remove WITH block and replace project.dataset.table with your real table reference
Also, of course, use real column names
My table has round 700 columns..
Below is an example of how you can easily generate above query for any number of columns.
1. Just run below
2. Copy result - this is a generated query
3. paste generated query into new UI and run it
4. Enjoy (I hope you will) result :o)
Of course, as usually replace project.dataset.table with your real table reference
#standardSQL
SELECT
CONCAT('''
SELECT SPLIT(y, ':')[OFFSET(0)] column
FROM (
SELECT REGEXP_REPLACE(TO_JSON_STRING(t), r'[{}"]', '') x
FROM (
SELECT ''', y,
'''
FROM `project.dataset.table`
) t
), UNNEST(SPLIT(x)) y
WHERE CAST(SPLIT(y, ':')[OFFSET(1)] AS INT64) = 0
'''
)
FROM (
SELECT
STRING_AGG(CONCAT('COUNT(', x, ') ', x), ', ') y
FROM (
SELECT REGEXP_EXTRACT_ALL(REGEXP_REPLACE(TO_JSON_STRING(t), r'[{}]', ''), r'"([\w_]+)":') x
FROM `project.dataset.table` t
LIMIT 1
), UNNEST(x) x
)
Note: please pay attention to query cost - both "generation query" and final query itself will do full scan
You can generate columns list much cheaper off of table schema in any client of your choice
To test / play with it - you can use same dummy data as for initial queries in my answer

SQL query to get the count by applying group by

I want to get the below result:
source table :
Cnt A B
4 ABC YU/FGH
5 ABC YU/DFE
5 ABC KL
2 LKP BN/ER
4 JK RE
Result:
Cnt A B
9 ABC YU
5 ABC KL
2 LKP BN
4 JK RE
Here I want the count by grouping 'B' and want to display the 'B' record only till the special character (/)
Basically, you will have to filter out the all the characters after the "/" symbol and then apply a SUM and a GROUP BY. You can see this below. The inner query filters out the unwanted string and the outer query does the SUM and the GROUP BY :
SELECT SUM(t.Cnt), t.A, t.B
FROM (
SELECT Cnt,
A,
CASE
WHEN CHARINDEX('/', B) > 0 THEN SUBSTRING(B, 0, CHARINDEX('/', B))
ELSE B
END AS B
FROM #Tab
) t
GROUP BY t.A, t.B
ORDER BY t.A
You can see this working here -> http://rextester.com/IQJ79191
Hope this helps!!!
You can get your string till '/' by using SUBSTRING.
select
count(SUBSTRING(reverse(B),0,charindex('/',reverse(B)))),
A,
SUBSTRING(reverse(B),0,charindex('/',reverse(B)))
from source_table group by B;
Solution for Oracle - substr(B,0,instr(B,'/',1)-1) B
Put this both in select and groupby
I can suggest you to use a query like this:
select
sum(Cnt) Cnt,
A,
left(B, charindex('/',B+'/',0)-1) B -- Using `+'\'` will do the trick
from
t
group by
A,
left(B, charindex('/',B+'/',0)-1);
By using String and CharIndex Functions.
;WITH SourceTable(Cnt,A,B) AS
(
SELECT 4,'ABC','YU/FGH'UNION ALL
SELECT 5,'ABC','YU/DFE'UNION ALL
SELECT 5,'ABC','KL' UNION ALL
SELECT 2,'LKP','BN/ER' UNION ALL
SELECT 4,'JK','RE'
)
SELECT SUM(Cnt) AS Cnt,A,CASE WHEN CHARINDEX('/',B) = 0 THEN B
ELSE SUBSTRING(B,0,CHARINDEX('/',B)) END AS [B] FROM SourceTable
GROUP BY A,CASE WHEN CHARINDEX('/',B) = 0 THEN B
ELSE SUBSTRING(B,0,CHARINDEX('/',B)) END
ORDER BY Cnt DESC
Try this query --
SELECT SUM(Cnt) AS [COUNT]
,A
,CASE
WHEN CHARINDEX('/', B) > 0
THEN SUBSTRING(B, 1, (CHARINDEX('/', B) - 1))
ELSE B
END
FROM tblSample
GROUP BY A, B
ORDER BY A, B

Transforming an SQL table using Unpivot

If I have a table with the following structure
ID NAME A1 A2 A3 B1 B2 B3
X----Y------0---1---2---3---4---5 (dashes are just to push values under headers)
How do I transform it to be the following:
ID NAME LETTER 1 2 3
X----Y----------A------0-1-2
X---Y------------B-----3-4-5
SELECT id, name, 'A' letter, a1 1, a2 2, a3 3 from example_table
UNION
SELECT id, name, 'B' letter, b1 1, b2 2, b3 3 from example_table
I'm not sure that 1,2,3 will work as column names; that may be RDBMS-dependant.
While I don't consider this a feasible answer, it may give you something to think about as it produces the end result in sql server.
SELECT
upvt.id
, upvt.name
, left(label,1) AS letter
, CASE
WHEN left(label,1) = 'A' THEN MIN(value)
WHEN left(label,1) = 'B' THEN MAX(value)
END AS [1]
, CASE
WHEN left(label,1) = 'A' THEN MIN(value1)
WHEN left(label,1) = 'B' THEN MAX(value1)
END AS [2]
, CASE
WHEN left(label,1) = 'A' THEN MIN(value2)
WHEN left(label,1) = 'B' THEN MAX(value2)
END AS [3]
FROM example_table
UNPIVOT (
value
FOR label IN (a1,b1)
) upvt
INNER JOIN (
SELECT
id
, name
, left(label1,1) AS letter
, value1
FROM example_table
UNPIVOT (
value1
FOR label1 IN (a2,b2)
) upvt1 ) as b
ON upvt.id = b.id
AND upvt.name = b.name
INNER JOIN (
SELECT
id
, name
, left(label2,1) AS letter
, value2
FROM example_table
UNPIVOT (
value2
FOR label2 IN (a3,b3)
) upvt2 ) c
ON upvt.id = c.id
AND upvt.name = c.name
group by upvt.id, upvt.name, upvt.label
Not exactly what I would call "production ready".

How can I Pivot a table in DB2? [duplicate]

This question already has answers here:
Pivoting in DB2
(3 answers)
Closed 5 years ago.
I have table A, below, where for each unique id, there are three codes with some value.
ID Code Value
---------------------
11 1 x
11 2 y
11 3 z
12 1 p
12 2 q
12 3 r
13 1 l
13 2 m
13 3 n
I have a second table B with format as below:
Id Code1_Val Code2_Val Code3_Val
Here there is just one row for each unique id. I want to populate this second table B from first table A for each id from the first table.
For the first table A above, the second table B should come out as:
Id Code1_Val Code2_Val Code3_Val
---------------------------------------------
11 x y z
12 p q r
13 l m n
How can I achieve this in a single SQL query?
select Id,
max(case when Code = '1' then Value end) as Code1_Val,
max(case when Code = '2' then Value end) as Code2_Val,
max(case when Code = '3' then Value end) as Code3_Val
from TABLEA
group by Id
SELECT Id,
max(DECODE(Code, 1, Value)) AS Code1_Val,
max(DECODE(Code, 2, Value)) AS Code2_Val,
max(DECODE(Code, 3, Value)) AS Code3_Val
FROM A
group by Id
If your version doesn't have DECODE(), you can also use this:
INSERT INTO B (id, code1_val, code2_val, code3_val)
WITH Ids (id) as (SELECT DISTINCT id
FROM A) -- Only to construct list of ids
SELECT Ids.id, a1.value, a2.value, a3.value
FROM Ids -- or substitute the actual id table
JOIN A a1
ON a1.id = ids.id
AND a1.code = 1
JOIN A a2
ON a2.id = ids.id
AND a2.code = 2
JOIN A a3
ON a3.id = ids.id
AND a3.code = 3
(Works on my V6R1 DB2 instance, and have an SQL Fiddle Example).
Here is a SQLFiddle example
insert into B (ID,Code1_Val,Code2_Val,Code3_Val)
select Id, max(V1),max(V2),max(V3) from
(
select ID,Value V1,'' V2,'' V3 from A where Code=1
union all
select ID,'' V1, Value V2,'' V3 from A where Code=2
union all
select ID,'' V1, '' V2,Value V3 from A where Code=3
) AG
group by ID
Here is the SQL Query:
insert into pivot_insert_table(id,code1_val,code2_val, code3_val)
select * from (select id,code,value from pivot_table)
pivot(max(value) for code in (1,2,3)) order by id ;
WITH Ids (id) as
(
SELECT DISTINCT id FROM A
)
SELECT Ids.id,
(select sub.value from A sub where Ids.id=sub.id and sub.code=1 fetch first rows only) Code1_Val,
(select sub.value from A sub where Ids.id=sub.id and sub.code=2 fetch first rows only) Code2_Val,
(select sub.value from A sub where Ids.id=sub.id and sub.code=3 fetch first rows only) Code3_Val
FROM Ids
You want to pivot your data. Since DB2 has no pivot function, yo can use Decode (basically a case statement.)
The syntax should be:
SELECT Id,
DECODE(Code, 1, Value) AS Code1_Val,
DECODE(Code, 2, Value) AS Code2_Val,
DECODE(Code, 3, Value) AS Code3_Val
FROM A