SQL: select max value when converting from long to wide format - sql

I have a table like the following and I need to convert it from this long format to wide and at the same time, I need to select the action value corresponding to max score:
user_id time_id val0 val1 val2 action_value score
1 1 1 0 0 0 0.6
1 1 0 1 0 1 0.3
1 1 0 0 1 2 0.3
1 2 1 0 0 0 0.7
1 2 0 1 0 1 0.4
1 2 0 0 1 2 0.3
2 1 1 0 0 0 0.4
2 1 0 1 0 1 0.5
2 1 0 0 1 2 0.4
the desired output is:
user_id time_id score_0 score_1 score_2 action_value
1 1 0.6. 0.3. 0.3 0
1 2 0.7. 0.4 0.3. 0
2 1 0.4 0.5 0.4. 1
the SQL I use do not have PIVOT, so I cannot use PIVOT. And I know that I can use conditional aggregation to convert the data from long format to wide format but I am not sure how I can also select the action_value column corresponding to the max score.

You could use a subquery, then greatest() (if your database supports it):
select
t.*,
case greatest(score0, score1, score2)
when score0 then 0
when score1 then 1
when score2 then 2
end action_value
from (
select
user_id,
time_id,
max(case when actual_value = 0 then score end) score0,
max(case when actual_value = 1 then score end) score1,
max(case when actual_value = 2 then score end) score2
from mytable
group by user_id, time_id
) t

Related

Is there a way to get max consecutive counts of 1s across columns in SQL?

Is there a way to get maximum consecutive counts across columns in SQL? I'm looking for longest duration of survival.
For example, if I have a dataset that looks like this
ID T1 T2 T3 T4 T5 T6 T7 T8 T9
1 1 1 0 0 0 1 1 1 1
2 0 0 0 1 1 1 1 1 0
3 0 1 0 1 0 1 1 0 0
4 0 1 0 0 0 0 0 0 0
5 0 1 1 0 0 0 0 0 0
6 1 0 1 1 0 1 1 1 0
I want to add a column to get the maximum consecutive 1s across the columns T1-T9 so it would look like this
ID T1 T2 T3 T4 T5 T6 T7 T8 T9 MAX
1 1 1 0 0 0 1 1 1 1 4
2 0 0 0 1 1 1 1 1 0 5
3 0 1 0 1 0 1 1 0 0 2
4 0 1 0 0 0 0 0 0 0 1
5 0 1 1 0 0 0 0 0 0 2
6 1 0 1 1 0 1 1 1 0 3
**The below code is a way to get maximum consecutive counts across Column in MySQL I think you want across the Row **
create table t(id integer,t1 integer,t2 integer,t3 integer,t4 integer,t5 integer,t6 integer,t7 integer,t8 integer,t9 integer);
insert into t values(1,1,0,1,0,1,1,0,0,0),(2,0,0,1,1,1,0,0,0,0),(3,1,0,1,1,1,1,0,0,0);
WITH CTE1 AS
(
SELECT id , ROW_NUMBER() OVER (ORDER BY id) Rnk FROM t
)
,CTE2 AS
(
SELECT *, CASE WHEN id-1 = LAG(id) OVER(ORDER BY rnk) THEN 0
ELSE 1 END cols FROM CTE1 c2
)
,CTE3 AS
(
SELECT *, SUM(cols) OVER(ORDER BY rnk) Grouper FROM CTE2 c2
)
SELECT * FROM
(
SELECT COUNT(*) Counts FROM CTE3 GROUP BY Grouper
)r
ORDER BY Counts DESC ;
I think the simplest method in proc sql might be a brute force approach:
select t.*,
(case when t1||t2||t3||t4||t5||t6||t7||t8||t9 like '%111111111%' then 9
when t1||t2||t3||t4||t5||t6||t7||t8||t9 like '%11111111%' then 8
when t1||t2||t3||t4||t5||t6||t7||t8||t9 like '%1111111%' then 7
when t1||t2||t3||t4||t5||t6||t7||t8||t9 like '%111111%' then 6
when t1||t2||t3||t4||t5||t6||t7||t8||t9 like '%11111%' then 5
when t1||t2||t3||t4||t5||t6||t7||t8||t9 like '%1111%' then 4
when t1||t2||t3||t4||t5||t6||t7||t8||t9 like '%111%' then 3
when t1||t2||t3||t4||t5||t6||t7||t8||t9 like '%11%' then 2
when t1||t2||t3||t4||t5||t6||t7||t8||t9 like '%1%' then 1
else 0
end) as max
from t;
Here is a db<>fiddle illustrating the logic using Postgres.

sql: long to wide format without using PIVOT

I have a table like the following:
user_id time_id val0 val1 val2 actual_value score
1 1 1 0 0 0 0.6
1 1 0 1 0 1 0.4
1 1 0 0 1 2 0.3
1 2 1 0 0 0 0.7
1 2 0 1 0 1 0.4
1 2 0 0 1 2 0.3
2 1 1 0 0 0 0.9
2 1 0 1 0 1 0.5
2 1 0 0 1 2 0.4
I want to convert the data to wide format like the following:
user_id time_id score_0 score_1 score_2
1 1 0.6. 0.3. 0.3
1 2 0.7. 0.4. 0.3
2 1 0.9. 0.5. 0.4
the SQL I used does not have a pivot choice so I am wondering how to convert the long format to wide without using PIVOT.
If I understand your question correctly, you can do conditional aggregation:
select
user_id,
time_id,
max(case when val0 = 1 then score end) score0,
max(case when val1 = 1 then score end) score1,
max(case when val2 = 1 then score end) score2
from mytable
group by user_id, time_id
Maybe you want to use actual_value to pivot instead of the val[n] columns:
select
user_id,
time_id,
max(case when actual_value = 0 then score end) score0,
max(case when actual_value = 1 then score end) score1,
max(case when actual_value = 2 then score end) score2
from mytable
group by user_id, time_id
You can use conditional aggregation:
select user_id, time_id,
max(case when actual_value = 0 then score end) as score_0,
max(case when actual_value = 1 then score end) as score_1,
max(case when actual_value = 2 then score end) as score_2
from t
group by user_id, time_id;

Rounding and getting a count of the number of times a number appears? SQL

I have the following data:
-1.25
-0.5
0
0
1.25
0.75
1.25
2
2
2
2
I'm trying to consolidate these numbers while also rounding them, and making anything that is negative go to 0.
Here is what I have so far:
SELECT
RawScore,
Count(*) AS Freq
FROM TestScores
GROUP BY RawScore
This returns
RawScore Freq
-1.25 1
-0.5 1
0 2
0.75 1
1.25 1
2 4
Is there a way to get this instead?
RawScore Freq
0 4
1 2
2 4
You can do:
select (case when rawScore < 0 then 0
else round(rawScore, 0)
end) as nolongerrawScore,
count(*)
from TestScores
group by (case when rawScore < 0 then 0
else round(rawScore, 0)
end)
order by nolongerrawScore;
SELECT
FLOOR(ABS(RawScore)),
Count(*) AS Freq
FROM TestScores
GROUP BY FLOOR(ABS(RawScore))

SQL Server - Show All possible Values and Count Them

I have the following table:
IdSce Year NoIte Value
1 0 1 1
1 0 2 5
1 0 3 1
1 1 1 2
1 1 2 3
1 1 3 2
2 0 1 4
2 0 2 4
2 0 3 1
2 1 1 2
2 1 2 4
2 1 3 3
I want to group by IdSce and Year, and show each possible value and count how many time each value appears like this:
IdSce Year Value1 Value2 Value3 Value4 Value5
1 0 2 0 0 0 1
1 1 0 2 1 0 0
2 0 1 0 0 2 0
2 1 0 1 1 1 0
Thanks !
EDIT
shawnt00 is really close to what I want, but I'm looking to do it as dynamic as possible, meaning if I have 10 different values for the column value, I will be missing information in my table. Therefore, if I have 10 different values, I want 10 new columns (value1, value2, ... , value10)
This is what I've tried so far:
SELECT IdSce
,Year
,SUM(CASE WHEN Value >= 0 and Value < 1 THEN 1 else 0 end) Zero
,SUM(CASE WHEN Value >= 1 and Value < 2 THEN 1 else 0 end) One
,SUM(CASE WHEN Value >= 2 and Value < 3 THEN 1 else 0 end) Two
,SUM(CASE WHEN Value >= 3 and Value < 4 THEN 1 else 0 end) Three
,SUM(CASE WHEN Value >= 4 and Value < 5 THEN 1 else 0 end) Four
,SUM(CASE WHEN Value >= 5 THEN 1 else 0 end) FiveMore
,SUM(CASE WHEN Value >= 0 THEN 1 else 0 end) Total
FROM Table
GROUP BY IdSce
,Year
Thanks for the help again!
Ok, I'll do it!
select IdSce, "Year"
count(case when Value = 1 then 1 end) as "1",
count(case when Value = 2 then 1 end) as "2",
count(case when Value = 3 then 1 end) as "3",
count(case when Value = 4 then 1 end) as "4",
count(case when Value = 5 then 1 end) as "5"
from T
group by IdSce, "Year"
I think you'll often find this filed under "conditional aggregation". SQL Server has a proprietary syntax that uses pivot if you want to look into that also.

SUM for each row and filter result SUM IN $x

My table's data in Postgres:
id user_id sell_amount sell_currency_id buy_amount buy_currency_id type status date_add
5 2 2.00000000 1 4.00000000 0 0 0 2013-12-15 19:06:40
6 3 2.60000000 1 5.10000000 0 0 0 2013-12-15 19:06:54
4 1 1.00000000 1 0.80000000 0 0 0 2013-12-15 19:07:05
7 4 4.00000000 1 8.20000000 0 0 0 2013-12-15 19:07:21
8 5 3.00000000 1 6.00000000 0 1 0 2013-12-15 19:07:40
I have to select id, user_id, sell_amount, sell_currency_id from this table were status=0 AND type=0 AND SUM to current row IN $x, order by ORDER BY buy_amount/sell_amount ASC, date_add ASC
Result for $x = 6
id user_id sell_amount sell_currency_id SUM(sell_amount)
4 1 1.00000000 1 1.00000000
6 3 2.60000000 1 3.60000000
5 2 2.00000000 1 5.60000000
7 4 4.00000000 1 9.60000000
You need a cumulative sum, which Postgres offers. The logic is then a little tricky. You want the first value that is greater than or equal to $x.
select id, user_id, sell_amount, sell_currency
from (select id, user_id, sell_amount, sell_currency,
sum(sell_amount) over (order by buy_amount/sell_amount ASC, date_add ASC) as cumsell
from table t
where status = 0 and type = 0
) t
where $x <= cumsell and $x > cumsell - sell_amount;