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

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.

Related

How to create a pivot table in PostgreSQL

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.

SQL Function for updating column with values

Those who have helped me before, i tend to use SAS9.4 a lot for my day to day work, however there are times when i need to use SQL Server
There is a output table i have with 2 variables (attached output.csv)
output table
ID, GROUP, DATE
The table has 830 rows:
330 have a "C" group
150 have a "A" group
50 have a "B" group
the remaining 300 have group as "TEMP"
within SQL i do not now how to programatically work out the total volume of A+B+C. The aim is to update "TEMP" column to ensure there is an Equal amount of "A" and "B" totalling 250 of each (the remainder of the total count)
so the table totals
330 have a "C" group
250 have a "A" group
250 have a "B" group
You want to proportion the "temp" to get equal amounts of "A" and "B".
So, the idea is to count up everything in A, B, and Temp and divide by 2. That is the final group size. Then you can use arithmetic to allocate the rows in Temp to the two groups:
select t.*,
(case when seqnum + a_cnt <= final_group_size then 'A' else 'B' end) as allocated_group
from (select t.*, row_number() over (order by newid()) as seqnum
from t
where group = 'Temp'
) t cross join
(select (cnt_a + cnt_b + cnt_temp) / 2 as final_group_size,
g.*
from (select sum(case when group = 'A' then 1 else 0 end) as cnt_a,
sum(case when group = 'B' then 1 else 0 end) as cnt_b,
sum(case when group = 'Temp' then 1 else 0 end) as cnt_temp
from t
) g
) g
SQL Server makes it easy to put this into an update:
with toupdate as (
select t.*,
(case when seqnum + a_cnt <= final_group_size then 'A' else 'B' end) as allocated_group
from (select t.*, row_number() over (order by newid()) as seqnum
from t
where group = 'Temp'
) t cross join
(select (cnt_a + cnt_b + cnt_temp) / 2 as final_group_size,
g.*
from (select sum(case when group = 'A' then 1 else 0 end) as cnt_a,
sum(case when group = 'B' then 1 else 0 end) as cnt_b,
sum(case when group = 'Temp' then 1 else 0 end) as cnt_temp
from t
) g
) g
)
update toupdate
set group = allocated_group;
I'd go with a top 250 update style approach
update top (250) [TableName] set Group = 'A' where exists (Select * from [TableName] t2 where t2.id = [TableName].id order by newid()) and Group = 'Temp'
update top (250) [TableName] set Group = 'B' where exists (Select * from [TableName] t2 where t2.id = [TableName].id order by newid()) and Group = 'Temp'

sql transformation - rows to column

i have been trying to solve this one image
my initial idea is like this
select name,
CASE
when count(name) = 1 then get first distinct value
when count(name) = 2 then get first distinct value
else get first distinct value
END as val1,
CASE
when count(name) = 1 then null
when count(name) = 2 then get second distinct value
else get second distinct value
END as val2,
CASE
when count(name) = 1 then null
when count(name) = 2 then null
else get third distinct value
END as val3
into desired_table
from source_table
group by name
is my attempt feasible? if so, how do i access the first, second and third distinct values?
use pivot . Your output table was incorrect. The correct form is available in db<>fiddle.
select name,x as value1,y as value2,z as value3
from
(
select *
from t1
) as SourceTable
pivot
(
max(value) for value in(x,y,z)
) as PivotTable
demo in db<>fiddle
You can use conditional aggregation along with row_number():
select name,
max(case when seqnum = 1 then value end) as value_1,
max(case when seqnum = 2 then value end) as value_2,
max(case when seqnum = 3 then value end) as value_3
into desired_table
from (select s.*,
row_number() over (partition by name order by value) as seqnum
from source_table s
) s
group by name;

sql17 - The column 'id' was specified multiple times for 't'

I write below query in mssm 17.
select t.id,
max (case when seq=1 then label end) as label-1,
max (case when seq=2 then label end) as label-2,
max (case when seq=3 then label end) as label-3,
max (case when seq=4 then label end) as label-4,
max (case when seq=5 then label end) as label-5,
from (select t1.*,t2.*,
row-number () over (partition by t1.id order by t2.label) as seq
from table1 t1 inner join table2 t2 on t1.id=t2.sequence
) t
group by t.id
The query don't execute and the error is: The column 'id' was specified multiple times for 't'.
I want to merge all rows with similar id into one row and different columns.
can anybody help me and solve the error?
You have multiple issues with the query. This specific issue is that id is in both table1 and table2. So, just select the columns that you need in the outer query.
Also, - is not allowed for column aliases. And the correct function name is row_number(), not row-number():
select t.id,
max(case when seq=1 then label end) as label_1,
max(case when seq=2 then label end) as label_2,
max(case when seq=3 then label end) as label_3,
max(case when seq=4 then label end) as label_4,
max(case when seq=5 then label end) as label_5,
from (select t1.id, t2.label,
row_number() over (partition by t1.id order by t2.label) as seq
from table1 t1 inner join
table2 t2
on t1.id = t2.sequence
) t
group by t.id

SQL query to return history as flat format

Looking for help with an SQL query to turn a history table into flat file format with up to 5 instances of results on table B. I have only shown 2 instances in the results. For a bonus point can these be sorted by EFF_DATE ascending?!
My query so far is
SELECT a.REFNO, a.M_NAME, b.EFF_DATE, b.VAL
FROM TABLEA a INNER JOIN TABLEB b ON (a.REFNO=b.REFNO)
WHERE a.REFNO = '1'
This is fine for returning results once per row, but how do I modify so up to 5 EFF_DATE and VAL instances are repeated on one row. The dates can be any date and ideally would like them sorted ascending left to right. Only those rows on TABLEB where Val > 0 should be included.
If you know the number of columns you want in the history, then you can use conditional aggregation or pivot. The challenge is not having a column for the pivot.
You can easily generate one, though, using ROW_NUMBER():
SELECT a.REFNO, a.M_NAME,
MAX(CASE WHEN seqnum = 1 THEN b.EFF_DATE END) as EFF_DATE_1,
MAX(CASE WHEN seqnum = 1 THEN b.VAL END) as VAL_1,
MAX(CASE WHEN seqnum = 2 THEN b.EFF_DATE END) as EFF_DATE_2,
MAX(CASE WHEN seqnum = 2 THEN b.VAL END) as VAL_2,
MAX(CASE WHEN seqnum = 3 THEN b.EFF_DATE END) as EFF_DATE_3,
MAX(CASE WHEN seqnum = 3 THEN b.VAL END) as VAL_3
FROM TABLEA a INNER JOIN
(SELECT b.*,
ROW_NUMBER() OVER (PARTITION BY REFNO ORDER BY EFF_DATE) as seqnum
FROM TABLEB b
) b
ON a.REFNO = b.REFNO
WHERE a.REFNO = '1'
GROUP BY a.REFNO, a.M_NAME;
If you don't know the number of columns in the output, then you will need dynamic SQL or to do the formatting at the application layer.