How to write sql query for this? - sql

My table looks like this:
id staus
1 p
1 p
1 c
2 p
2 c
I need to produce counts of rows with the statuses of 'p' and 'c' for each id, so the result I expect should look like this:
id p c
1 2 1 <-- id 1 has two rows with 'p' and one row with 'c'
2 1 1 <-- id 2 has one row with 'p' and one row with 'c'
How can i achieve this?

You can do it like this:
SELECT
id
, SUM (CASE STATUS WHEN 'p' THEN 1 ELSE 0 END) as p
, SUM (CASE STATUS WHEN 'c' THEN 1 ELSE 0 END) as c
FROM my_table
GROUP BY id
When you have more than just a few fixed items like 'p' and 'c' to aggregate, pivoting may provide a better option.

Pivot solution. Works from sql-server 2008
declare #t table(id int, staus char(1))
insert #t values( 1,'p'),( 1,'p'),( 1,'c'),( 2,'p'),( 2,'c')
SELECT id, [p], [c]
from #t
PIVOT
(count([staus])
FOR staus
in([p],[c])
)AS p
Result:
id p c
1 2 1
2 1 1

It seems that you need to do a pivot of your table, there is a simple article that I used when i faced your same problem pivot table sql server

Related

Subqueries for aggregation needs in sql

I have a table like this:
Id Date Price Item Type
1 2009-09-21 25 1 M
2 2009-08-31 16 2 T
1 2009-09-23 21 1 M
2 2009-09-03 12 3 T
I try to receive the output of ID and column of sum price mult items for type='M' and another column with same logic for type='T'
Only way how to do it for me is using multi-cte but it is kind of complex and big:
with cte as (
select distinct a.id, a.date
sum(price*a.item) as numm
from table a
where a.type='M'
group by a.id),
crx as (
select cte.id, cte.numm, sum(a.price*a.item) as numm_1 from cte
join table a on a.id=cte.id and a.date=cte.date
where a.type='T'
group by cte.id)
select * from crx
Having a certain feeling that it can be done better (for example using subqueries)-asking you how can it be done.
p.s.
SQLlite stuff would be greatly appreciated!
Thanks!
Perhaps this will help
Declare #YourTable table (Id int,Date date,Price money,Item int,Type varchar(25))
Insert into #YourTable values
(1,'2009-09-21',25,1,'M'),
(2,'2009-08-31',16,2,'T'),
(1,'2009-09-23',21,1,'M'),
(2,'2009-09-03',12,3,'T')
Select ID
,sum(case when Type='M' then Price*Item else 0 end) as M
,sum(case when Type='T' then Price*Item else 0 end) as T
From YourTable
Group By ID
Returns
ID M T
1 46.00 0.00
2 0.00 68.00

Multiple counts for each ID

I'm trying to get my head around this, but unfortunately neither of my approaches works:
I need a table with 3 columns:
ItemID
Number cases where ItemID has CostcentreID x
Number cases where ItemID has CostcentreID y
SELECT ItemID, Count1, Count2
FROM Table
Output should be like:
--ItemID--Count1--Count2
1 12 5
2 3 2
What i get when using
SELECT ItemdID, SUM(case when costc...),...
FROM Table
is:
--ItemID--Count1--Count2
1 12 0
2 3 0
due to the GROUP BY statement.
Anyway to solve this without a Cursor?
Also, a JOIN of 5 tables is needed.
Thanks in advance!
I'm not sure what you need with the joins, but here is the first part.
DECLARE #table TABLE(ItemID INT, CostCentreID CHAR(1));
INSERT INTO #table
VALUES (1,'X'),
(1,'X'),
(1,'Y'),
(2,'X'),
(2,'Y'),
(2,'Y'),
(2,'Y');
SELECT ItemID,
SUM(
CASE
WHEN CostCentreID = 'X' THEN 1 ELSE 0
END
) AS CostCentreX,
SUM(
CASE
WHEN CostCentreID = 'Y' THEN 1 ELSE 0
END
) AS CostCentreY
FROM #table
GROUP BY ItemID
Results:
ItemID CostCentreX CostCentreY
----------- ----------- -----------
1 2 1
2 1 3

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)

Checking if the row has the max value in a group

I'm trying get to find out if a row has the max value in a group. Here's really simple example:
Data
VoteCount LocationId UserId
3 1 1
4 1 2
3 2 2
4 2 1
Pseudo-query
select
LocationId,
sum(case
when UserId = 1 /* and has max vote count*/
then 1 else 0
end) as IsUser1Winner,
sum(case
when UserId = 2 /* and has max vote count*/
then 1 else 0
end) as IsUser2Winner
from LocationVote
group by LocationID
It should return:
LocationId IsUser1Winner IsUser2Winner
1 0 1
2 1 1
I also couldn't find a way to generate dynamic column names here. What would be the simplest way to write this query?
You could also do this using a Case statement
WITH CTE as
(SELECT
MAX(VoteCount) max_votes
, LocationId
FROM LocationResult
group by LocationId
)
SELECT
A.LocationId
, Case When UserId=1
THEN 1
ELSE 0
END IsUser1Winner
, Case when UserId=2
THEn 1
ELSE 0
END IsUser2Winner
from LocationResult A
inner join
CTE B
on A.VoteCount = B.max_votes
and A.LocationId = B.LocationId
Try this:
select *
from table t
cross apply (
select max(votes) max_value
from table ref
where ref.group = t.group
)votes
where votes.max_value = t.votes
but if your table is huge and has no propriate indexes performance may be poor
Another way is to get max values by groups into table variable or temp table and then join it to original table.

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
;