How do I return a row with 0 if the group by has 0 records? - sql

I have this alert_levels table:
| id | levels |
-----------------
| 1 | critical |
| 2 | error |
| 3 | warning |
| 4 | info |
Then I have this alerts table
| id | alert_time | alert_level_id | alert_type |
--------------------------------------------------------------
| 1 | 2020-03-01 08:01:00.000 | 4 | Type 1 |
| 2 | 2020-03-03 10:58:00.000 | 4 | Type 1 |
| 3 | 2020-03-17 09:05:00.000 | 4 | Type 2 |
| 4 | 2020-03-21 21:03:00.000 | 4 | Type 2 |
| 5 | 2020-03-27 23:10:00.000 | 4 | Type 1 |
| 6 | 2020-04-10 05:49:00.000 | 4 | Type 2 |
| 7 | 2020-04-10 06:29:00.000 | 4 | Type 2 |
| 8 | 2020-04-14 18:56:00.000 | 4 | Type 2 |
| 9 | 2020-04-19 22:34:00.000 | 4 | Type 2 |
...
The alert_level_id in the alerts table is a foreign key of id from the alert_levels table.
What I want is to count the number of occurences of each alert_type grouped by the alert_level_id whithin a chosen time period. And if there is no occurency then it should show 0.
This is how it should look like:
| alert_level_id | type_1_count | type_2_count | total_count|
-------------------------------------------------------------
| 1 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 |
| 3 | 0 | 0 | 0 |
| 4 | 9 | 130 | 139 |
I've tried something like this:
SELECT al.id,
count(CASE WHEN alert_type = 'Type 1' THEN 1 END) type_1_count,
count(CASE WHEN log_type = 'Type 2' THEN 1 END) type_2_count,
count(CASE WHEN log_type = 'Type 1' OR log_type = 'Type 2' THEN 1 END) total_count
FROM alert_levels al
LEFT JOIN alerts a ON al.id = a.alert_level_id
WHERE a.alert_time >= ? AND a.alert_time < ?
GROUP BY al.id
ORDER BY al.id ASC;
The first thing with this query is that I feel like there is a simpler query for this, and secondly if there is only alerts with a an alert_level_id 4 in the chosen period, it only retuns one the row with that alert level. But I always want all 4 rows returned..

In Postgres, you can use filter for conditional aggregation:
SELECT al.id,
count(*) FILTER (WHERE a.alert_type = 'Type 1') as type_1_count,
count(*) FILTER (WHERE a.alter_type = 'Type 2') as type_2_count,
COUNT(a.id) as total_count
FROM alert_levels al LEFT JOIN
alerts a
ON al.id = a.alert_level_id AND
a.alert_time >= ? AND a.alert_time < ? AND
a.type in ('Type 1', 'Type 2')
GROUP BY al.id
ORDER BY al.id ASC;
Also note the conditions that have been moved to the ON clause.

Related

How to assign duplicate increment in SQL?

While going through SQL columns, if we find text match "NEW" in Calc column, update the incrementing a count starting with 1 in Results column.
It should look like this on the output:
The following uses an id column to resolve the order issue. Replace that with your corresponding expression. This also addresses the requirement to start the display sequence with 1 and also show 0 for the 'NEW' rows.
The SQL (updated):
SELECT logs.*
, CASE WHEN text = 'NEW' THEN 0
ELSE
COALESCE(SUM(CASE WHEN text = 'NEW' THEN 1 END) OVER (PARTITION BY xrank ORDER BY id)+1, 1)
END AS display
FROM logs
ORDER BY id
The result:
+----+-------+------+---------+
| id | xrank | text | display |
+----+-------+------+---------+
| 1 | 1 | A | 1 |
| 2 | 1 | B | 1 |
| 3 | 1 | C | 1 |
| 4 | 1 | NEW | 0 |
| 5 | 1 | D | 2 |
| 6 | 1 | Q | 2 |
| 7 | 1 | B | 2 |
| 8 | 1 | NEW | 0 |
| 9 | 1 | D | 3 |
| 10 | 1 | Z | 3 |
| 11 | 2 | A | 1 |
| 12 | 2 | B | 1 |
| 13 | 2 | C | 1 |
| 14 | 2 | NEW | 0 |
| 15 | 2 | D | 2 |
| 16 | 2 | Q | 2 |
| 17 | 2 | B | 2 |
| 18 | 2 | NEW | 0 |
| 19 | 2 | D | 3 |
| 20 | 2 | Z | 3 |
+----+-------+------+---------+
You need a column that specifies the ordering for the table. With that, just use a cumulative sum:
select t.*,
1 + sum(case when Calc = 'NEW' then 1 else 0 end) over (partition by Rank_Id order by Seq) as display
from t;

Combine the data of two tables using SQL Pivot and joins for a subquery

I'm trying to create a report to find the number of users subscribed to the notification type.
I am stuck with subqueries because if these two tables
Table 1
NotificationMaster
+----+-------+
| ID | Name |
+----+-------+
| 1 | Email |
| 2 | Push |
| 3 | Call |
+----+-------+
Table 2
NotificationPreference
+------------+------------------+------------+--------------+
| ResourceID | NotificationID | IsChecked | AccountID |
+------------+------------------+------------+--------------+
| 23 | 1 | 1 1 |
| 36 | 2 | 0 2 |
| 45 | 3 | 1 3 |
| 23 | 1 | 0 1 |
| 36 | 2 | 1 2 |
| 45 | 3 | 0 3 |
| 23 | 1 | 1 1 |
| 36 | 2 | 0 3 |
| 45 | 3 | 1 3 |
+------------+------------------+--------------------------+
Expected Output
Notification Vs Resource Count
+----------+-------+------+------+
| Accountid Email | Push | Call |
+----------+-------+------+------+
| 1 | 2 | 1 | 2 |
+----------+-------+------+------+
Other Tables
AccountName
+----+-------+
| ID | Name |
+----+-------+
| 1 | Blues |
+----+-------+
| 2 | Jazz |
+----+-------+
| 3 | Rock |
+----+-------+
ResourceNames
+----------+----------------+-----------+
| Resource | Name | AccountID |
+----------+----------------+-----------+
| 23 | MJ | 1 |
| 36 | Paul | 1 |
| 45 | Jay Z | 3 |
+----------+----------------+-----------+
Progress Till Now
SELECT A.ID
,A.Name
,count(R.id) AS 'Total Resource Count'
,(SELECT count(DISTINCT np.resourceid)
FROM NotificationPreference np
INNER JOIN NotificationMaster nm ON np.notificationid = nm.id
WHERE np.accountid = A.ID
AND nm.id = 1
) AS 'Email'
FROM AccountName A
LEFT JOIN [ResourceNames] R ON A.ID = R.[AccountID]
LEFT JOIN NotificationPreference np ON np.resourceid = R.ID
GROUP BY A.ID
,A.Name
The basic pivot use conditional COUNT() :
SELECT Accountid
, COUNT( CASE WHEN nm.Name = 'Email' THEN 1 END ) as Email
, COUNT( CASE WHEN nm.Name = 'Push' THEN 1 END ) as Push
, COUNT( CASE WHEN nm.Name = 'Call' THEN 1 END ) as Call
FROM NotificationPreference np
JOIN NotificationMaster nm
ON np.NotificationID = nm.id
GROUP BY Accountid

Oracle SQL count and group by multiple fields

I am able to get the data merging two tables to get the following table.
+------------+------+--------+--------+------------+------------+
| Group Name | Type | Manger | Status | ControlOne | ControlTwo |
+------------+------+--------+--------+------------+------------+
| Group A | 1 | 1 | finish | 2 | 2 |
| Group A | 2 | 1 | open | 0 | 2 |
| Group A | 1 | 1 | finish | 0 | 0 |
| Group A | 1 | 2 | finish | 2 | 0 |
| Group B | 1 | 1 | open | 2 | 0 |
| Group B | 1 | 2 | open | 2 | 2 |
| Group B | 2 | 2 | open | 0 | 2 |
| Group B | 2 | 1 | finish | 0 | 0 |
| Group B | 1 | 1 | open | 2 | 0 |
+------------+------+--------+--------+------------+------------+
Now I need to get the total counts based on GroupName/ Type and Manager to have the output for each group in the following format:
+------------+------+-------------------------------------------------+--------------------------------------------+------------------------------+----------------------------+
| Group Name | Type | Manager1Finish | Manager1Open | Manager2Finish | Manager2Open |
+------------+------+-------------------------------------------------+--------------------------------------------+------------------------------+----------------------------+
| Group A | 1 | 2(count of finish by Group A, manager1, type 1) | 0(count of open Manager1, Type 1, Group A) | 1(count of finish Manager 2) | 0(count of open manager 2) |
| Group A | 2 | 0 | 1 | 0 | 0 |
+------------+------+-------------------------------------------------+--------------------------------------------+------------------------------+----------------------------+
Could you please help to how to achieve this?
Try with CASE WHEN:
SELECT GroupName,
TYPE,
COUNT (CASE
WHEN Manager = 1
AND status = 'Finish'
THEN
1
END)
AS Manager1Finish,
COUNT (CASE
WHEN Manager = 1
AND status = 'Open'
THEN
1
END)
AS Manager1Open,
COUNT (CASE
WHEN Manager = 2
AND status = 'Finish'
THEN
1
END)
AS Manager2Finish,
COUNT (CASE
WHEN Manager = 2
AND status = 'Open'
THEN
2
END)
AS Manager2Open
FROM tablename
GROUP BY GroupName, TYPE
select [group], [type],
sum(case when manager=1 and status='finish' then 1 else 0 end) as m1finish,
sum(case when manager=1 and status='open' then 1 else 0 end) as m1open,
sum(...etc...)
from mytable
group by [group],[type]

SQL - How to avoid the joining of three queries into one

I have a SQL script that selects assignments that a student has been assigned. In order to find out if the student completed his assignment, i use a sub-query. Once the student finishes an assignment he should be able to work on the next one.
I figure I can do this by selected the top 1 assignment that has not been completed ( 0 value). Which I can do with an additional query, of the first query, but then i would need a third query to join that query together. Is there a way i can achieve this selection of the top 1 assignment that has a value of 0, with 2 queries or less?
First Attempt
SELECT ag.group_id,
ag.title,
ac.collection_id,
ag.order,
ac.NAME,
ac.isactive,
(SELECT top 1 iscompleted
FROM student_completion
WHERE fk_collection_id = collection_id
AND fk_student_id like '404')
AS isCompleted,
FROM assignments AS ag
JOIN assignments_collection AS ac
ON ag.fk_collection_id = ac.collection_id
Order BY group_id
/*
SELECT TOP 1 isCompleted
(SELECT ag.group_id,
ag.title,
ac.collection_id,
ag.order,
ac.NAME,
ac.isactive,
(SELECT top 1 iscompleted
FROM student_completion
WHERE fk_collection_id = collection_id
AND fk_student_id like '404')
AS isCompleted,
FROM assignments AS ag
JOIN assignments_collection AS ac
ON ag.fk_collection_id = ac.collection_id
Order BY group_id)
Where isCompleted = 0
.........
*/
Data
+----------+--------------+---------------+-------+----------------------+----------+-------------+
| group_id | title | collection_id | order | name | isactive | isCompleted |
+----------+--------------+---------------+-------+----------------------+----------+-------------+
| 1 | Assingment_1 | 5 | 0 | Welcome to Linux | 1 | 0 |
| 2 | Assingment_2 | 6 | 0 | Installation | 1 | 0 |
| 3 | Assingment_3 | 9 | 1 | Intro to Bash | 1 | 0 |
| 3 | Assingment_4 | 3 | 1 | Intro to Bash part 2 | 1 | 0 |
+----------+--------------+---------------+-------+----------------------+----------+-------------+
Expected Data
+----------+--------------+---------------+-------+----------------------+----------+-------------+-----------+
| group_id | title | collection_id | order | name | isactive | isCompleted | available |
+----------+--------------+---------------+-------+----------------------+----------+-------------+-----------+
| 1 | Assingment_1 | 5 | 0 | Welcome to Linux | 1 | 0 | 1 |
| 2 | Assingment_2 | 6 | 0 | Installation | 1 | 0 | 0 |
| 3 | Assingment_3 | 9 | 1 | Intro to Bash | 1 | 0 | 0 |
| 3 | Assingment_4 | 3 | 1 | Intro to Bash part 2 | 1 | 0 | 0 |
+----------+--------------+---------------+-------+----------------------+----------+-------------+-----------+
student_completion
+---------------+------------------+-------------+
| FK_studentKey | FK_collectionKey | isCompleted |
+---------------+------------------+-------------+
| 404 | 5 | 1 |
+---------------+------------------+-------------+
Your question is extremely lacking in explanation. I am taking a shot in the dark here. Does this do what you want?
select d.*
, available = sc.isCompleted
from Data d
left join student_completion sc on sc.FK_colletionKey = d.collection_id

MySQL: Pivot + Counting

I need help with a SQL that will convert this table:
===================
| Id | FK | Status|
===================
| 1 | A | 100 |
| 2 | A | 101 |
| 3 | B | 100 |
| 4 | B | 101 |
| 5 | C | 100 |
| 6 | C | 101 |
| 7 | A | 102 |
| 8 | A | 102 |
| 9 | B | 102 |
| 10 | B | 102 |
===================
to this:
==========================================
| FK | Count 100 | Count 101 | Count 102 |
==========================================
| A | 1 | 1 | 2 |
| B | 1 | 1 | 2 |
| C | 1 | 1 | 0 |
==========================================
I can so simple counts, etc., but am struggling trying to pivot the table with the information derived. Any help is appreciated.
Use:
SELECT t.fk,
SUM(CASE WHEN t.status = 100 THEN 1 ELSE 0 END) AS count_100,
SUM(CASE WHEN t.status = 101 THEN 1 ELSE 0 END) AS count_101,
SUM(CASE WHEN t.status = 102 THEN 1 ELSE 0 END) AS count_102
FROM TABLE t
GROUP BY t.fk
use:
select * from
(select fk,fk as fk1,statusFK from #t
) as t
pivot
(COUNT(fk1) for statusFK IN ([100],[101],[102])
) AS pt
Just adding a shortcut to #OMG's answer.
You can eliminate CASE statement:
SELECT t.fk,
SUM(t.status = 100) AS count_100,
SUM(t.status = 101) AS count_101,
SUM(t.status = 102) AS count_102
FROM TABLE t
GROUP BY t.fk