Select TOP 3 values with their respective groups and values - sql

So i have a table like this
user
GROUP
VALUE
A
G1
0.9
A
G2
0.8
A
G3
0.3
A
G4
0.7
B
G1
0.9
B
G2
0.8
B
G3
0.7
C
G1
0.9
C
G2
0.8
and need to get to something like this
user
first_G
Fir_G_val
second_G
sec_G_val
third_G
thi_G_val
A
G1
0.9
G2
0.8
G4
0.7
B
G1
0.9
G2
0.8
G3
0.7
C
G1
0.9
G2
0.8
NULL
NULL
I tried in different ways, none worked out for me (Guided by this post)

This operation is called pivot and can be carried out by:
selecting the ranking for each group, using the ROW_NUMBER window function
extracting group and value alternatively, for each of the new field
aggregating values on each user
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY user_ ORDER BY VALUE DESC) AS rn
FROM tab
)
SELECT user_,
MAX(CASE WHEN rn = 1 THEN GROUP_ END) AS first_G,
MAX(CASE WHEN rn = 1 THEN VALUE_ END) AS first_G_val,
MAX(CASE WHEN rn = 2 THEN GROUP_ END) AS second_G,
MAX(CASE WHEN rn = 2 THEN VALUE_ END) AS second_G_val,
MAX(CASE WHEN rn = 3 THEN GROUP_ END) AS third_G,
MAX(CASE WHEN rn = 3 THEN VALUE_ END) AS third_G_val
FROM cte
GROUP BY user_

Related

How to select 70 percent of a column based on condition in SQL?

This is my existing table data
C1 C2 C3
1 A 1
2 B 1
3 C 0
4 D 0
5 E 0
6 F 0
7 G 1
8 H 1
9 I 1
10 J 0
I want to get this. What I am trying is I want to select 70% C3 column with value 1. In total the C3 has five ones. So 70% of 5 is 3.5 which is 4 ones. So I want to get my final dataset with 70 percent of ones in C3
C1 C2 C3
1 A 1
2 B 1
3 C 0
4 D 0
5 E 0
7 G 1
8 H 1
Here is the answer
select *
from
(SELECT *,
(SELECT SUM(C3) FROM table_name t1 WHERE t1.C1 <= t.C1) AS cumulative_sum,
(select sum(C3) from table_name) as total_sum
FROM table_name t) t
where (cumulative_sum - C3) < 0.8 * total_sum
Hmmm. You don't seem to want a random selection. They seem to be ordered by col1. So, you can calculate this as:
select t.*
from (select t.*,
sum(case when col3 = 1 then 1 else 0 end) over (order by col1) as running_col3,
sum(case when col3 = 1 then 1 else 0 end) over () as total_col3
from t
) t
where running_col3 >= 0.8 * total_col3 and
(running_col3 - col3) < 0.8 * total_col3;
Note: If col3 has only 0 and 1, you can simplify the above to:
select t.*
from (select t.*,
sum(col3) over (order by col1) as running_col3,
sum(col3) over () as total_col3
from t
) t
where running_col3 >= 0.8 * total_col3 and
(running_col3 - col3) < 0.8 * total_col3

combine 3 rows (with two columns) into columns (6) - per user

I have something like that:
Name colA colB
row1 John a b
row2 John c d
row3 John e f
row4 Anna g h
row5 Anna i j
row6 Anna k l
and I would like to have something like that:
Name colA1 colB1 colA2 colB2 colA3 colB3
row1 John a b c d e f
row2 Anna g h i j k l
It will be always 3 rows per user.
You can use row_number() and conditional aggregation:
select name,
max(case when seqnum = 1 then a end) as a_1,
max(case when seqnum = 1 then b end) as b_1,
max(case when seqnum = 2 then a end) as a_2,
max(case when seqnum = 2 then b end) as b_2,
max(case when seqnum = 3 then a end) as a_3,
max(case when seqnum = 3 then b end) as b_3
from (select t.*
row_number() over (partition by name order by (select null)) as seqnum
from t
) t
group by name;

Find the common value over partition

I have a table like this :
Loan_Num asset LTV
1 20 0.2
2 20 0.2
3 20 0.12
4 20 0.2
5 10 0.3
6 10 0.3
7 10 0.22
8 10 0.3
And I want to add a common value to this table by the group of asset.
Loan_Num asset LTV cV
1 20 0.2 0.2
2 20 0.2 0.2
3 20 0.12 0.2
4 20 0.2 0.2
5 10 0.3 0.3
6 10 0.3 0.3
7 10 0.22 0.3
8 10 0.3 0.3
Any suggestions how to do this? is there a built in function for common value?
One way of doing this would be
WITH CTE1
AS (SELECT *,
COUNT(*) OVER (PARTITION BY [asset], [LTV]) AS C
FROM YourTable),
CTE2
AS (SELECT *,
RANK() OVER (PARTITION BY [asset] ORDER BY C DESC, [LTV] DESC) AS R
FROM CTE1)
SELECT [Loan_Num],
[asset],
[LTV],
MAX(CASE
WHEN R = 1
THEN [LTV]
END) OVER (PARTITION BY [asset]) AS cV
FROM CTE2
Demo
Though actually this would be slightly more efficient as it removes a sort
WITH CTE1
AS (SELECT *,
COUNT(*) OVER (PARTITION BY [asset], [LTV]) AS C
FROM YourTable),
CTE2
AS (SELECT *,
MAX(C) OVER (PARTITION BY [asset]) AS MaxC
FROM CTE1)
SELECT [Loan_Num],
[asset],
[LTV],
MAX(CASE
WHEN C = MaxC
THEN [LTV]
END) OVER (PARTITION BY [asset]) AS cV
FROM CTE2

Get specific items from partition which should not be in other partition

I have below table -
ID type group_name creation_date
1 A G1 C1
2 B G2 C2
3 C G2 C3
4 B G1 C4
I want to extract the old type items in each group, but if that type item is latest item in other partition , then i won't extract that.
So, for G1, I will have 2 items A and B where C1 > C4
For G2, I will have 2 items B and C where C2 > C3.
Ideally, B is older for group G1 and C is older for group G2
But i don't want to extract B for G1 since it is latest for G2. Hence
the output should be C only.
Could anyone help how can i achieve this ?
Query:
SELECT DISTINCT
type
FROM (
SELECT type,
rnk,
COUNT( CASE rnk WHEN 1 THEN 1 END ) OVER ( PARTITION BY type ) AS ct
FROM (
SELECT type,
RANK() OVER ( PARTITION BY group_name ORDER BY creation_date DESC ) AS rnk
FROM table_name
)
)
WHERE rnk > 1 AND ct = 0;
Output:
TYPE
----
C

calculating percent change over time

I've created the structure and sample data here. I'm not sure how to go about calculating the change over time.
My desired result set is:
a | % growth
abc | 4.16
def | 0.83
hig | -0.2
The % change being (last value - first value) / days:
a | % growth
abc | (30-5) / 6
def | (6-1) / 6
hig | (4-5) / 5
I'm trying:
SELECT a.*,
b.val,
c.val
FROM (SELECT a,
Min(dt) AS lowerDt,
Max(dt) AS upperDt
FROM tt
GROUP BY a) a
LEFT JOIN tt b
ON b.dt = a.lowerdt
AND b.a = a.a
LEFT JOIN tt c
ON c.dt = a.upperdt
AND b.a = a.a
If possible, I'd like to avoid a CTE.
You don't want min and max, you really want first and last.
One way I do that is to use ROW_NUMBER() to tell me the position from the begining or the end. Then use MAX(CASE WHEN pos=1 THEN x ELSE null END) to get the values I want.
SELECT
a,
MAX(CASE WHEN pos_from_first = 1 THEN dt ELSE NULL END) AS first_date,
MAX(CASE WHEN pos_from_final = 1 THEN dt ELSE NULL END) AS final_date,
MAX(CASE WHEN pos_from_first = 1 THEN val ELSE NULL END) AS first_value,
MAX(CASE WHEN pos_from_final = 1 THEN val ELSE NULL END) AS final_value,
100
*
CAST(MAX(CASE WHEN pos_from_final = 1 THEN val ELSE NULL END) AS DECIMAL(9,6))
/
CAST(MAX(CASE WHEN pos_from_first = 1 THEN val ELSE NULL END) AS DECIMAL(9,6))
-
100 AS perc_change
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY a ORDER BY dt ASC) AS pos_from_first,
ROW_NUMBER() OVER (PARTITION BY a ORDER BY dt DESC) AS pos_from_final,
*
FROM
tt
)
AS ordered
GROUP BY
a
http://sqlfiddle.com/#!6/ad95d/11