How to sum() if missing data in same column in SQL - sql

I need to sum up the quantity according to typein the table below.
Table
Name Quantity Type
a 6 AB
b 2 BB
b 4 AB
c 8 BB
a 3 BB
b 5 AB
Outcome
Name AB_Type BB_Type
a 6 3
b 9 2
c 0 8
I am trying the below query but I can't get the numbers right.
SELECT S.Name, SUM(S1.Quantity) AS AB_Type, SUM(S2.Quantity) AS BB_Type
FROM Table AS S, Table AS S1, Table AS S2
WHERE S.Name = S1.SName = S2.Name AND S1.Type = 'AB'AND S2.Type = 'BB'
GROUP BY S.Name;
Thanks in advance!!

Try this
SELECT
Name,
SUM(CASE WHEN Type = 'AB' THEN Quantity ELSE 0 END) AS AB_Type,
SUM(CASE WHEN Type = 'BB' THEN Quantity ELSE 0 END) AS BB_Type
FROM Table
GROUP BY Name

you can use pivot as follows:
SELECT Name,
iif(AB_Type is null,0,AB_Type) AB_Type,
iif(BB_Type is null,0,BB_Type) BB_Type
FROM (
SELECT
Name
,Quantity
,CONCAT(Type, '_type') AS Col
FROM table
) Src
PIVOT (
sum(Quantity)
FOR Col IN (
[AB_Type], [BB_Type]
)
) Pvt

Related

T-SQL: How do I flatten out a table like this? [duplicate]

This question already has answers here:
Convert Rows to columns using 'Pivot' in SQL Server
(9 answers)
Closed 3 years ago.
I have a table I loaded that looks like this:
CUSTID VALUETYPE COST
1 A 123
1 B 456
1 C 789
2 B 222
And I need to flatten it out in the same table or insert into a new one to look like this:
CUSTID A B C
1 123 456 789
2 0 222 0
Each row has an identity column not shown.
What would this cursor look like?
Thank you.
I don't see that you need to sum the columns:
select
custid,
max(case when valuetype = 'A' then cost else 0 end) A,
max(case when valuetype = 'B' then cost else 0 end) B,
max(case when valuetype = 'C' then cost else 0 end) C
from tablename
group by custid
Use a query, such as conditional aggregation:
select custid,
sum(case when valuetype = 'A' then cost end) as a,
sum(case when valuetype = 'B' then cost end) as b,
sum(case when valuetype = 'C' then cost end) as c
from t
group by custid;
use case when
select custid , sum(case when valuetype='A' then cost else 0 end) A,
sum(case when valuetype='B' then cost else 0 end) B
,sum(case when valuetype='C' then cost else 0 end) C
from t group by custid
You could make use of a PIVOT
SELECT
CUSTID
,ISNULL(p.A,0) AS A
,ISNULL(p.B,0) AS B
,ISNULL(p.C,0) AS C
FROM t
PIVOT (
SUM(COST) FOR VALUETYPE IN ([A],[B],[C])) p
If you don't mind NULL values in the Results
Select *
From YourTable
Pivot (sum(Cost) for ValueType in ([A],[B],[C])) pvt
Returns
CUSTID A B C
1 123 456 789
2 NULL 222 NULL
Otherwise, You Can Eliminate NULL Values
Select *
From (Select * From YourTable
Union All
Select A.CustID ,B.VALUETYPE,0
From (Select Distinct CustID from YourTable) A
Cross Join (Select Distinct VALUETYPE from YourTable) B
) src
Pivot (sum(Cost) for ValueType in ([A],[B],[C])) pvt
Returns
CUSTID A B C
1 123 456 789
2 0 222 0

query returning duplication's despite nested query returning no duplicates

I have the following query below which works. However it is return duplicates which I don't understand.
The first three nested queries return no duplicates so I don't understand why the end result has duplicates sedols?
;with b as
(
select sedol, wgt from myTbl
where name = 'B'
), j as
(
select sedol, wgt from myTbl
where name = 'J'
), s as
(
select sedol, wgt from myTbl
where name = 'S'
), hlds as
(
select coalesce(b.sedol, j.sedol, s.sedol) sedol, isnull(b.wgt,0) bw, isnull(j.wgt,0) jw, isnull(s.wgt,0) sw
from b full outer join j on b.sedol = j.sedol
full outer join s on b.sedol = s.sedol
)
select hlds.* from hlds
order by sedol
sample data
myTbl
sedol name wgt
abc b 1
abc j 2
abc s 3
def j 2
def s 4
current result
abc 1 2 3
def 0 2 0
def 0 0 4
should be
abc 1 2 3
def 0 2 4
Lets improve this query and save the joins with conditional aggregation:
SELECT t.sedol,
MAX(CASE WHEN t.name = 'B' THEN t.wgt END) as [b],
MAX(CASE WHEN t.name = 'J' THEN t.wgt END) as [j],
MAX(CASE WHEN t.name = 'S' THEN t.wgt END) as [s]
FROM YourTable t
GROUP BY t.sedol

How to group by using multiple conditions

I have the following table
Type SubType value
A 1 1
A 2 2
A 3 3
A 4 4
B 1 1
B 2 2
B 3 3
C 1 1
C 2 2
C 3 3
C 4 4
I want to group by all rows except where Type=A and the output should like below
Type Sum
A1 1
A2 2
A3 3
A4 4
B 6
C 10
Is it possible to group by few rows on one condition and others on a different condition?
Yes, you have to write an expression that creates the group definition:
Select case When Type = 'A' then type + ltrim(str(subtype, 9))
Else Type End Type, Sum(Value) Sum
From table
Group By case When Type = 'A' then type + ltrim(str(subtype, 9))
Else Type End
Yes, you can GROUP BY a CASE expression;
SELECT CASE WHEN type='A'
THEN type+CAST(subtype AS VARCHAR(MAX))
ELSE type END [Type],
SUM(value) [Sum]
FROM mytable
GROUP BY CASE WHEN type='A'
THEN type+CAST(subtype AS VARCHAR(MAX))
ELSE type END
ORDER BY [Type]
An SQLfiddle to test with.
In SQL Server 2012, you can use CONCAT without the cast, which simplifies the query somewhat.
Another option. Split the logic into the 2 cases:
SELECT Type + CAST(subtype AS VARCHAR(MAX)) AS Type,
SUM(Value) AS Sum
FROM mytable
WHERE Type = 'A'
GROUP BY Type, Subtype
UNION ALL
SELECT Type,
SUM(Value)
FROM mytable
WHERE Type <> 'A'
GROUP BY Type
ORDER BY Type ;
Tested at SQL-Fiddle (thnx to #Joachim Isakkson)

Exclude value of a record in a group if another is present

In the example table below, I'm trying to figure out a way to sum amount over id for all marks where mark 'C' doesn't exist within an id. When mark 'C' does exist in an id, I want the sum of amounts over that id, excluding the amount against mark 'A'. As illustration, my desired output is at the bottom. I've considered using partitions and the EXISTS command, but I'm having trouble conceptualizing the solution. If any of you could take a look and point me in the right direction, it would be greatly appreciated :)
sample table:
id mark amount
------------------
1 A 1
2 A 3
2 B 2
3 A 2
4 A 1
4 B 3
5 A 1
5 C 3
6 A 2
6 C 2
desired output:
id sum(amount)
-----------------
1 1
2 5
3 2
4 4
5 3
6 2
select
id,
case
when count(case mark when 'C' then 1 else null end) = 0
then
sum(amount)
else
sum(case when mark <> 'A' then amount else 0 end)
end
from sampletable
group by id
Here is my effort:
select id, sum(amount) from table t where not t.id = 'A' group by id
having id in (select id from table t where mark = 'C')
union
select id, sum(amount) from table t where t.id group by id
having id not in (select id from table t where mark = 'C')
SELECT
id,
sum(amount) AS sum_amount
FROM atable t
WHERE mark <> 'A'
OR NOT EXISTS (
SELECT *
FROM atable
WHERE id = t.id
AND mark = 'C'
)
GROUP BY
id
;

sql combine two subqueries

I have two tables. Table A has an id column. Table B has an Aid column and a type column. Example data:
A: id
--
1
2
B: Aid | type
----+-----
1 | 1
1 | 1
1 | 3
1 | 1
1 | 4
1 | 5
1 | 4
2 | 2
2 | 4
2 | 3
I want to get all the IDs from table A where there is a certain amount of type 1 and type 3 actions. My query looks like this:
SELECT id
FROM A
WHERE (SELECT COUNT(type)
FROM B
WHERE B.Aid = A.id
AND B.type = 1) = 3
AND (SELECT COUNT(type)
FROM B
WHERE B.Aid = A.id
AND B.type = 3) = 1
so on the data above, just the id 1 should be returned.
Can I combine the 2 subqueries somehow? The goal is to make the query run faster.
Does postgres support CTEs?
WITH counts (Counts, Type, Aid) as (
select count(type), type
from b group by Type, Aid
)
select id
from A
join Counts B1 on b1.Aid = a.id and b1.type = 1
join Counts B3 on b3.Aid = a.id and b3.type = 3
where
b1.counts = 3 and b3.counts = 1
I'd suggest comparing the execution plans, but I suspect it would be similar since everything should get collapsed before execution.
Select ...
From A
Join (
Select B.Id
, Sum ( Case When B.Type = 1 Then 1 Else 0 End ) As Type1Count
, Sum ( Case When B.Type = 3 Then 1 Else 0 End ) As Type3Count
From B
Where B.Type In(1,3)
Group By B.Id
) As Z
On Z.Id = A.Id
Where Z.Type1Count = 3
And Z.Type3Count = 1
This works in TSQL, does it work in Postgres?
SELECT A.ID
FROM A
WHERE A.ID in
(
SELECT AID
FROM B
GROUP BY AID
HAVING
SUM(CASE WHEN Type = 1 THEN 1 ELSE 0 END) = 3
OR SUM(CASE WHEN Type = 3 THEN 1 ELSE 0 END) = 1
)
Another alternative:
SELECT DISTINCT Aid FROM (
SELECT Aid,type,count(*) as n from B
GROUP BY Aid,type, ) as g
WHERE ( g.n=1 AND g.type = 3 )
OR ( g.n=3 AND g.type = 1 )
I doubt this will perform better than your original, though.
You seem to be doing the best strategy: counting only the candidate rows.
Perhaps some redundant prefiltering might help:
SELECT DISTINCT Aid FROM (
SELECT Aid,type,count(*) as n from B
WHERE g.type = 3 OR g.type = 1 -- prefilter
GROUP BY Aid,type, ) as g
WHERE ( g.n=1 AND g.type = 3 )
OR ( g.n=3 AND g.type = 1 )