I want to count the amount of rows of every componistId. When I run the following SQL statement it works fine:
SELECT C.componistId, COUNT(*)
FROM Componist C LEFT JOIN Stuk S ON S.componistId = C.componistId
GROUP BY C.componistId
Now I want only the rows where stukNrOrigineel is null
SELECT C.componistId, COUNT(*)
FROM Componist C LEFT JOIN Stuk S ON S.componistId = C.componistId
WHERE S.stuknrOrigineel IS NULL
GROUP BY C.componistId
But when I do this, all the rows with a result of 0 disappear. Only the rows with at least 1 row are displayed. How can I make this work?
You need to include the condition in the on clause:
SELECT C.componistId, COUNT(C.componistId)
FROM Componist C LEFT JOIN
Stuk S
ON S.componistId = C.componistId AND
S.stuknrOrigineel IS NULL
GROUP BY C.componistId;
Note: I changed the COUNT() to count from the second table. This is normally what you want when combining LEFT JOIN with COUNT().
On some databases, I think the above might not quite work as expected (the question is whether the condition on S is evaluated before or after the LEFT JOIN). This should always work:
SELECT C.componistId, COUNT(s.componistId)
FROM Componist C LEFT JOIN
(SELECT S.* FROM Stuk S WHERE S.stuknrOrigineel IS NULL
) s
ON S.componistId = C.componistId AND
GROUP BY C.componistId;
Another generic solution is move the condition into the aggregation function:
SELECT C.componistId,
SUM(CASE WHEN S.stuknrOrigineel IS NULL THEN 1 ELSE 0 END)
FROM Componist C LEFT JOIN
Stuk S
ON S.componistId = C.componistId
GROUP BY C.componistId;
Related
Currently, I have this SQL query:
SELECT AVG(ttbe.MarkGiven) FROM tblTestsTakenByEmployee ttbe
INNER JOIN tblCoursesTakenByEmployee ctbe ON ttbe.EmployeeId = ctbe.EmployeeId
LEFT JOIN tblCourse c ON ctbe.CourseId = c.CourseId
WHERE ctbe.HasCompletedCourse = 'Y'
GROUP BY ctbe.CourseId, c.CourseName, EXTRACT(YEAR FROM ctbe.DateOfCourseCompletion), ctbe.EmployeeId
At the moment, this returns the average mark of a single employee on a course which was completed on a certain year, calculated across each of the tests it contains (a course can have multiple tests).
I want to output an additional column to the SELECT query which specifies whether that employee has passed the course, based on a threshold. For example, if AVG(ttbe.MarkGiven) >= 40 then it would return 'Y' in the new column, otherwise it would return 'N'. What's the simplest and most efficient way of achieving this?
you could use the CASE expression like that:
SELECT AVG(ttbe.MarkGiven),
CASE WHEN AVG(ttbe.MarkGiven) >= 40 THEN 'Y' ELSE 'N' END as exam_passed
FROM tblTestsTakenByEmployee ttbe
INNER JOIN tblCoursesTakenByEmployee ctbe ON ttbe.EmployeeId = ctbe.EmployeeId
LEFT JOIN tblCourse c ON ctbe.CourseId = c.CourseId
WHERE ctbe.HasCompletedCourse = 'Y'
GROUP BY ctbe.CourseId, c.CourseName, EXTRACT(YEAR FROM ctbe.DateOfCourseCompletion),
ctbe.EmployeeId
https://www.oracletutorial.com/oracle-basics/oracle-case/#:~:text=Oracle%20CASE%20expression%20allows%20you,that%20accepts%20a%20valid%20expression.
You could use your current query as a CTE and then from the CTE use the reulst and compare it to your threshold
The advantag of the CTE is that you will not have to recalculate your AVG(ttbe.MarkGiven)
WITH _CTE as
(
SELECT AVG(ttbe.MarkGiven) as Col1
FROM tblTestsTakenByEmployee ttbe
INNER JOIN tblCoursesTakenByEmployee ctbe
ON ttbe.EmployeeId = ctbe.EmployeeId
LEFT JOIN tblCourse c
ON ctbe.CourseId = c.CourseId
WHERE ctbe.HasCompletedCourse = 'Y'
GROUP BY ctbe.CourseId, c.CourseName, EXTRACT(YEAR FROM ctbe.DateOfCourseCompletion), ctbe.EmployeeId
)
Select Col1
,CASE
WHEN Col1 >= 50 THEN 'Y'
ELSE 'N'
END AS Col1_Threshold
I want to make a query where an attribute (same attribute) matches two different conditions at the same time. I have to check if a driver was found in both cities.
I tried to use intersect but I don't get any matches. But in my table I have one driver that matches this conditions.
SELECT s.NumeSofer
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l ON c.idLocContr=l.idLoc
WHERE l.DenLoc IN ('Iasi', 'Rosiori') AND l.Jud IN ('IS', 'NT');
INTERSECT
SELECT s.NumeSofer
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l ON c.idLocContr=l.idLoc
WHERE l.DenLoc='Rosiori' AND l.Jud='NT';
You can use aggregation and a HAVING clause, like:
SELECT s.NumeSofer
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l
ON c.idLocContr = l.idLoc
AND (l.DenLoc, l.Jud) IN ( ('Iasi', 'IS'), ('Rosiori', 'NT') )
GROUP BY s.NumeSofer
HAVING
MAX(CASE WHEN l.DenLoc = 'Iasi' AND l.Jud = 'IS' THEN 1 END) = 1
AND MAX(CASE WHEN l.DenLoc = 'Rosiori' AND l.Jud = 'NT' THEN 1 END) = 1
This will bring you all NumeSofer for which at least one record exists in localitati with DenLoc='Iasi' AND Jud='IS' and at least one record exists with DenLoc='Rosiori' AND Jud='NT'.
Note: the IN operator can be used with tuple values; this reduce the lenght of the query, while avoiding using OR, which is usually not good for general performance.
Do a GROUP BY instead. Use case expressions to do conditional aggregation:
SELECT s.NumeSofer, count(distinct l.DenLoc) as totcount,
count(case when l.DenLoc='Rosiori' then 1 end) as Rosioricount,
count(case when l.DenLoc='Iasi' then 1 end) as Iasicount
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l ON c.idLocContr=l.idLoc
WHERE (l.DenLoc='Rosiori' AND l.Jud='NT')
OR (l.DenLoc='Iasi' AND l.Jud='IS')
GROUP BY s.NumeSofer
ORDER BY totcount desc
Any rows with totcount = 2?
To get only drivers with both DenLoc's add a HAVING clause:
SELECT s.NumeSofer, count(distinct l.DenLoc) as totcount,
count(case when l.DenLoc='Rosiori' then 1 end) as Rosioricount,
count(case when l.DenLoc='Iasi' then 1 end) as Iasicount
FROM Soferi s
INNER JOIN contraventii c ON s.idSofer=c.idSofer
INNER JOIN localitati l ON c.idLocContr=l.idLoc
WHERE (l.DenLoc='Rosiori' AND l.Jud='NT')
OR (l.DenLoc='Iasi' AND l.Jud='IS')
GROUP BY s.NumeSofer
HAVING count(distinct l.DenLoc) > 1
I am trying to create a numerator(num) and denominator(den) column that I will later use to create a metric value. In my numerator column, I need to have a criteria that my denominator column does not have. When I add the where clause to my sub query, I am getting the error below. I do not want to add INRInRange to my Group By clause.
Column 'dbo.PersonDetailB.INRInRange' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
SELECT
dbo.PersonDetailSpecialty.PracticeAbbrevName,
(SELECT COUNT(DISTINCT dbo.Problem.PID) WHERE PersonDetailB.INRInRange='True') AS num,
COUNT(DISTINCT dbo.Problem.PID) AS den
FROM
dbo.PersonDetailB
RIGHT OUTER JOIN
dbo.PersonDetailSpecialty ON dbo.PersonDetailB.PID = dbo.PersonDetailSpecialty.PID
LEFT OUTER JOIN
dbo.Problem ON dbo.PersonDetailSpecialty.PID = dbo.Problem.PID
GROUP BY
practiceabbrevname
Create a sub-query that counts PersonDetailB.INRInRange and LEFT OUTER JOIN it with the original query.
SELECT Main.PracticeAbbrevName, InRange.Num AS num, Main.den
FROM
(SELECT
dbo.PersonDetailSpecialty.PracticeAbbrevName,
COUNT(DISTINCT dbo.Problem.PID) AS den
FROM
dbo.PersonDetailB
RIGHT OUTER JOIN
dbo.PersonDetailSpecialty ON dbo.PersonDetailB.PID = dbo.PersonDetailSpecialty.PID
LEFT OUTER JOIN
dbo.Problem ON dbo.PersonDetailSpecialty.PID = dbo.Problem.PID
GROUP BY
practiceabbrevname) Main
LEFT OUTER JOIN
(SELECT practiceabbrevname, COUNT(DISTINCT dbo.Problem.PID) Num WHERE PersonDetailB.INRInRange='True' GROUP BY practiceabbrevname) InRange ON Main.practiceabbrevname = InRange.practiceabbrevname
The problem with this statement:
SELECT dbo.PersonDetailSpecialty.PracticeAbbrevName,
(SELECT COUNT(DISTINCT dbo.Problem.PID) WHERE PersonDetailB.INRInRange = 'True') AS num,
COUNT(DISTINCT dbo.Problem.PID) AS den
is that PersonDetailB.INRInRange1 doesn't have a unique value in each group. It is possible that it does. One method is to add it to the GROUP BY:
GROUP BY practiceabbrevname, PersonDetailB.INRInRange
Another method would use an aggregation function in the subquery:
SELECT dbo.PersonDetailSpecialty.PracticeAbbrevName,
(SELECT COUNT(DISTINCT dbo.Problem.PID) WHERE MAX(PersonDetailB.INRInRange) = 'True') AS num,
COUNT(DISTINCT dbo.Problem.PID) AS den
Join a separate table as a different name on the where criteria that was in the sub query.
SELECT PersonDetailSpecialty.PracticeAbbrevName,
COUNT(PDTRUE.PID) as num,
COUNT(dbo.Problem.PID) AS den
FROM dbo.PersonDetailSpecialty LEFT OUTER JOIN
dbo.PersonDetailB as PDTRUE ON dbo.PersonDetailSpecialty.PID = PDTRUE.PID and PDTRUE.INRInRange='True' LEFT OUTER JOIN
dbo.PersonDetailB ON dbo.PersonDetailSpecialty.PID = PersonDetailB.PID LEFT OUTER JOIN
dbo.Medicate ON dbo.PersonDetailSpecialty.PID = dbo.Medicate.PID LEFT OUTER JOIN
dbo.Problem ON dbo.PersonDetailSpecialty.PID = dbo.Problem.PID
GROUP BY PersonDetailSpecialty.PracticeAbbrevName
This is the relevant code needed in the FROM section
dbo.PersonDetailSpecialty LEFT OUTER JOIN dbo.PersonDetailB as PDTRUE
ON dbo.PersonDetailSpecialty.PID = PDTRUE.PID and PDTRUE.INRInRange='True'
This lets you add
COUNT(PDTRUE.PID) as num,
as a simple part of the overall select query
I am getting the error-msg when i try the following code:
SELECT COUNT(*) FROM [ServerName].[dbo].[TableName1] B
WHERE right(B.TableName1, 2) = '34' AND
B.UnitId=
(select distinct D.UnitId
from [ServerName].[dbo].[TableName2] D
INNER JOIN #temp E ON D.UnitId=E.UnitId)
Does anyone know how to solve it? It says the subquery returned more than 1 value. The temp-table is created and contains a selection of the number individuals that I want to count, by unitId.
Try using a join instead of the subquery:
SELECT COUNT(*)
FROM [ServerName].[dbo].[TableName1] B
INNER JOIN [ServerName].[dbo].[TableName2] D ON B.UnitId = D.UnitId
INNER JOIN #temp E ON D.UnitId = E.UnitId
WHERE RIGHT(B.TableName1, 2) = '34'
The subquery (select distinct) has more than one value returned. As such B.UnitId cannot equal more than one value.
You can use IN if its ok for B.UnitId to have more than one value.
SELECT COUNT(*)
FROM [ServerName].[dbo].[TableName1] B
WHERE right(B.TableName1, 2) = '34'
AND B.UnitId in (select D.UnitId
from [ServerName].[dbo].[TableName2] D
INNER JOIN #temp E ON D.UnitId=E.UnitId)
Distinct becomes superfluous when you use IN.
I seem to be having trouble with the following query. It basically works, but I have a case where it is returning one row from mc_WorkoutDetails twice!
Here's the original query:
ALTER PROCEDURE [dbo].[mc_Workouts_GetActivities]
#WorkoutID bigint
AS
BEGIN
SET NOCOUNT ON
SELECT d.ID, a.Description,
CASE WHEN Reps = 0 THEN NULL ELSE Reps END AS Reps,
CASE WHEN Sets = 0 THEN NULL ELSE Sets END AS Sets,
CASE WHEN Minutes = 0 THEN NULL ELSE Minutes END AS Minutes,
d.Comments, c.Name AS Category, a.CategoryID,
(CASE WHEN v.ActivityID IS NULL THEN 0 ELSE 1 END) AS HasVideo,
a.ID AS ActivityID
FROM mc_WorkoutDetails d
INNER JOIN mc_Activities a ON d.ActivityID = a.ID
INNER JOIN mc_Activities_Categories c ON a.CategoryID = c.ID
LEFT OUTER JOIN mc_TrainerVideos v ON a.ID = v.ActivityID
WHERE (d.WorkoutID = #WorkoutID)
ORDER BY SortOrder, a.Description
RETURN ##ERROR
END
Then I tried changing:
INNER JOIN mc_Activities a ON d.ActivityID = a.ID
INNER JOIN mc_Activities_Categories c ON a.CategoryID = c.ID
To:
LEFT OUTER JOIN mc_Activities a ON d.ActivityID = a.ID
LEFT OUTER JOIN mc_Activities_Categories c ON a.CategoryID = c.ID
But that didn't seem to help. I still get the duplicate row.
Can anyone see what's happening?
What you can do is add a join back to the same table using a group to weed out the duplicate rows.
So add this to your join after FROM mc_WorkoutDetails d:
inner join (select [columns you want to select], max(id) id
from mc_WorkoutDetails
group by [columns you want to select] ) q on q.id = d.id
Let me know if that makes sense. Basically you are doing a distinct and getting the max id so you eliminate one of the rows in the join. You have to remember that even if you want there to be duplicates, they will be eliminated even if they are suppose to be there.
The full alter would be:
ALTER PROCEDURE [dbo].[mc_Workouts_GetActivities]
#WorkoutID bigint
AS
BEGIN
SET NOCOUNT ON
SELECT d.ID, a.Description,
CASE WHEN Reps = 0 THEN NULL ELSE Reps END AS Reps,
CASE WHEN Sets = 0 THEN NULL ELSE Sets END AS Sets,
CASE WHEN Minutes = 0 THEN NULL ELSE Minutes END AS Minutes,
d.Comments, c.Name AS Category, a.CategoryID,
(CASE WHEN v.ActivityID IS NULL THEN 0 ELSE 1 END) AS HasVideo,
a.ID AS ActivityID
FROM mc_WorkoutDetails d
inner join (select Reps, Sets, Comments, Minutes, max(id) id
from mc_WorkoutDetails
group by Reps, Sets, Comments, Minutes ) q on q.id = d.id
INNER JOIN mc_Activities a ON d.ActivityID = a.ID
INNER JOIN mc_Activities_Categories c ON a.CategoryID = c.ID
LEFT OUTER JOIN mc_TrainerVideos v ON a.ID = v.ActivityID
WHERE (d.WorkoutID = #WorkoutID)
ORDER BY SortOrder, a.Description
RETURN ##ERROR
END
Thanks for everyone's input. The general suggestions here were correct: I had two rows in the mc_workoutDetails table that referenced the same row in the mc_Activities table.
While the foreign key was part of a unique primary key, it was a compound key and so this column could contain duplicates as long as the other column in the key were different.