Is there any better method without subquery? - sql

I have some data like this:
TYPE
A
B
C
aaa
5
6
2022-05-01
aaa
8
7
2022-05-08
aaa
9
8
2022-05-16
bbb
7
4
2022-05-09
bbb
6
8
2022-05-14
bbb
3
3
2022-05-25
I need to get an output like this:
TYPE
A
D
C
aaa
22
8
2022-05-16
bbb
16
3
2022-05-25
My current code :
SELECT type, SUM(A) AS A, SUM(D) AS D, MAX(C) AS C
FROM
(SELECT
type, A, B, C,
CASE
WHEN C = MAX(C) OVER(PARTITION BY type)
THEN B
ELSE 0
END AS D,
FROM
data) AS bbb
GROUP BY
type
Is there any better method without using a subquery?
I'm using SQL Server.

Since you are asking for a query without a subquery, you can try this one:
SELECT DISTINCT TYPE,
SUM(A) OVER(PARTITION BY TYPE) AS A,
FIRST_VALUE(B) OVER(PARTITION BY TYPE ORDER BY C DESC) AS D,
MAX(C) OVER(PARTITION BY TYPE) AS C
FROM data;
Output:
TYPE
A
D
C
aaa
22
8
2022-05-16 00:00:00.000
bbb
16
3
2022-05-25 00:00:00.000
See this db<>fiddle demo.

Here is one approach using ROW_NUMBER along with pivoting logic:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY TYPE ORDER BY C DESC) rn
FROM data
)
SELECT TYPE, SUM(A) AS A,
MAX(CASE WHEN rn = 1 THEN B END) AS D,
MAX(CASE WHEN rn = 1 THEN C END) AS C
FROM cte
GROUP BY TYPE;
Here is a working demo.

Related

Sum a column based on a condition in another column with partitions

I'm trying to sum a column based on a condition in another column with partition by in SQL, but it's not working. So I hope somebody can help me with this.
My table is like this:
Group_1
Group_2
Date
Value
A
D
01/01/2021
1
A
D
01/02/2021
3
A
E
01/03/2021
5
B
D
01/01/2021
7
B
D
01/02/2021
9
B
E
01/03/2021
11
B
D
01/05/2021
17
B
D
01/03/2021
13
B
E
01/04/2021
13
C
D
01/01/2021
7
C
D
01/02/2021
10
So, I need to sum the values of [Value] for all rows where there is a 'D' on [Group_2] that is older than the first 'E' on the same group (if it exists) for each group of [Group_1].
And the result should be like:
Group_1
Group_2
Sum
A
D
4
B
D
16
C
D
17
Anybody knows how can I solve this kind of problem?
Try the following aggregation with NOT EXISTS:
SELECT Group_1, Group_2, SUM(Value) AS Value_Sum
FROM table_name T
WHERE Group_2 <> 'E' AND
NOT EXISTS (SELECT 1 FROM table_name D
WHERE D.Group_1 = T.Group_1 AND
D.Group_2 = 'E' AND
D.Date <= T.Date)
GROUP BY Group_1, Group_2
ORDER BY Group_1, Group_2
See a demo.
select group_1
,group_2
,sum(value)
from
(
select group_1
,group_2
,case when count(case when group_2 = 'E' then 1 end) over(partition by group_1 order by date) = 0 then value end as value
from t
) t
group by group_1, group_2
having group_2 = 'D'
group_1
group_2
sum
A
D
4
B
D
16
C
D
17
Fiddle

Select distinct fields from multiple table

Table 1 -
ID VehicleID
1 A
2 A
3 A
1 B
1 C
4 C
2 D
Table 2-
ID VehicleID VehicleNo
1 A AA
2 A AA
3 A
1 B BB
1 C CC
4 C CC
2 D DD
Output-
VehicleId VehicleNo
A AA
B BB
C CC
D DD
This is how I understood it; read comments within code.
SQL> with
2 -- calculate "RN" (so that you'd have something to match rows on)
3 a as
4 (select vehicleid,
5 row_number() over (order by vehicleid) rn
6 from (select distinct vehicleid from tab1)
7 ),
8 b as
9 (select vehicleno,
10 row_number() over (order by vehicleno) rn
11 from (select distinct vehicleno from tab2)
12 )
13 -- final query
14 select a.vehicleid, b.vehicleno
15 from a left join b on a.rn = b.rn;
VEHICLEID VEHICLENO
---------- ----------
A AA
B BB
C CC
D DD
SQL>
One simple method is aggregation:
select VehicleId, max(VehicleNo) as VehicleNo
from table2
group by VehicleId;

Is there a way to get first row of a group in postgres based on Max(date)

Input :
id name value1 value2 date
1 A 1 1 2019-01-01
1 A 2 2 2019-02-15
1 A 3 3 2019-01-15
1 A 1 1 2019-07-13
2 B 1 2 2019-01-01
2 B 1 3 2019-02-15
2 B 2 1 2019-07-13
3 C 2 4 2019-02-15
3 C 1 2 2019-01-01
3 C 1 9 2019-07-13
3 C 3 1 2019-02-15
Expected Output :
id name value1 value2 date
1 A 1 Avg(value2) 2019-07-13
2 B 2 Avg(value2) 2019-07-13
3 C 1 Avg(value2) 2019-07-13
You can use window functions. rank() over() can be used to identify the first record in each group, and avg() over() will give you a window average of value2 in each group:
select id, name, value1, avg_value2 value2, date
from (
select
t.*,
avg(value2) over(partition by id, name) avg_value2,
rank() over(partition by id, name order by date desc) rn
from mytable t
) t
where rn = 1
sort your data in the right way, use the window function row_number() as identifier and select the first entry of every partition.
with temp_data as
(
select
row_number() over (partition by debug.tbl_data.id order by debug.tbl_data.date desc) as index,
*,
avg(debug.tbl_data.value2)over (partition by debug.tbl_data.id) as data_avg
from debug.tbl_data
order by id asc, debug.tbl_data.date desc
)
select
*
from temp_data
where index = 1
You seem to want the most common value of value1. In statistics, this is called the "mode". You can do this as:
select id, name,
mode() within group (order by value1) as value1_mode,
avg(value2),
max(date)
from t
group by id, name;

Smarter GROUP BY

Consider Table like this.
I will call it Test
Id A B C D
1 1 1 8 25
2 1 2 5 35
3 1 3 2 75
4 2 2 2 45
5 3 2 5 26
Now I want rows with max 'Id' Grouped by 'A'
Id A B C D
3 1 3 2 75
4 2 2 2 45
5 3 2 5 26
-
--Work, but I do not want
SELECT MAX(Id), A FROM Test GROUP BY A
--I want but do not work
SELECT MAX(Id), A, B, C, D FROM Test GROUP BY A
--Work but I do not want
SELECT MAX(Id), A, B, C, D FROM Test GROUP BY A, B, C, D
--Work and I want
SELECT old.Id, old.A, new.B, new.C, new.D
FROM(
SELECT
MAX(Id) AS Id, A
FROM
Test GROUP BY A
)old
JOIN Test new
ON old.Id = new.Id
Is there a better way to write last query without join
Most databases support window functions:
select *
from (
select *, row_number() over (partition by a order by id desc) rn
from test
) t
where rn = 1
Most DBMS now support Common Table Expressions (CTE). You can use one.
;with maxa as (
select row_number() over(partition by a order by id desc) rn,
id,a,b,c,d from test
)
select id,a,b,c,d
from maxa
where rn=1

SQLSERVER group by (aggregate column based on other column)

I have a table which has 3 columns A, B, C
I want to do a query like this:
select A, Max(B), ( C in the row having max B ) from Table group by A.
is there a way to do such a query?
Test Data:
A B C
2 5 3
2 6 1
4 5 1
4 7 9
6 5 0
the expected result would be:
2 6 1
4 7 9
6 5 0
;WITH CTE AS
(
SELECT A,
B,
C,
RN = ROW_NUMBER() OVER(PARTITION BY A ORDER BY B DESC)
FROM YourTable
)
SELECT A, B, C
FROM CTE
WHERE RN = 1
Try this
select t.*
from table t
join (Select A,max(b) B from table group by A) c
on c.a=t.a
and c.b=a.b