Count each condition within group - sql

For every unique GroupId I would like to get a count of each IsGreen, IsRound, IsLoud condition and a total number of rows.
Sample data:
-----------------------------------------------------
id | ItemId | GroupId | IsGreen | IsRound | IsLoud
----+--------+---------+---------+---------+---------
1 | 1001 | 1 | 0 | 1 | 1
2 | 1002 | 1 | 1 | 1 | 0
3 | 1003 | 2 | 0 | 0 | 0
4 | 1004 | 2 | 1 | 0 | 1
5 | 1005 | 2 | 0 | 0 | 0
6 | 1006 | 3 | 0 | 0 | 0
7 | 1007 | 3 | 0 | 0 | 0
Desired result:
----------------------------------------------------------
GroupId | TotalRows | TotalGreen | TotalRound | TotalLoud
--------+-----------+------------+------------+-----------
1 | 2 | 1 | 2 | 1
2 | 3 | 1 | 0 | 1
3 | 2 | 0 | 0 | 0
I'm using the following code to create the table, the problem I'm having is that if any of the groups have no rows that match one of the conditions that group does not appear in the final table. What is the best way to accomplish what I want to do?
SELECT total.GroupId
, total.[Count] AS TotalRows
, IsGreen.[Count] AS TotalGreen
, IsRound.[Count] AS TotalRound
, IsLoud.[Count] AS TotalLoud
FROM (
SELECT GroupId
, count(*) AS [Count]
FROM TestData
GROUP BY GroupId
) TotalRows
INNER JOIN (
SELECT GroupId
, count(*) AS [Count]
FROM TestData
WHERE IsGreen = 1
GROUP BY GroupId
) IsGreen ON IsGreen.GroupId = TotalRows.GroupId
INNER JOIN (
SELECT GroupId
, count(*) AS [Count]
FROM TestData
WHERE IsRound = 1
GROUP BY GroupId
) IsRound ON IsRound.GroupId = TotalRows.GroupId
INNER JOIN (
SELECT GroupId
, count(*) AS [Count]
FROM TestData
WHERE IsLoud = 1
GROUP BY GroupId
) IsLoud ON IsLoud.GroupId = TotalRows.GroupId

You can use count to count rows per each [GroupId] and sum to count each property .
select [GroupId]
, count([GroupId]) as [TotalRows]
, sum([IsGreen]) as [TotalGreen]
, sum([IsRound]) as [TotalRound]
, sum([IsLoud]) as [TotalLoud]
from [TestData]
group by [GroupId]

Use conditional Aggregate. Try this.
SELECT GroupId,
Count(GroupId) TotalRows,
Count(CASE WHEN IsGreen = 1 THEN 1 END) TotalGreen,
Count(CASE WHEN IsRound = 1 THEN 1 END) TotalRound,
Count(CASE WHEN IsLoud = 1 THEN 1 END) TotalLoud
FROM tablename
GROUP BY GroupId

Related

How to get count from one table which is mutually dependent to another table

I have two table
Let's name as first table: QC_Meeting_Master
Second table: QC_Project_Master I want to calculate count of problems_ID Which is mutually depend on second table
ID | QC_ID | Problems_ID |
___|_______|_____________|
1 | 1 | 2 |
2 | 1 | 7 |
ID | QC_ID | Problem_ID |
___|_______|_____________|
1 | 1 | 7 |
2 | 1 | 7 |
3 | 1 | 7 |
4 | 1 | 7 |
5 | 1 | 2 |
6 | 1 | 2 |
7 | 1 | 2 |
select COUNT(Problem_ID) from [QC_Project_Master] where Problem_ID in
(select Problems_ID from QC_Meeting_Master QMM join QC_Project_Master QPM on QMM.Problems_ID = QPM.Problem_ID)
I have to calculate Count of QC_Project_Master (problem_ID) on basis of QC_Meeting_Master (Problems_ID)
it means for first table: QC_Meeting_Master(Problems_ID) = 2,
then count should be 3
And for Second table: QC_Project_Master (Problems_ID) = 7,
then count should be 4
use conditional aggregation
select sum(case when t2.Problem_ID=2 then 1 else 0 end),
sum(case when t2.Problem_ID=7 then 1 else 0 end) from
table1 t1 join table2 t2 on t1.QC_ID=t2.QC_ID and t1.Problems_ID=t2.Problems_ID
if you need all the group count then use below
select t2.QC_ID,t2.Problems_ID, count(*) from
table1 t1 join table2 t2
on t1.QC_ID=t2.QC_ID and t1.Problems_ID=t2.Problems_ID
group by t2.QC_ID,t2.Problems_ID
As far as I understood your problem this is simple aggregation and JOIN as below:
SELECT mm.QC_ID, mm.Problem_ID, pm.cnt
FROM QC_Meeting_Master mm
INNER JOIN
(
SELECT QC_ID, Problem_ID, COUNT(*) cnt
FROM QC_Project_Master
GROUP BY QC_ID, Problem_ID
) pm
ON pm.QC_ID = mm.QC_ID AND pm.Problem_ID = mm.Problem_ID;

SQL Subquery with a single table result with NULL

I have tried to select with sub queries in only a single table. but for some reason i got a NULL result.
The table I have look like this.
| id | ItemCode | ItemAmount | Counter |
-----------------------------------------
| 1 | 001 | 1 | Counter-1 |
| 2 | 001 | 1 | Counter-2 |
| 3 | 002 | 2 | Counter-1 |
| 4 | 002 | 2 | Counter-2 |
| 5 | 002 | 1 | Counter-2 |
I have tried this SQL :
select
id,
itemCode,
(select ItemAmount where Counter = 'Counter-1') as 'Count 1 Result',
(select Counter where Counter = 'COUNTER-1') as 'Count Is 1',
(select ItemAmount where Counter = 'Counter-2') as 'Count 2 Result',
(select Counter where Counter = 'COUNTER-2') as 'Count Is 2',
from
My_Table
and the result I got is :
| id | ItemCode | Count 1 Result | Count Is 1 | Count 2 Result | Count Is 2 |
----------------------------------------------------------------------
| 1 | 001 | 1 | Counter-1 | NULL | NULL |
| 2 | 001 | NULL | NULL | 1 | Counter-2 |
| 3 | 002 | 2 | Counter-1 | NULL | NULL |
| 4 | 002 | NULL | NULL | 2 | Counter-2 |
| 5 | 002 | NULL | NULL | 1 | Counter-2 |
As you can see, i got NULL result with the NULL Value. How can i do it with the result like this :
| id | ItemCode | Count 1 Result | Count Is 1 | Count 2 Result | Count Is 2 |
-------------------------------------------------------------------------
| 1 | 001 | 1 | Counter-1 | 2 | Counter-2 |
--------------------------------------------------------------------------
| 2 | 002 | 2 | Counter-1 | 3 | Counter-2 |
--------------------------------------------------------------------------
I want to make it no NULL value anymore and there is no double Item Code with the SUM Item Amount if the counter and item code is have the same value.
Is that even possible to do it with one table ? if it is how do i do that. thanks in advance
Try conditional aggregation, something like:
SELECT min(id) id,
itemcode,
sum(CASE
WHEN counter = 'Counter-1' THEN
itemamount
ELSE
0
END) count1result,
'Counter-1' countis1,
sum(CASE
WHEN counter = 'Counter-2' THEN
itemamount
ELSE
0
END) count2result,
'Counter-2' countis2
FROM my_table
GROUP BY itemcode;
You can try to use Aggregate function condition to make it.
Here is SQL-server sample:
CREATE TABLE My_Table(
id INT,
ItemCode VARCHAR(50),
ItemAmount INT,
Counter VARCHAR(50)
);
INSERT INTO My_Table VALUES (1, '001', 1 ,'Counter-1');
INSERT INTO My_Table VALUES (2, '001', 1 ,'Counter-2');
INSERT INTO My_Table VALUES (3, '002', 2 ,'Counter-1');
INSERT INTO My_Table VALUES (4, '002', 2 ,'Counter-2');
INSERT INTO My_Table VALUES (5, '002', 1 ,'Counter-2');
Query 1:
select
ROW_NUMBER() OVER(ORDER BY itemCode) id,
itemCode,
SUM(CASE WHEN Counter = 'Counter-1' THEN ItemAmount ELSE 0 END) as 'Count 1 Result',
MAX(CASE WHEN Counter = 'COUNTER-1' THEN Counter END) as 'Count Is 1',
SUM(CASE WHEN Counter = 'Counter-2' THEN ItemAmount ELSE 0 END) as 'Count 2 Result',
MAX(CASE WHEN Counter = 'COUNTER-2' THEN Counter END) as 'Count Is 2'
from
My_Table
GROUP BY
itemCode
Results:
| id | itemCode | Count 1 Result | Count Is 1 | Count 2 Result | Count Is 2 |
|----|----------|----------------|------------|----------------|------------|
| 1 | 001 | 1 | Counter-1 | 1 | Counter-2 |
| 2 | 002 | 2 | Counter-1 | 3 | Counter-2 |
Even though I'm not sure what you are expecting with out disturbing your code I have given the query. Implement in your requirement
;WITH CTE AS (select
itemCode,
(select SUM(ItemAmount) where Counter = 'Counter-1') as 'Count 1 Result',
(select MAX(Counter) where Counter = 'COUNTER-1') as 'Count Is 1',
(select SUM(ItemAmount) where Counter = 'Counter-2') as 'Count 2 Result',
(select MAX(Counter) where Counter = 'COUNTER-2') as 'Count Is 2'
from
My_table
GROUP BY
itemCode,Counter )
Select RANK()OVER ( ORDER BY itemcode)Id,
itemCode,
MAX([Count 1 Result])[Count 1 Result],
MAX([Count Is 1])[Count Is 1],
MAX([Count 2 Result])[Count 2 Result],
MAX([Count Is 2])[Count Is 2]
from CTE
GROUP BY itemCode
For the data retrieval part the following would give your data,
by grouping on ItemCode and Counter:
select
ItemCode,
Counter,
min(Id) as Id,
sum(ItemAmount) as ItemAmount,
from
My_Table
group by ItemCode, Counter
ItemCode Counter Id ItemAmount
======== ========= === ==========
001 Counter-1 1 1
001 Counter-2 1 2
002 Counter-1 3 1
002 Counter-2 3 3
To display that as so called pivot table, rows-to-columns, there are several solutions, one generic SQL solution:
In Modern SQL:
select ItemCode,
ItemAmount filter (where Counter = 'Counter-1') as A1,
ItemAmount filter (where Counter = 'Counter-2') as A2
from (select
ItemCode,
Counter,
min(Id) as Id,
sum(ItemAmount) as ItemAmount,
from
My_Table
group by ItemCode, Counter)
group by ItemCode
If filter is still not supported in the used SQL:
select ItemCode,
sum(case when Counter = 'Counter-1' then ItemAmount end) as A1,
sum(case when Counter = 'Counter-2' then ItemAmount end) as A2,

Efficient ROW_NUMBER increment when column matches value

I'm trying to find an efficient way to derive the column Expected below from only Id and State. What I want is for the number Expected to increase each time State is 0 (ordered by Id).
+----+-------+----------+
| Id | State | Expected |
+----+-------+----------+
| 1 | 0 | 1 |
| 2 | 1 | 1 |
| 3 | 0 | 2 |
| 4 | 1 | 2 |
| 5 | 4 | 2 |
| 6 | 2 | 2 |
| 7 | 3 | 2 |
| 8 | 0 | 3 |
| 9 | 5 | 3 |
| 10 | 3 | 3 |
| 11 | 1 | 3 |
+----+-------+----------+
I have managed to accomplish this with the following SQL, but the execution time is very poor when the data set is large:
WITH Groups AS
(
SELECT Id, ROW_NUMBER() OVER (ORDER BY Id) AS GroupId FROM tblState WHERE State=0
)
SELECT S.Id, S.[State], S.Expected, G.GroupId FROM tblState S
OUTER APPLY (SELECT TOP 1 GroupId FROM Groups WHERE Groups.Id <= S.Id ORDER BY Id DESC) G
Is there a simpler and more efficient way to produce this result? (In SQL Server 2012 or later)
Just use a cumulative sum:
select s.*,
sum(case when state = 0 then 1 else 0 end) over (order by id) as expected
from tblState s;
Other method uses subquery :
select *,
(select count(*)
from table t1
where t1.id < t.id and state = 0
) as expected
from table t;

SQL - self join 'n' times with condition

I have the following table:
Id | Type
--------------------------------------------
C1C1A90D-B131-4450-B1BF-5041F36F9144 | 1
C7B1752D-FD30-445A-AD6C-51D1434607D3 | 2
3AAF8BB6-A6D4-4780-BEF9-ACBBF75A85DE | 3
--------------------------------------------
67EF1537-A22E-4D2D-AAEA-FC0D9E2B9912 | 1
546519ED-5E78-4DAD-ADFF-9DC0AA67B763 | 2
8F66A3F9-C652-4758-8E17-B4DE0B0D85A4 | 3
--------------------------------------------
-- ... and so on ... --
Now, I need a specific type of SELECT (something like this):
SELECT
[Id] AS [OneId] -- Where [Type] = 1,
[Id] AS [TwoId] -- Where [Type] = 2,
[Id] AS [ThreeId] -- Where [Type] = 3
FROM Table
This is what I've tried so far but with bad results:
SELECT
oneI.[Id] AS [OneId] -- Where [Type] = 1,
twoI.[Id] AS [TwoId] -- Where [Type] = 2,
threeI.[Id] AS [ThreeId] -- Where [Type] = 3
FROM Table AS i
INNER JOIN Table AS oneI ON
i.[Id] = oneI.[Id]
INNER JOIN Table AS twoI ON
i.[Id] = twoI.[Id]
INNER JOIN Table AS threeI ON
i.[Id] = threeI.[Id]
WHERE
oneI.[Type] = 1
AND twoI.[Type] = 2
AND threeI.[Type] = 3
Or even worse (which gave me more lots of combinations):
SELECT
oneI.[Id] AS [OneId] -- Where [Type] = 1,
twoI.[Id] AS [TwoId] -- Where [Type] = 2,
threeI.[Id] AS [ThreeId] -- Where [Type] = 3
FROM Table AS i, TABLE AS oneI, Table AS twoI, Table AS threeI
WHERE
oneI.[Type] = 1
AND twoI.[Type] = 2
AND threeI.[Type] = 3
EDIT:
I would also have another column on which I would group those Ids. So the updated table look like:
Id | GroupId | Type
------------------------------------------------------------------
C1C1A90D-B131-4450-B1BF-5041F36F9144 -- OneId | 1 | 1
C7B1752D-FD30-445A-AD6C-51D1434607D3 -- TwoId | 1 | 2
3AAF8BB6-A6D4-4780-BEF9-ACBBF75A85DE -- ThreeId | 1 | 3
------------------------------------------------------------------
67EF1537-A22E-4D2D-AAEA-FC0D9E2B9912 -- OneId | 2 | 1
546519ED-5E78-4DAD-ADFF-9DC0AA67B763 -- TwoId | 2 | 2
8F66A3F9-C652-4758-8E17-B4DE0B0D85A4 -- ThreeId | 2 | 3
----------------------------------------------------------------
So, I would need the following result:
OneI | TwoI | ThreeI | GroupId
------------------------------------------------------
OneId | TwoId | ThreeId | 1
OneId | TwoId | ThreeId | 2
EDIT:
I have one more special case - [Type] column can repeat:
Id | GroupId | Type
-----------------------------------------------------------------------
C1C1A90D-B131-4450-B1BF-5041F36F9144 -- OneId | 1 | 1
C7B1752D-FD30-445A-AD6C-51D1434607D3 -- TwoId | 1 | 2
3AAF8BB6-A6D4-4780-BEF9-ACBBF75A85DE -- ThreeId | 1 | 3
FEB4A345-FEA0-4530-AE52-6CF4F07E37BA -- OtherThreeId | 1 | 3
-----------------------------------------------------------------------
67EF1537-A22E-4D2D-AAEA-FC0D9E2B9912 -- OneId | 2 | 1
546519ED-5E78-4DAD-ADFF-9DC0AA67B763 -- TwoId | 2 | 2
8F66A3F9-C652-4758-8E17-B4DE0B0D85A4 -- ThreeId | 2 | 3
----------------------------------------------------------------
And now the result would be:
OneI | TwoI | ThreeI | GroupId
------------------------------------------------------
OneId | TwoId | ThreeId | 1
OneId | TwoId | OtherThreeId | 1
OneId | TwoId | ThreeId | 2
sqlfiddle
Well, this would required some sequential ordering columns, but you could also express this as
select max(case when [Type] = 1 then Id end) OneId,
max(case when [Type] = 2 then Id end) TwoId,
max(case when [Type] = 3 then Id end) ThreeId
from (select *,
row_number() over (order by (select 1)) Seq
from table
) t
group by (Seq - [Type]);
EDIT :- However, if you want to include group also then use them as in select statement as
select (Seq - [Type]) as GroupId,
max(case when [Type] = 1 then 'OneId' end) OneI,
max(case when [Type] = 2 then 'TwoId' end) TwoI,
max(case when [Type] = 3 then 'ThreeId' end) ThreeI
from (select *,
row_number() over (order by (select 1)) Seq
from table
) t
group by (Seq - [Type]);
For your updated table you can directly use table with group by clause with your GroupId column as then you don't use row_number() function and subquery
select max(case when [Type] = 1 then 'OneId' end) OneI,
max(case when [Type] = 2 then 'TwoId' end) TwoI,
max(case when [Type] = 3 then 'ThreeId' end) ThreeI,
GroupId
from table t
group by GroupId;
Demo
You need an Grp attribute then specifies which rows are together in one group. Something like this
Id | Type | Grp
----------------------------------------------------
C1C1A90D-B131-4450-B1BF-5041F36F9144 | 1 | 1
C7B1752D-FD30-445A-AD6C-51D1434607D3 | 2 | 1
3AAF8BB6-A6D4-4780-BEF9-ACBBF75A85DE | 3 | 1
---------------------------------------------------
67EF1537-A22E-4D2D-AAEA-FC0D9E2B9912 | 1 | 2
546519ED-5E78-4DAD-ADFF-9DC0AA67B763 | 2 | 2
8F66A3F9-C652-4758-8E17-B4DE0B0D85A4 | 3 | 2
--------------------------------------------------
Then you can use conditional aggregation like this
SELECT
MAX(CASE WHEN [Type] = 1 THEN [Id] END) AS [OneId],
MAX(CASE WHEN [Type] = 2 THEN [Id] END) AS [TwoId],
MAX(CASE WHEN [Type] = 3 THEN [Id] END) AS [ThreeId]
FROM YourTable
GROUP BY Grp
Looks like this would do the job for you
; WITH CTE
AS
(
SELECT
Id,
[Type]
FROM YourTable
WHERE [Type]
IN
(
1,2,3,4,5
)
)
SELECT
*
FROM CTE
PIVOT
(
MAX(id)
FOR [Type] IN
(
[1],[2],[3],[4],[5]
)
)Pvt

Efficent way to generate a summery table in SQL. Please see explanation

I am very new to sql.I need some help in generating summery information
MemberTable
MonthID | UserID | TeamID
-----------------------------
1 | 1 | 1
1 | 2 | 1
1 | 3 | 1
1 | 4 | 1
1 | 5 | 2
1 | 6 | 2
1 | 7 | 2
AND
ReportTable
ID* | MonthID | UserID | IsSend
-----------------------------------
1 | 1 | 2 | False
2 | 1 | 3 | True
3 | 1 | 5 | True
I want to generate a summery like the following
TeamID | Total Count | Send Count | Not Send Count
-----------------------------------------------------------
1 | 4 | 1 | 3
2 | 3 | 1 | 2
Total Count : No of users in a team
Send Count : Total User in a team with IsSend = True
Not Send Count : Total Count - Send Count
What would be the efficent way?
Give this a try:
select mt.teamId, count(*) totalCount,
count(case when rt.isSend = 'True' then 1 end) sendCount,
count(case when rt.isSend != 'True' then 1 end) notSendCount
from memberTable mt
join reportTable rt on mt.userId = rt.userId
group by mt.teamId
Note that your expected result does not reflect your data. The result based on your data should be:
+--------+------------+-----------+--------------+
| TEAMID | TOTALCOUNT | SENDCOUNT | NOTSENDCOUNT |
+--------+------------+-----------+--------------+
| 1 | 2 | 1 | 1 |
| 2 | 1 | 1 | 0 |
+--------+------------+-----------+--------------+
select MT.TeamID,
count(distinct MT.UserID) as "Total Count",
count(distinct case when RT.IsSend = 1 then MT.UserID end) as "Send Count",
count(distinct MT.UserID) - count(distinct case when RT.IsSend = 1 then MT.UserID end) as "Not Send Count"
from MemberTable as MT
left outer join ReportTable as RT
on MT.MonthID = RT.MonthID and
MT.UserID = RT.UserID
group by MT.TeamID
Result:
TeamID Total Count Send Count Not Send Count
----------- ----------- ----------- --------------
1 4 1 3
2 3 1 2
Try here: https://data.stackexchange.com/stackoverflow/query/66347
Without havign the tables to try this on, I can't check that this will work, but this shoul get you most of the way:
SELECT TeamID, count(userID) as "Total count", Sum(IsSend) as "Send Count" FROM MemberTable JOIN ReportTable ON UserID GROUP BY TeamID;