SQL - Select in Select - sql

I have the below working query from which I create a stacked-column-graph.
I now want to be able to filter top 10 [Prod_Model] with the highest count of [plugin ID] in total.
I tried to create an additional column summing [Plugin ID] (vulCnt) for each product disregarding [Risk].
My efforts to use INNER JOIN or SELECT inside the SELECT failed.
My working query:
select t.Prod_Model,
count(t.[Plugin ID]) as vulCnt,
(case t.Risk
when 'Critical' then 1
when 'High' then 2
when 'Medium' then 3
when 'Low' then 4
Else 5
End) As Rsk_Levl,
t.Risk
from ************ t
where t.prod_model <>''
group by t.Prod_Model, t.Risk
order by t.Prod_Model
Result is -
|Prod_Model|vulCnt| Risk_Level| Risk |
|procut 1 | 4 | 1 | Critical|
|procut 1 | 2 | 1 | High |
|procut 1 | 6 | 1 | Medium |
|procut 1 | 1 | 1 | Low |
|procut 2 | 4 | 1 | Critical|
|procut 2 | 2 | 1 | High |
|procut 2 | 6 | 1 | Medium |
|procut 2 | 1 | 1 | Low |
Now I need top##.
Help will be much appreciated.

I think that you want a window sum to generate the additional column. You can then use it this information to rank and limit:
select *
from (
select
Prod_Model,
count([Plugin ID]) as vulCnt,
(case Risk
when 'Critical' then 1
when 'High' then 2
when 'Medium' then 3
when 'Low' then 4
Else 5
End) As Rsk_Levl,
Risk,
dense_rank() over(order by cnt desc) rn
from (
select t.*, count(*) over(partition by Prod_Model) cnt
from mytable t
where prod_model <> ''
) t
group by Prod_Model, Risk
) t
where rn <= 10
order by rn

Related

How to query and filter the minimum value in one column while keeping the other columns in SQL

How can I keep only the minimum value in one column while preserving the rest of the data in the other columns in SQL? I'm trying to filter the field 'OrderID' to exhibit the lowest order# by CustomerID and Level.
Table:
Order# |CustomerID | Level
12345 | 200 | 3
12389 | 200 | 3
12590 | 200 | 3
12790 | 200 | 4
13091 | 200 | 5
15678 | 500 | 3
15788 | 500 | 4
16100 | 500 | 4
16990 | 500 | 5
17331 | 600 | 3
17660 | 600 | 4
17700 | 600 | 5
My goal is identify the when the CustomerID hit level 3,4,5 the first time based on their Order# by having a Result table below.
All other Order#'s after they hit the level are removed and a new column Level 3, Level 4, and Level 5 are created to indicate what was the order number that met those level thresholds.
Result
CustomerID | Level 3 | Level 4| Level 5
200 | 12345 | 12790 | 13091
500 | 15678 | 15788 | 16990
600 | 17331 | 17660 | 17700
Thank you in advance for your time.
I would just suggest conditional aggregation:
SELECT CustomerID,
MIN(CASE WHEN Level = 3 THEN OrderNo END) AS Level3,
MIN(CASE WHEN Level = 4 THEN OrderNo END) AS Level4,
MIN(CASE WHEN Level = 5 THEN OrderNo END) AS Level5
FROM t
GROUP BY CustomerID
ORDER BY CustomerID;
Neither a subquery nor window functions are needed for this.
This is easy to handle using ROW_NUMBER:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CustomerID, Level ORDER BY OrderNo) rn
FROM yourTable
)
SELECT
CustomerID,
MAX(CASE WHEN Level = 3 AND rn = 1 THEN OrderNo END) AS Level3,
MAX(CASE WHEN Level = 4 AND rn = 1 THEN OrderNo END) AS Level4,
MAX(CASE WHEN Level = 5 AND rn = 1 THEN OrderNo END) AS Level5
FROM cte
GROUP BY CustomerID
ORDER BY CustomerID;
Demo

how to join or merge as one row in SQL

I have these 2 tables;
table A
| ID | Name | S_ID |
|----|--------|------|
| 1 | mark | 1 |
| 2 | john | 2 |
table B (rows are not limited to 5 and Scores could be more than 3)
| ID | S_ID | Score |
|-------------------|
| 1 | 1 | 90% |
| 2 | 1 | 80% |
| 3 | 1 | 10% |
| 4 | 2 | 10% |
| 5 | 2 | 12% |
Normally using "GROUP_CONCAT" would work but is there any way to achieve this;
You are asking for a pivot query, either with or without a fixed number of columns. Assuming the former, we can use ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY S_ID ORDER BY ID) rn
FROM tableB
)
SELECT
a.ID,
a.Name,
MAX(CASE WHEN rn = 1 THEN b.S_ID END) AS S_ID_1,
MAX(CASE WHEN rn = 1 THEN b.Score END) AS Score_1,
MAX(CASE WHEN rn = 2 THEN b.S_ID END) AS S_ID_2,
MAX(CASE WHEN rn = 2 THEN b.Score END) AS Score_2,
MAX(CASE WHEN rn = 3 THEN b.S_ID END) AS S_ID_3,
MAX(CASE WHEN rn = 3 THEN b.Score END) AS Score_3
FROM cte
GROUP BY
a.ID,
a.Name;

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;

Count each condition within group

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

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;