SQL COUNT return multiple rows - sql

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

Related

sqlite return same value on left join

in below my sqlite command i want to get count of barcoeds when that's equals with sessions.id
select sessions.id, sessions.session_name, sessions.session_type,sessions.date_time, count(barcodes.id) as barcode_count
from sessions left join barcodes
on sessions.id = barcodes.session_id
group by barcodes.id
order by sessions.id desc
this command works, but that return more data with same value, for example if data is one, that return more than 3, but really i have one row
0 = {_List} size = 5
0 = 11
1 = "111"
2 = 2
3 = "1398/05/14 ساعت: 08:43"
4 = 1
1 = {_List} size = 5
0 = 11
1 = "111"
2 = 2
3 = "1398/05/14 ساعت: 08:43"
4 = 1
2 = {_List} size = 5
0 = 11
1 = "111"
2 = 2
3 = "1398/05/14 ساعت: 08:43"
4 = 1
I think you want one row per session. So, your query is aggregating by the wrong column:
select s.id, s.session_name, s.session_type,
s.date_time, count(b.id) as barcode_count
from sessions s left join
barcodes b
on s.id = b.session_id
group by s.id
---------^ sessions not barcode
order by s.id desc;
You might find this also easy to do with a correlated subquery:
select s.*,
(select count(*) from barcodes b where b.session_id = s.id)
from sessions s;
The use of table aliases in these queries makes them easier to write and to read.
First count the ids for each session_id in table barcodes and then join to sessions:
select
s.id, s.session_name, s.session_type, s.date_time,
coalesce(b.barcode_count, 0) barcode_count
from sessions s left join (
select session_id, count(id) barcode_count
from barcodes
group by session_id
) b on s.id = b.session_id
order by s.id desc
I guess id is unique in the table barcodes so there is no need for count(distinct id).

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.

SQL join to table with 3 possible cases: table can have no records, match 1 or more, records or require all records found to match

I have a 2 tables:
Questions table with Question ID
Part Table:
Question ID
Part ID
BAllPartsRequired
The user will select some parts (or may select none) and depending on what was selected certain questions will be displayed.
I want to join the 2 tables for 3 scenerios but do them all in 1 query. I can write each scenerio individually (EDIT I thought I could but scenario 3 I can not get to work where it requires all found in part table to be selected) but can not figure out how to get them all in 1 query (I have done something similar before but cant remember how).
If no parts exist in part table for that question retruen the question
If any part selected exists in part table return question (i.e. user selects 1 part and 5 parts are associated to that question then the question will match and be returned). BAllPartsRequired = false
If user selects parts and ALL of the parts are associated to the question the question is returend but if NOT all parts are selected by user the question is not returend (i.e. user selects 3 parts and there are 4 parts in table then the user will not see the question, but if the user selectes all 4 parts the question will be shown). BAllPartsRequired = true
I am an advanced SQL programmer but this is eluding me and I know I have done this before but how do I get it to work in 1 query, do I do a nested join, a left join, a case on the where statement or something else.
Sample Data:
Question Form Association:
NFormAssociationID NQuestionID FormType
1 1 PAEdit
2 2 PAEdit
3 3 PAEdit
4 4 PAEdit
Question Part Form Association Table:
ID NFormAssociationID PartNumber BAllPartsRequired
1 1 1 0
2 2 2 1
3 2 3 1
Query without the new parts table added:
Select ROW_NUMBER() OVER(ORDER BY QL.NOrderBy) AS RowNumber,
QL.NQuestionID, QL.FieldName, QL.Question, QL.BRequired, QFL.FormFieldType, QFL.SingleMultipleSM,
QFL.CSSStyle
FROM dbo.QuestionFormAssociation QA WITH (NOLOCK)
INNER JOIN dbo.QuestionLookup QL WITH (NOLOCK) ON QA.NQuestionID = QL.NQuestionID
INNER JOIN dbo.QuestionFieldTypeLookup QFL WITH (NOLOCK) ON QL.NFieldTypeID = QFL.NFieldTypeID
WHERE QA.BActive = 1 AND QL.BActive = 1 AND QFL.BActive=1
AND QA.FormType = 'PAEdit'
ORDER BY QL.NOrderBy
Simple query using new table
Select ID
FROM dbo.QuestionPartFormAssociation
WHERE BAllPartsRequired = 1
AND PartNumber IN ('1', '2') --'1', '2', '3')
It sounds like you are trying to find the eligible questions, based on some criteria.
In this sitatuion, it is best to summarize first at the question level, and then apply logic to those summaries. Here is an example:
select q.questionid
from (select q.questionid,
max(case when qp.questionid is null then 1 else 0 end) as HasNoParts,
sum(case when qp.partid in (<user parts>) then 1 else 0 end) as NumUserParts,
count(qp.questionid) as NumParts,
max(qp.AllPartsRequired) as AreAllPartsRequired
from question q left outer join
questionpart qp
on q.questionid = qp.questionid
group by q.questionid
) q
where HasNoParts = 1 or -- condition 1
AreAllPartsRequired = 0 and NumUserParts > 0 or -- condition 2
AreAllPartsRequired = 1 and NmUserParts = NumParts -- condition 3
I've simplified the table and column names to make the logic clearer.
updated with full answer from OP:
Select ROW_NUMBER() OVER(ORDER BY QL.NOrderBy) AS RowNumber,
QL.NQuestionID, QL.FieldName, QL.Question, QL.BRequired, QFL.FormFieldType, QFL.SingleMultipleSM,
QFL.CSSStyle
FROM dbo.QuestionFormAssociation QA WITH (NOLOCK)
INNER JOIN dbo.QuestionLookup QL WITH (NOLOCK) ON QA.NQuestionID = QL.NQuestionID
INNER JOIN dbo.QuestionFieldTypeLookup QFL WITH (NOLOCK) ON QL.NFieldTypeID = QFL.NFieldTypeID
INNER JOIN (
select q.NFormAssociationID,
max(case when qp.NFormAssociationID is null then 1 else 0 end) as HasNoParts,
sum(case when qp.PartNumber in ('1','2','3') then 1 else 0 end) as NumUserParts,
count(qp.NFormAssociationID) as NumParts,
qp.BAllPartsRequired
from QuestionFormAssociation q
left outer join QuestionPartFormAssociation qp on q.NFormAssociationID = qp.NFormAssociationID
AND QP.BActive = 1
WHERE Q.FormType = 'PAEdit'
AND Q.BActive = 1
group by q.NFormAssociationID, qp.BAllPartsRequired
) QSUB ON QA.NFormAssociationID = QSUB.NFormAssociationID
WHERE QA.BActive = 1 AND QL.BActive = 1 AND QFL.BActive=1
AND (
QSUB.HasNoParts = 1 -- condition 1
OR (QSUB.BAllPartsRequired = 0 and QSUB.NumUserParts > 0) -- condition 2
OR (QSUB.BAllPartsRequired = 1 and QSUB.NumUserParts = QSUB.NumParts) -- condition 3
)
ORDER BY QL.NOrderBy

SQL Query with CASE and group by

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

SQL Server : zero values with COUNT(*)

I have a table #MemberAttribute
MemberID AttributeID AttributeValue
1 1 False
1 2 True
2 1 False
2 2 True
3 1 False
3 2 False
I want to group by attributeID and get count of attributes whose value is True. But when attributetype is false for a particular attribute there I want it to display 0. Right now the attributeID with all false vallues just doesn't show up.
Here is the sql query
SELECT MA.AttributeID, GA.Name,
--COUNT(isNull(MA.AttributeID,0)) as AttributCount,
CASE WHEN COUNT(MA.AttributeID) > 0 THEN COUNT(MA.AttributeID) Else 0 END AS 'AttributCount'
--CASE WHEN COUNT(MA.AttributeID) < 0 THEN 0 Else COUNT(MA.AttributeID) END AS 'TOTAL Attributes'
from GroupAttribute GA
inner join #MemberAttribute MA on GA.GroupAttributeID = MA.AttributeID
WHERE MA.AttributeValue = 'True'
GROUP BY MA.AttributeID,GA.Name
FOR AttributeID = 1 all the values are = False... so the result is like this
AttributeID Name AttributeCount <br/>
2 Attr2 2 <br/>
I want
1 Attr1 0 <br/>
too in the result set.
Try this - note that the 1 in ...THEN 1 ELSE ... is an arbitrary non-NULL value - it could be 'fred' or 12345 - that it's not NULL is the important part.
SELECT MA.AttributeID, GA.Name,
COUNT(CASE WHEN MA.AttributeValue = 'True' THEN 1 ELSE NULL END) AS 'AttributeCount'
from GroupAttribute GA
inner join #MemberAttribute MA on GA.GroupAttributeID = MA.AttributeID
GROUP BY MA.AttributeID,GA.Name
...somewhat more intuitively (thanks Ken) - and note that here the 1 and 0 are important...
SELECT MA.AttributeID, GA.Name,
SUM(CASE WHEN MA.AttributeValue = 'True' THEN 1 ELSE 0 END) AS 'AttributeCount'
from GroupAttribute GA
inner join #MemberAttribute MA on GA.GroupAttributeID = MA.AttributeID
GROUP BY MA.AttributeID,GA.Name
After some wriggling, I came up with this beauty containing no CASE expression:
SELECT GA.GroupAttributeID AS AttributeID, GA.Name,
COUNT(MA.AttributeID) AS AttributeCount
FROM GroupAttribute AS GA
LEFT OUTER JOIN #MemberAttribute AS MA
ON GA.GroupAttributeID = MA.AttributeID AND MA.AttributeValue = 'True'
GROUP BY GA.GroupAttributeID, GA.Name
This takes advantage of the fact that if there are no 'True' values for a particular AttributeID, the MA.AttributeID resulting from the LEFT OUTER JOIN will be NULL. The NULL value passed into COUNT() will lead to an AttributeCount of zero. The LEFT OUTER JOIN also ensures that a row will be present in the result set for AttributeID rows with zero counts.
The assumption with this query is that all group attributes are represented in the #MemberAttribute table variable. If not, there will be rows with zero counts representing those group attributes that are absent. If this is undesirable, a WHERE clause can be added to filter them out, complicating this query. Will's solution(s) would be far more practical if this is the case.
The execution plan compares well with Will's first solution, containing one less (Compute Scalar) step. It does use a LEFT OUTER JOIN vs an INNER JOIN, however, making the two methods practically identical for this simple example. It would be interesting to see how the two solutions scale if table variable is converted to a fairly large table, instead.
Will's actual plan for his solution involving COUNT():
My actual plan: