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

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

Related

Is there any better method without subquery?

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.

SQL server pivot + sum + group by

I have data as following and i need to group sum pivot
AA
BB
date
a
1
01/01/2020
a
2
01/01/2020
b
5
01/01/2020
b
1
01/01/2020
c
5
01/01/2020
d
1
01/01/2020
d
8
02/01/2020
e
1
01/01/2020
what I obtain with my sql code
a
b
c
d
e
01/01/2020
3
6
5
1
1
02/01/2020
/
/
/
8
/
what I need to obtain: a and d grouped as f and c and e grouped as g and b separate
b
f
g
01/01/2020
6
4
6
02/01/2020
/
8
/
I have got the following sql but I cant seem to do the group summing. Do you do it before pivoting or after?
SELECT * FROM(
SELECT AA,Date
FROM [dbo].[Data] )
AS SourceTable
PIVOT(SUM([BB])
FOR [AA] IN([a],[b],[c],[d],[e]))
AS PivotTable
IF I try this it doesnt work
SELECT * FROM(
SELECT AA,Date
FROM [dbo].[Data] )
AS SourceTable
PIVOT(SUM([BB])
FOR [AA] IN([a]+[d],[b],[c]+[e]))
AS PivotTable
Use conditional aggregation as follows:
select sum(case when aa in ('a','d') then BB end) as f,
sum(case when aa in ('c','e') then BB end) as g,
sum(case when aa = 'b' then BB end) as b
from table_name
group by date
I find that this is simpler done with conditional aggregation:
select
date,
sum(case when d.aa = 'b' then bb else 0 end) as b,
sum(case when d.aa in ('a', 'd') then bb else 0 end) as f,
sum(case when d.aa in ('c', 'e') then bb else 0 end) as g
from data d
group by date

split data in a table into 2 columns based on conditions

got to split a column in my table into 2 based on conditions.All in a select clause.
my table has a column as detail_code. There are 2 detail codes .I want each detail code as a separate column in my select clause.
I tried subquery but it throws subquery returns more than one row.
select id,(select detail_code from detc where type_ind ='C')as Detail_code1,
(select detail_code from detc where type_ind ='P')as Detail_code2,
(Select sum(amount) from amount_tbl where detail_code in (select detail_code from detc where type_ind ='C')and term_code='2019')as amt_detail_code1
from id_table;
My output should be
Id Detail_code1 Detail_code2 sum(amt_Detail_Code1) sum(amt_Detail_code2)
1 C P 15 45
2 C P 785 74
My input
ID Detail_cd ind amt
317002687 CA20 C 3
317002687 CA21 C 60
317002687 CA23 C 18.75
317002687 CA25 C 179.64
317002687 CA26 C 136.5
317002687 CA27 C 8.25
317002687 CA28 C 4
317002687 CA2B C 8
317002687 CA2H C 6.75
317002687 CA2I C 237
317002687 CA2J C 65.4
This query would produce the output you describe:
SELECT id, 'C' AS detail_code1, 'P' AS detail_code2,
SUM(CASE WHEN ind = 'C' THEN amt END) AS sum_detail_code1,
SUM(CASE WHEN ind = 'P' THEN amt END) AS sum_detail_code2
FROM name_of_your_input_table
GROUP BY id;
Alternatively, since you know what the two detail codes are, you could make those the column names and have the value of each column be the average for that detail code. One way to do this is to pivot. Here's the Oracle syntax:
SELECT *
FROM (SELECT id, ind AS detail_code, amt
FROM name_of_your_input_table)
PIVOT (AVG(amt)
FOR detail_code IN ('C', 'P'));

sql query to get multiple data through date

table ta having four columns ( SQL server and D column is date)
A | B | C|D
1 |11| 0|10-MAY-2019
1 |12| 0|10-MAY-2019
1 |13| 0|null
2 |33| 5|null
2 |34| 10|null
2 |35| 78|null
5 |45| 0|10-MAY-2019
5 |49| 0|10-MAY-2019
5 |51| 0|10-MAY-2019
8 |10| 0|1-MAY-2018
8 |14| 0|1-MAY-2018
8 |34| 0|1-MAY-2018
I am looking the SQL query to fetch the distinct A value which is having C value ZERO for all the B (ie. SUM(ABS(C))=0) and all D value for that will not be null and should be > GETDATE() - 90 (i.e any day between current date and 90 days)
From above table I would only get the value of A as '5'
Try this-
SELECT * FROM
(
SELECT A
FROM your_table
WHERE D > CAST(DATEADD(DD,-90,GETDATE()) AS DATE)
GROUP BY A
HAVING COUNT(A) = SUM(CASE WHEN C= 0 THEN 1 ELSE 0 END)
)A
WHERE NOT EXISTS
(
SELECT 1 FROM your_table B WHERE A.A = B.A
AND D IS NULL
)
You can use aggregation. I think this is the logic you describe:
select a
from t
where d > dateadd(day, -90, getdate()) or d is null
group by a
having max(c) = 0 and
count(*) = count(d); -- no NULL d values

How do I return the sum for this query?

I have the following tables I need to find out the sum.
Table A
ID Name
1 Jason
2 Peter
3 Ravi
Table B
ID ID_SEC
1 11
1 12
1 13
2 21
2 22
2 23
3 31
3 32
3 33
Table C
ID_SEC Value Include_Ind
11 100 Y
12 200 Y
13 300 N
21 10 Y
22 20 N
23 30 N
31 1000 N
32 2000 N
33 3000 N
Output
ID Name Total Include_Ind_count [only count when Y]
1 Jason 600 2
2 Peter 60 1
3 Ravi 6000 0
Use:
SELECT a.id,
a.name,
SUM(c.value) AS total
FROM TABLE_A a
JOIN TABLE_B b ON b.id = a.id
JOIN TABLE_C c ON c.id_sec = b.id_sec
GROUP BY a.id, a.name
The trick to counting INCLUDE_IND only when the flag is set to 'Y' is to use CASE() to test its value:
SQL> select a.id
2 , a.name
3 , sum ( c.val) as total
4 , count( case when c.include_ind = 'Y' then 1
5 else null end ) as inc_ind_cnt
6 from a
7 join b on ( b.id = a.id )
8 join c on ( c.id_sec = b.id_sec )
9 group by a.name, a.id
10 order by a.id
11 /
ID NAME TOTAL INC_IND_CNT
---------- ---------- ---------- -----------
1 Jason 600 2
2 Peter 60 1
3 Ravi 6000 0
SQL>
The ORDER BY is necessary to guarantee sort order since Oracle changed the algorithm it uses for GROUP BY operations in 10g.
You can use inner Joins and SUM for getting the result -
Assuming you tableC.Value is int field. Else you need to cast it.
SELECT tabA.id, tabA.name, SUM(tabC.value)
FROM TABLE_A tabA
INNER JOIN TABLE_B tabB ON tabB.id = tabA.id
INNER JOIN TABLE_C tabc ON tabC.id_sec = tabB.id_sec
GROUP BY tabA.id, tabA.name