SQL Query with CASE and group by - sql

I have a Feature Table where each feature is identified by its ID(DB column) and Bugs table where each feature has one to many relation ship with bugs table.
Feature Table has columns
id Description
Bugs Table has columns
ID Feature_ID Status
I will consider a bug as opened if its state is either 0 or 1 and as closed if Status is 2.
I am trying write a query which indicates whether a Feature can be considered as passed or failed based on it's Status.
select F.ID
CASE WHEN count(B.ID) > 0 THEN 'FAIL'
ELSE 'PASS'
END as FEATURE_STATUS
from Feature F,
Bugs B
where B.Status in (0,1)
group by F.ID;
My query always gives the Failed Features but not passed, how can modify my query to return both?

It sounds like you want something like
SELECT f.id,
(CASE WHEN open_bugs = 0
THEN 'PASS'
ELSE 'FAIL'
END) feature_status,
open_bugs,
closed_bugs
FROM (SELECT f.id,
SUM( CASE WHEN b.status IN (0,1)
THEN 1
ELSE 0
END) open_bugs,
SUM( CASE WHEN b.status = 2
THEN 1
ELSE 0
END) closed_bugs
FROM feature f
JOIN bugs b ON (f.id = b.feature_id)
GROUP BY f.id)

SELECT F.ID,
CASE WHEN SUM(CASE WHEN B.ID IN (0, 1) THEN 1 ELSE 0 END) > 0 THEN 'Fail'
ELSE 'Success' END AS FEATURE_STATUS
from Feature F
JOIN Bugs B ON B.Feature_ID = F.ID
group by F.ID

Related

SQL COUNT return multiple rows

I have those two Tables:
tblCommentReactions
id
Liked
CommentID
1
0
1
2
1
1
3
1
1
4
0
2
1 is Like and 0 is dislike.
tblComments:
id
userID
message
1
1
message 1
2
1
message 2
3
2
message 1
I tried to select all comments and Count the dislikes and likes and give the result in the same Row.
SELECT c.ID as CommentID, c.message,
COUNT(case when Liked = 1 AND r.CommentID = c.ID then 1 else null end) as likes,
COUNT(case when Liked = 0 AND r.CommentID = c.ID then 1 else null end) as dislikes
FROM tblcomments as c LEFT JOIN tblcommentreactions as r ON c.ID = r.CommentID
WHERE c.userID = 1;
Expected Output should be:
CommentID
message
likes
dislikes
1
message 1
2
1
2
message 2
0
1
On my Return it counts everything and only returns the first message. Could you tell me what i need to change in my request, to get my expected output?
There are two issues in your query:
you have no GROUP BY clause in presence of non-aggregated fields inside the SELECT clause, which will bring you have an error fired by the DBMS in the best case scenario, no error but random/subtle semantic errors in the worst one.
you are attempting to filter your rows (the WHERE condition) before the aggregation is applied.
In order to solve:
the first problem, you need to add the GROUP BY clause with the two missing selected fields, namely "c.ID" and "c.message"
the second problem, you need to transform your current WHERE clause into an HAVING one (as long as this one applies after the aggregation has been carried out) and add the checked field, namely "c.userID", inside the GROUP BY clause, as long as it is a field that was selected along with the fields in the SELECT clause.
SELECT c.ID as CommentID,
c.message,
COUNT(CASE WHEN Liked = 1 THEN 1 END) AS likes,
COUNT(CASE WHEN Liked = 0 THEN 1 END) AS dislikes
FROM tblComments AS c
LEFT JOIN tblCommentReactions AS r
ON c.ID = r.CommentID
GROUP BY c.ID,
c.message,
c.userID
HAVING c.userID = 1
Minor fixes on the CASE construct that doesn't require "AND r.CommentID = c.ID" as already pointed in the comments section, but also the non-required "ELSE NULL" condition, that is considered by PostgreSQL as default for this construct.
Here's a demo in MySQL, though this should work in the most common DBMS' more or less.
Try using group by clause e.g
select cr.CommentID ,c.message,
COUNT(case when Liked = 1 AND cr.CommentID = c.ID then 1 else null end) as likes ,
COUNT(case when Liked = 0 AND cr.CommentID = c.ID then 1 else null end) as dislikes
from [tblCommentReactions] cr inner join tblComments c on cr.CommentID = c.id
group by cr.CommentID , c.message
SELECT c.Id, COUNT(cr1.Liked), COUNT(cr2.Liked)
FROM Comments c
LEFT JOIN CommentReactions cr1 ON cr1.CommentId = c.Id AND cr1.Liked = 0
LEFT JOIN CommentReactions cr2 ON cr2.CommentId = c.Id AND cr2.Liked = 1
GROUP BY c.Id

Create overview table with true false values based on rows exist in child table

I have a table with cases, and another table with notifications.
For simplicity let's say the case table contains
id int
name nvarchar(100)
The notification table contains:
id int
caseid int
notificationtype string
Notification types can be either 'standard' or 'critical'.
I'd like an sql that can give me an overview for each case, and if they have any critical or standard notifications.
So a result like this:
CaseId CaseName StdNotification CriticalNotification
1 Test case yes no
I tried this SQL:
select distinct case.id as CaseId,
case.name as CaseName,
notifications.notificationtype,
case notifications when 'standard' then 'yes' else 'no' end as StdNotification,
case notifications when 'critical' then 'yes' else 'no' end as CriticalNotification
from cases
inner join notifications on Notifications.caseid = case.id
But this gives me duplicate rows for each combination
CaseId CaseName StdNotification CriticalNotification
1 Test case yes no
1 Test case no yes
So, how do I construct a sql that wil make some kind of "sum" and only return one row for each case?
You don't want distinct. You want group by. Your data structure suggests that a given case could have more than one notification, so I would go with counts using conditional aggregation:
select c.id as CaseId, c.name as CaseName,
sum(case when n.notificationtype = 'Standard' then 1 else 0 end) as NumStandard,
sum(case when n.notificationtype = 'Critical' then 1 else 0 end) as numCritical
from cases c left join
notifications n
on n.caseid = c.id
group by c.id, c.name;
You can convert these to "yes" and "no" using another case.
Also, note that I changed the inner join to a left join, so you'll get cases that have no notifications at all.
SELECT C.id as CaseId,
C.name as CaseName,
IIF(n1.notificationtype IS null, 'no', 'yes') as StdNotification,
IIF(n2.notificationtype IS null, 'no', 'yes') as CriticalNotification
FROM [case] C
left join notification n1
on C.id = n1.caseid and n1.notificationtype = 'standard'
left join notification n2
on C.id = n2.caseid and n2.notificationtype = 'critical'

incorrect joins - 1 row per result set

I trying to determine which people in my databases have either unsubscribed from my news letters, which people have bad email addresses and which dont have either. I have activities activities for both iUnsub' and 'iBadEmail'.
the code i wrote was
select distinct
n.id,
'Unsubscribe' =
case
when a.activity_type = 'iUnsub' then '1'
end,
'Bad Email' =
case
when a.activity_type = 'iBadEmail' then '1'
end
from name n
left join activity a on n.id = a.id
where n.id in
(
'1002421',
'1005587',
'1009073',
'1001102'
)
the results i receive creates 2 results for each id
id Unsubscribe Bad Email
1001102 NULL NULL
1002421 NULL NULL
1002421 1 NULL
1005587 NULL NULL
1005587 1 NULL
1009073 NULL 1
1009073 NULL NULL
i would like to the code to only give me one row for each id like below
id Unsubscribe Bad Email
1001102 NULL NULL
1002421 1 NULL
1005587 1 NULL
1009073 NULL 1
The problem is that you have multiple activity rows 3 or your names, and you are returning a row in the result for each activity. Name 1001102 either has no or only one activity which is neither Unsub or BadEmail.
select n.Id,
sum(case when a.activity_id = 'iUnSub' then 1 else 0 end) UnSub,
sum(case when a.activity_id = 'iBadEmail' then 1 else 0 end) BadEmail
from name
left outer join activity a on n.id = a.id
where a.activity
and n.id in
(
'1002421',
'1005587',
'1009073',
'1001102'
)
group by n.Id
This will give you a non-zero figure if UnSubbed or BadEmail, and if both are 0, then it's presumably OK.
The left outer join is for cases whene a name has no activity rows. If you don't include that then they will not be included in the output. If that's fine, change it to an inner join.

Join using combined conditions on one join table

I have join a table joining songs to genres. The table has a 'source' column that's used to identify where the genre was found. Genres are found from blogs, artists, tags, and posts.
So,
songs | song_genre | genres
id | song_id, source, genre_id | id
What I want to build is a song SELECT query that works something like this, given I already have a genre_id:
IF exists song_genre with source='artist' AND a song_genre with source='blog'
OR exists song_genre with source='artist' AND a song_genre with source='post'
OR exists song_genre with source='tag'
I'm was going to do it by doing a bunch of joins, but am sure I'm not doing it very well.
Using Postgres 9.1.
kgu87's query is correct, but likely produces a relatively expensive plan with the numerous counts over subselects. All those counts can be accumulated with one pass over the genre table with cases on source and a group by song_id. Without sample data it's hard to say whether this is faster, but I suspect it's likely. I think it's simpler at any rate.
select g.song_id
from song_genre g
group by g.song_id
having
( sum(case when g.source = 'tag' then 1 else 0 end) > 0 )
or
( sum(case when g.source = 'artist' then 1 else 0 end) > 0
and (
sum(case when g.source = 'blog' then 1 else 0 end) > 0
or
sum(case when g.source = 'post' then 1 else 0 end) > 0
)
)
select id
from
(
select distinct
id,
(
select
count(*) from
song_genre b
where a.id = b.song_id
and b.source = 'artist'
) as artist,
(
select
count(*) from
song_genre b
where a.id = b.song_id
and b.source = 'blog'
) as blog,
(
select
count(*) from
song_genre b
where a.id = b.song_id
and b.source = 'post'
) as post,
(
select
count(*) from
song_genre b
where a.id = b.song_id
and b.source = 'tag'
) as tag
from songs A
) AA
where
(AA.artist > 0 AND AA.blog > 0)
OR
(AA.artist > 0 AND AA.post > 0)
OR
(AA.tag > 0)

In Oracle how to search with sub conditions on same column

In Oracle how do I find Cars which must have Feature1 and have at lest, one out of Feature2 or Feature3. Sample table and expected result should look like below screenshot. Thanks Kiran
This should work:
select t1.car, t1.feature
from yourtable t1
inner join
( -- inner select returns the cars with the Feature1 and Feature2 or Feature3
select car, feature
from yourtable
where feature = 'Feature1'
and exists (select car
from yourtable
where feature in ('Feature2', 'Feature3'))
) t2
on t1.car = t2.car
where t1.feature in ('Feature1', 'Feature2', 'Feature3') -- this excludes any other features
see SQL Fiddle with Demo
I like to do this with GROUP BY and HAVING:
select car
from t
group by car
having max(case when feature = 'Feature1' then 1 else 0 end) = 1 and
max(case when feature in ('Feature1', 'Feature2') then 1 else 0 end) = 1
This query returns the cars. To get the featuers as well, you have to join tis back in:
select t.*
from (select car
from t
group by car
having max(case when feature = 'Feature1' then 1 else 0 end) = 1 and
max(case when feature in ('Feature1', 'Feature2') then 1 else 0 end) = 1
) c join
t
on t.car = c.car
I like this method, because the same idea can be used for handling many different similar queries -- AND conditions, OR conditions, different subgroups, and different counts.