How to create a pivot table in PostgreSQL - sql

I am looking to essentially create a pivot view using PostgreSQL, such that the table below:
Column A
Column B
Happy
Sad
Sad
Happy
Happy
Sad
becomes
Count
Column A
Column B
Happy
2
1
Sad
1
2
I've been able to use case/when operators far enough such that I can see the counts under independent columns,
SELECT
COUNT(CASE WHEN column1 = 'Happy' THEN 1 END) AS column1_Happy_count,
COUNT(CASE WHEN column1 = 'Sad' THEN 1 END) AS column1_Sad_count,
COUNT(CASE WHEN column2 = 'Happy' THEN 1 END) AS column2_Happy_count,
COUNT(CASE WHEN column2 = 'Sad' THEN 1 END) AS column2_Sad_count,
COUNT(CASE WHEN column3 = 'Happy' THEN 1 END) AS column3_Happy_count,
COUNT(CASE WHEN column3 = 'Sad' THEN 1 END) AS column3_Sad_count
FROM your_table;
but am missing the step to essentially each the pair of columns vertically.
I'm unable to use extensions such as tablefunc and crosstab.

Try this:
CREATE TABLE my_table (
column_a varchar(10),
column_b varchar(10)
);
INSERT INTO my_table (column_a, column_b)
VALUES ('Happy', 'Sad'),
('Sad', 'Happy'),
('Happy', 'Sad'),
('Good', 'Bad');
WITH DataSource (col, val) AS
(
SELECT 'a', column_a
FROM my_table
UNION ALL
SELECT 'b', column_b
FROM my_table
)
SELECT uniq.val AS "Count"
,MAX(case when counts.col = 'a' then counts end) AS "Column A"
,MAX(case when counts.col = 'b' then counts end) AS "Column B"
FROM
(
SELECT DISTINCT val
FROM DataSource
) uniq
INNER JOIN
(
SELECT col
,val
,COUNT(*) counts
FROM DataSource
GROUP BY col
,val
) counts
ON uniq.val = counts.val
GROUP BY uniq.val
will give you this:

You may aggregate for ColumnA, aggregate for ColumnB then do a full join as the following:
select coalesce(A.ColumnA ,B.ColumnB) as "Count",
A.cnt as "Column A",
B.cnt as "Column B"
from
(
select ColumnA, count(*) cnt
from tbl_name
group by ColumnA
) A
full join
(
select ColumnB, count(*) cnt
from tbl_name
group by ColumnB
) B
on A.ColumnA = B.ColumnB
If the distinct values in ColumnA are the same as the distinct values of ColumnB then you can use join instead of the full join.
See demo.

Related

How to find the common values for a column which is present in many tables

I have a lot of tables in oracle DB. All tables have one common column named as 'COLUMN_FILTER' .All tables have lots of unique values for 'COLUMN_FILTER'. Is there any way to find the common records for 'COLUMN_FILTER' , present in all tables ? For sample please refer to the below scenario, where I have provided unique values for 'COLUMN_FILTER'.
Table A: 'X','Y','Z'
Table B: 'W','X'
Table C: 'Z'
Table D: 'Y','Z'
I am expecting the output to be 'Z','W' (any possible minimum set). So that I can put this filter on all tables.
Use HAVING COUNT(*)>1 clause along with GROUPing BY that column
SELECT column_filter
FROM
(
SELECT column_filter FROM tableA
UNION ALL
SELECT column_filter FROM tableB
UNION ALL
SELECT column_filter FROM tableC
UNION ALL
SELECT column_filter FROM tableD
)
GROUP BY column_filter
HAVING COUNT(*)>1
To find out the most common element you could have a query as follows
The highest value of cnt_vals indicate the tables which has the most common values of common_filter across all tables
select table_name
,column_filter
,count(column_filter) over(partition by 1) as cnt_vals
from (
select distinct column_filter,'A' as table_name
from tablea
union all
select distinct column_filter,'B' as table_name
from tableb
union all
select distinct column_filter,'C' as table_name
from tablec
union all
select distinct column_filter,'D' as table_name
from tabled
)x
order by 3 desc
You can find pairs using a join:
with f as (
select distinct column_filter, 'A' as table_name
from tablea
union all
select distinct column_filter, 'B' as table_name
from tableb
union all
select distinct column_filter, 'C' as table_name
from tablec
union all
select distinct column_filter, 'D' as table_name
from tabled
)
select f1.column_filter, f2.column_filter
from f f1 join
f f2
on f1.column_filter < f2.column_filter
group by f1.column_filter, f2.column_filter
having sum(case when 'A' in (f1.table_name, f2.table_name) then 1 else 0 end) > 0 and
sum(case when 'B' in (f1.table_name, f2.table_name) then 1 else 0 end) > 0 and
sum(case when 'C' in (f1.table_name, f2.table_name) then 1 else 0 end) > 0 and
sum(case when 'D' in (f1.table_name, f2.table_name) then 1 else 0 end) > 0;
You can easily extend this for triples:
with f as (
select distinct column_filter, 'A' as table_name
from tablea
union all
select distinct column_filter, 'B' as table_name
from tableb
union all
select distinct column_filter, 'C' as table_name
from tablec
union all
select distinct column_filter, 'D' as table_name
from tabled
)
select f1.column_filter, f2.column_filter
from f f1 join
f f2
on f1.column_filter < f2.column_filter join
f f3
on f3.column_filter < f2.column_filter
group by f1.column_filter, f2.column_filter, fd.column_filter
having sum(case when 'A' in (f1.table_name, f2.table_name, f3.table_name) then 1 else 0 end) > 0 and
sum(case when 'B' in (f1.table_name, f2.table_name, f3.table_name) then 1 else 0 end) > 0 and
sum(case when 'C' in (f1.table_name, f2.table_name, f3.table_name) then 1 else 0 end) > 0 and
sum(case when 'D' in (f1.table_name, f2.table_name, f3.table_name) then 1 else 0 end) > 0;
It is possible to express this as a recursive query, if you don't want to try more and more combinations. In Oracle 12C+, I would recommend a recursive CTE. In earlier versions, you have to adapt using connect by.

In oracle SQL , how to count the no of records based on conditions

I have table with below structure :
Col2
A
A
B
B
E
E
I wanted the SQL query to output me the following :
Internal 4
External 2
Total 6
Logic : If the values in the Col2 are A,B then it should be summed up as Internal , If E then it should be summed up as External.
To map your column values use DECODE, simple providing the list of the original and new values for the column.
select decode(col2,'A','Internal','B','Internal','E','External') col from tab
To calculate the total you do not need to rescan the whole table (performance drops to the half) but use group by rollup that calculates the Total
with t as (
select decode(col2,'A','Internal','B','Internal','E','External') col from tab)
select nvl(col,'Total') col, count(*) cnt
from t
group by rollup (col)
Result
COL CNT
-------- ----------
External 2
Internal 4
Total 6
select sum(case when col2 in ('A', 'B') then 1 else 0 end) as internal,
sum(case when col2 = 'E' then 1 else 0 end) as external,
count(col2) as total
from your_table
select 'Internal' "summed up as"
,sum(case when Col2 in ('A', 'B') then 1
else 0
end) "sum"
from test
union
select 'External' "summed up as"
,sum(case when Col2 = 'E' then 1
else 0
end) "sum"
from test
union
select 'Total' "summed up as"
, count(Col2) "sum"
from test;
Here is a DEMO
try like below using union all and make customize group
select case when col2 in ('A','B') then 'Internal' else 'External' end,
count(*) as result
from table_name
group by case when col2 in ('A','B') then 'Internal' else 'External' end
union all
select 'total', count(*) from table_name
select sum(Col2Count) as Internal from (SELECT Col2 as Col2, count( Col2 ) as Col2Count
FROM tablename group by Col2) where Col2 in (A,B);
This will give you result as :
Internal
4

Grouping by one column and counting from multiple columns

I am trying to write a query on the below scenario
Result
Example:
ColumnA ColumnB
A B
B C
Result:
ColumnA countB countC
A 1 0
B 0 1
From your sample data, no aggregation is needed:
SELECT columnA,
(CASE WHEN columnB = 'B' THEN 1 ELSE 0 END) as countB,
(CASE WHEN columnB = 'C' THEN 1 ELSE 0 END) as countC
FROM t;
It should work:
Select
columnA,
count(ColumnB) as ColumnB,
count(ColumnC) as ColumnC
From Table
Group By
columnA
There is not enough sample data to confirm that, but based also on the title of your question you seem to be looking for condition aggregation :
SELECT
columnA,
COUNT(CASE WHEN columnB = 'B' THEN 1 END) countB,
COUNT(CASE WHEN columnB = 'C' THEN 1 END) countC
FROM mytable
GROUP BY columnA
Assuming that your real scenario is larger that that, I think that is better to use a pivot
DECLARE #table TABLE(ColumnA CHAR(1), ColumnB CHAR(1))
INSERT #table VALUES ('A', 'B')
INSERT #table VALUES ('B', 'C')
SELECT ColumnA, A AS CountA, B AS CountB, C AS CountC, D AS CountD....
FROM
(
SELECT ColumnA, ColumnB
FROM #table
) AS Source
PIVOT
(
Count(ColumnB)
FOR ColumnB IN ([A], [B], [C], [D].....)
) AS Pvt

Multiples Conditions Sum SQL

I am aiming to produce code that generates an independent total check. As per the below table:
column1 Column2 Column3 value independent total check
A B C 10 Null
A B E 11 Null
A B total 21 21
x y z 10 Null
x y p 20 Null
x y total 30 30
I am trying to employ a conditional sum, but with no success! What I have at the moment is:
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL DROP Table #Temp
select t2.Column1, t2.Column2, t2.Column3,t2.value, independanttotal =
case
when t2.Column1 ='A' and t2.Column2= 'B'and t2.T_subdesk = 'Total' then sum(t2.value)
when t2.Column1 ='x' and t2.Column2= 'y'and t2.T_subdesk = 'Total' then sum(t2.value)
end
into #Temp
from #Temp_A t2
group by t2.Column1,t2.Column2,t2.Column3,t2.value
but this is clearly incorrect, although it produces the correct result actually I am just reproducing the total value. Do I need some kind of nested sum? Do I need to separate out this into different table? this is really frustrating me
thanks for your help in advance!
You seem to want:
select t.*,
sum(case when column3 <> 'total' then value end) over (partition by column1, column2) as independent_total
from #temp t;
This puts the computation on all rows. I don't think that is a problem, but you can use a case expression outside the sum() if that is really an issue.
If you only want this on the "total" row, you can do:
select t.*,
(case when column3 = 'total'
then sum(case when column3 <> 'total' then value end) over (partition by column1, column2)
end) as independent_total
from #temp t;
Or, you slightly simplify the logic:
select t.*,
(case when column3 = 'total'
then sum(value) over (partition by column1, column2) - value
end) as independent_total
from #temp t;
This gets the total sum for the two columns and then subtracts the value on the 'total' row.

SQL query to select rows using max aggregate and putting them together

I have a table in which the first column is the key value. I have to select one row for each of the key values (select the one with maximum "count" field ) and then arrange them adjacent to each other. I wrote this query for this purpose:
SELECT name as "name",
MAX(CASE WHEN key_id=8 THEN count ELSE 0.0 END) AS "key_1",
key_value as "key_1_value",
MAX(CASE WHEN key_id=9 THEN count ELSE 0.0 END) AS "key_2",
key_value as "key_2_value",
MAX(CASE WHEN key_id=10 THEN count ELSE 0.0 END) AS "key_3",
key_value as "key_2_value"
FROM table1 GROUP BY name;
The result I get is :
name1 281000018371 0.881841 247000421624 0.881841 285000032094 0.881841
The values for the count is correct i.e. the maximum value of count for that particular key_id but the key_value is correct only for the first key_id which is repeated for the other two.
Can someone please tell me how to change this query so that I get the key_value from the row with corresponding key_id and max(count).
Is this what you're trying to do?
SELECT
name as "name",
MAX(CASE WHEN key_id=8 THEN count ELSE 0.0 END) AS "key_1",
MAX(CASE WHEN key_id=8 THEN key_value END) AS "key_1_value",
MAX(CASE WHEN key_id=9 THEN count ELSE 0.0 END) AS "key_2",
MAX(CASE WHEN key_id=9 THEN key_value END) AS "key_2_value",
MAX(CASE WHEN key_id=10 THEN count ELSE 0.0 END) AS "key_3",
MAX(CASE WHEN key_id=10 THEN key_value END) AS "key_3_value"
FROM table1
GROUP BY name;
Without sample data, I'm assuming that you're looking at multiple rows and trying to get certain keys onto columns. Your original query is pulling the same value for every column.
EDIT:
Performance would be horrible on this if you have any major amounts of data, but you could try doing this ...
SELECT
name as "name",
MAX(CASE WHEN key_id=8 THEN count ELSE 0.0 END) AS "key_1",
(SELECT TOP 1 key_value FROM Table1 k1 WHERE k1.name = Table1.name AND key_id = 8 ORDER BY Count DESC) as "key_1_value",
MAX(CASE WHEN key_id=9 THEN count ELSE 0.0 END) AS "key_2",
(SELECT TOP 1 key_value FROM Table1 k2 WHERE k2.name = Table1.name AND key_id = 9 ORDER BY Count DESC) as "key_2_value",
MAX(CASE WHEN key_id=10 THEN count ELSE 0.0 END) AS "key_3",
(SELECT TOP 1 key_value FROM Table1 k3 WHERE k3.name = Table1.name AND key_id = 10 ORDER BY Count DESC) as "key_3_value"
FROM table1
GROUP BY name;
Hmm...maybe:
SELECT distinct name, b.MaxCount, b.Key_Value, c.MaxCount, c.Key_Value, d.MaxCount, d.Key_Value
from table1 a
left join
(SELECT key_Value, MAX(ifnull((count,0)) as MaxCount from table1 where key_id =8 group by key_Value) b
on a.name = b.name
left join
(SELECT key_Value, MAX(ifnull((count,0)) as MaxCount from table1 where key_id =9 group by key_Value) c
on a.name = b.name
left join
(SELECT key_Value, MAX(ifnull((count,0)) as MaxCount from table1 where key_id =10 group by key_Value) d
on a.name = b.name
group by name
I assume your join condition.
You got an IO error. This could be because of the isnull should be ifnull in SQLITE. sorry for that.