How to join these two queries? - sql

This query gets several AssignmentId's
SELECT AS2.AssignmentId
FROM dbo.AssignmentSummary AS AS2
WHERE AS2.SixweekPosition = 1 AND AS2.TeacherId = 'mggarcia'
This query gets a value for only one assignment through the variable #assignmentId
SELECT S.StudentId,
CASE WHEN OW.OverwrittenScore IS NOT NULL
THEN OW.OverwrittenScore
ELSE dbo.GetFinalScore(S.StudentId, #assignmentId)
END AS FinalScore
FROM dbo.Students AS S
LEFT JOIN dbo.OverwrittenScores AS OW
ON S.StudentId = OW.StudentID
AND OW.AssignmentId = #assignmentId
WHERE S.ClassId IN (
SELECT C.ClassId
FROM Classes AS C
WHERE C.TeacherId = #teacherId
)
As I pointed, in the last query works when you assign a value through the variable and returns a table. Now I want to get a table of several AssignmentId's from the first query.
What do I need? A Join table? I have no idea about what to do now.

AND OW.AssignmentId IN
(
SELECT AS2.AssignmentId
FROM dbo.AssignmentSummary AS AS2
WHERE AS2.SixweekPosition = 1 AND AS2.TeacherId = 'mggarcia'
)
the suggestion can be optimize if you can tell me how are the tables are related with each other.

You can combine them using in:
SELECT S.StudentId,
CASE WHEN OW.OverwrittenScore IS NOT NULL
THEN OW.OverwrittenScore
ELSE dbo.GetFinalScore(S.StudentId, #assignmentId)
END AS FinalScore
FROM dbo.Students AS S
LEFT JOIN dbo.OverwrittenScores AS OW
ON S.StudentId = OW.StudentID
AND OW.AssignmentId in (SELECT AS2.AssignmentId
FROM dbo.AssignmentSummary AS AS2
WHERE AS2.SixweekPosition = 1 AND AS2.TeacherId = 'mggarcia'
)
WHERE S.ClassId IN (
SELECT C.ClassId
FROM Classes AS C
WHERE C.TeacherId = #teacherId
)
There may be ways to simplify this query. This does a direct conversion of substituting the first query into the second.

Use APPLY operator with correlated subquery. Also you can replace CASE expression to function ISNULL.
SELECT S.StudentId,
ISNULL(o.OverwrittenScore, dbo.GetFinalScore(S.StudentId, o.AssignmentId)) AS FinalScore
FROM dbo.Students AS S
OUTER APPLY (
SELECT OW.OverwrittenScore, AS2.AssignmentId
FROM dbo.OverwrittenScores AS OW JOIN dbo.AssignmentSummary AS AS2
ON OW.AssignmentId = AS2.AssignmentId
WHERE AS2.SixweekPosition = 1 AND AS2.TeacherId = 'mggarcia'
AND S.StudentId = OW.StudentID
) o
WHERE S.ClassId IN (
SELECT C.ClassId
FROM Classes AS C
WHERE C.TeacherId = #teacherId
)

Related

Where Clause Using Conditional Statement

i have query below
SELECT #RoleUser = MR.Code FROM MasterRole MR INNER JOIN MasterUsersRole MUR ON MR.Id = MUR.RoleId
INNER JOIN MasterUsers MU ON Mu.UserCode = MUR.UserCode
WHERE MU.UserCode = #UserLoginID
select 1 Num
, MyHistory.ID
, MyHistory.RequestNumber
, MyHistory.FlowID
, MyHistory.FlowProcessStatusID
from
(
select *
from Requests R
inner join
(
--DECLARE #UserLoginID nvarchar(200) = 'dum.testing.3'
select distinct
RequestID
from dbo.RequestTrackingHistory RTH
where IIF(#UserLoginID = 'admin', #UserLoginID, RTH.CreatedBy) = #UserLoginID
OR ( CreatedBy IN
SELECT Mu.UserCode from MasterUsers MU
INNER JOIN MasterUsersRole MUR ON MU.UserCode = MUR.UserCode
INNER JOIN MasterRole MR ON MUR.RoleId = MR.Id
WHERE MR.Code = #RoleUser
)
)
) RT on R.ID = RT.RequestID
) as MyHistory
inner join MasterFlow F on MyHistory.FlowID = F.ID
inner join
(
select FP.ID
, FP.Name
, FP.AssignType
, FP.AssignTo
, FP.IsStart
, case FP.AssignType
when 'GROUP' then
G.Name
end as 'AssignToName'
from MasterFlowProcess FP
left join dbo.MasterRole G on FP.AssignTo = G.ID and FP.AssignType = 'GROUP'
) FP on MyHistory.FlowProcessID = FP.ID
inner join MasterFlowProcessStatus FPS on MyHistory.FlowProcessStatusID = FPS.ID
left join MasterFlowProcessStatusNext FPSN on FPS.ID = FPSN.ProcessStatusFlowID
left join MasterFlowProcess FPN on FPSN.NextProcessFlowID = FPN.ID
left JOIN MasterRole MR ON MR.Id = FPN.AssignTo
left join MasterUsersRole MUR on MR.Id = MUR.RoleId
left join MasterUsers MURO on MUR.UserCode = MURO.UserCode
inner join MasterUsers UC on MyHistory.CreatedBy = UC.UserCode
left join MasterUsers UU on MyHistory.UpdatedBy = UU.UserCode
LEFT JOIN RequestMT RMT ON MyHistory.ID = RMT.RequestID
LEFT JOIN RequestGT RGT ON MyHistory.ID = RGT.RequestID
left join (SELECT sum(QtyCU) countQty , RequestId from dbo.RequestGTDetail where IsActive = 1 group by RequestId) RGTD on RGTD.RequestId = RGT.RequestId
left join (SELECT sum(QtyPCS) countQty , RequestId from dbo.RequestMTDetail where IsActive = 1 group by RequestId) RMTD on RMTD.RequestId = RMT.RequestId
left join (SELECT COUNT(IIF(returnable = 0, returnable, null)) countReturnable , RequestId from dbo.RequestMTDetail group by RequestId) RMTR on RMTR.RequestId = RMT.RequestId
left JOIN dbo.MasterDistributor md ON md.Code = RGT.CustId or md.Code = RMT.CustId
left JOIN dbo.MasterUsersDistributor MUD ON MUD.UserCode = MURO.UserCode AND md.Code = MUD.DistributorCode
LEFT JOIN dbo.MasterReason MRMT ON RMT.ReasonId = MRMT.Id
LEFT JOIN dbo.MasterReason MRGT ON RGT.ReasonId = MRGT.Id
LEFT JOIN dbo.MasterDistributorGroup MDG ON MDG.Id = MD.GroupId
OUTER APPLY dbo.FnGetHistoryApproveDate(MyHistory.Id) AS x
where REPLACE(FPS.Name, '#Requestor', uc.Name) <> 'DRAFT'
AND MUD.DistributorCode IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = #UserLoginID)
i want to add some logic in where clause
this line
==> AND MUD.DistributorCode IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = #UserLoginID)
it depend on the #RoleUser variable, if #RoleUser IN ('A','B') then where clause above is executed, but if #RoleUser Not IN ('A','B') where clause not executed
i,m trying this where clause
AND IIF(#RoleUser IN ('A','B'), MUD.DistributorCode, #RoleUser) IN (SELECT DistributorCode FROM dbo.MasterUsersDistributor WHERE UserCode = IIF(#RoleUser IN ('A','B'), #UserLoginID, NULL))
it didn't work, only executed if #RoleUser IS ('A','B') other than that it return 0 record
any help or advice is really appreciated
thank you
The cleanest way I'm implemented these kind of crazy rules is a
holderTable
and a countVariable against the holder table.
I'll give a generic examples.
This is a "approach" and "philosophy", not a specific answer....with complex WHERE clauses.
DECLARE #customerCountryCount int
DECLARE #customerCountry TABLE ( CountryName varchar(15) )
if ( "the moon is blue on tuesday" ) /* << whatever rules you have */
BEGIN
INSERT INTO #customerCountry SELECT "Honduras" UNION ALL SELECT "Malaysia"
END
if ( "favorite color = green" ) /* << whatever rules you have */
BEGIN
INSERT INTO #customerCountry SELECT "Greenland" UNION ALL SELECT "Peru"
END
SELECT #customerCountryCount = COUNT(*) FROM #customerCountry
Select * from dbo.Customers c
WHERE
(#customerCountryCount = 0)
OR
( exists (select null from #customerCountry innerVariableTable where UPPER(innerVariableTable.CountryName) = UPPER(c.Country) ))
)
This way, instead of putting all the "twisted logic" in an overly complex WHERE statement..... you have "separation of concerns"...
Your inserts into #customerCountry are separated from your use of #customerCountry.
And you have the #customerCountryCount "trick" to distinguish when nothing was used.
You can add a #customerCountryNotExists table as well, and code it to where not exists.
As a side note, you may want to try using a #temp table (instead of a #variabletable (#customerCountry above)... and performance test these 2 options.
There is no "single answer". You have to "try it out".
And many many variables go into #temp table performance (from a sql-server SETUP, not "how you code a stored procedure". That is way outside the scope of this question.
Here is a SOF link to "safe" #temp table usage.
Temporary table in SQL server causing ' There is already an object named' error

SQL many to many select people with multiple vacancies

I am working with sql server through SSMS right now. How can i choose all people with multiple(>2)vacancies?
I am trying something like that, but i dont understand how to make part with "more than 2 vacancies"?
SELECT dbo.applicants.FirstName, dbo.vacancy.Name
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id = dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id WHERE dbo.vacancy.Name='third vacancy'
SELECT dbo.applicants.FirstName, dbo.vacancy.Name
FROM dbo.applicants A INNER JOIN
dbo.VacancyApplicant V ON A.id = V.ApplicantId
WHERE EXIST(
SELECT 1
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id =
dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id
WHERE A.id=dbo.applicants.id
GROUP BY dbo.applicants.id,dbo.vacancy.id
HAVING COUNT(1)>2
)
Group By and Having are you basic answer. Below is a simple solution, might not be ideal, but can give you the idea.
I am finding target "applicants" ids in subquery, that uses GROUP BY and HAVING then outer query joins to that to output FirstName and LastName of applicant
SELECT dbo.applicants.FirstName, dbo.applicants.LastName FROM
dbo.applicants a INNER JOIN
(
SELECT dbo.applicants.id
FROM dbo.applicants INNER JOIN
dbo.VacancyApplicant ON dbo.applicants.id = dbo.VacancyApplicant.ApplicantId INNER JOIN
dbo.vacancy ON dbo.VacancyApplicant.VacancyId = dbo.vacancy.id AND dbo.vacancy.Name='third vacancy'
GROUP BY dbo.applications.id
HAVING COUNT(dbo.vacancy.id) > 2
) targetIds ON a.id = targetIds.id
"more than 2 vacancies"?
Your question only mentions vacancies but your query is filtering for a particular name. I assume you really want more than two of that name.
If I understand correctly, you want aggregation:
SELECT a.FirstName, a.Name
FROM dbo.applicants a INNER JOIN
dbo.VacancyApplicant va
ON a.id = va.ApplicantId INNER JOIN
dbo.vacancy v
ON va.VacancyId = v.id
WHERE v.Name = 'third vacancy'
GROUP BY a.FirstName, v.Name
HAVING COUNT(*) > 2;
Note the use of table aliases. They make the query easier to write and to read.
WITH TempCTE AS (
SELECT DISTINCT ap.FirstName
,vc.Name
,COUNT (va.VacancyId) OVER (PARTITION BY ap.id) AS NoOfVacancies
FROM dbo.applicants ap
JOIN dbo.VacancyApplicant va
ON ap.id = va.ApplicantId
JOIN dbo.vacancy vc
ON va.VacancyId = vc.id
)
SELECT FirstName,[Name], NoOfVacancies FROM TempCTE
WHERE NoOfVacancies > 2

How to create distinct count from queries with several tables

I am trying to create one single query that will give me a distinct count for both the ActivityID and the CommentID. My query in MS Access looks like this:
SELECT
tbl_Category.Category, Count(tbl_Activity.ActivityID) AS CountOfActivityID,
Count(tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;
I know the answer must somehow include SELECT DISTINCT but am not able to get it to work. Do I need to create multiple subqueries?
This is really painful in MS Access. I think the following does what you want to do:
SELECT ac.Category, ac.num_activities, aco.num_comments
FROM (SELECT ca.category, COUNT(*) as num_activities
FROM (SELECT DISTINCT c.Category, a.ActivityID
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as caa
GROUP BY ca.category
) as ca LEFT JOIN
(SELECT c.Category, COUNT(*) as num_comments
FROM (SELECT DISTINCT c.Category, co.CommentId
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as aco
GROUP BY c.Category
) as aco
ON aco.CommentId = ac.CommentId
Note that your LEFT JOINs are superfluous because the WHERE clause turns them into INNER JOINs. This adjusts the logic for that purpose. The filtering is also very tricky, because it uses both tables, requiring that both subqueries have both JOINs.
You can use DISTINCT:
SELECT
tbl_Category.Category, Count(DISTINCT tbl_Activity.ActivityID) AS CountOfActivityID,
Count(DISTINCT tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;

Can this subquery be written as a join and still get the same result set/number of rows?

How do I write the below as a join and get the same number of rows?
SELECT
s.subjectid,
s.subjectname,
(SELECT
COUNT(*)
FROM dbo.Classes AS c
WHERE c.SubjectID = s.SubjectID
AND c.MondaySchedule = 1)
AS numofclasses
FROM dbo.subjects AS s
ORDER BY numofclasses DESC
I am trying to write it like below but getting a different answer:
SELECT
s.subjectid,
COUNT(ClassID) AS numberofclasses
FROM dbo.subjects AS s
LEFT JOIN dbo.classes AS c
ON s.SubjectID = c.SubjectID
WHERE c.MondaySchedule = 1
GROUP BY s.Subjectid
ORDER BY numberofclasses DESC
Move the where condition to the on condition. It is converting the outer join to an inner join:
select s.subjectid, count(ClassID) as numberofclasses
from dbo.subjects s left join
dbo.classes c
on s.SubjectID = c.SubjectID and c.MondaySchedule = 1
group by s.Subjectid
order by numberofclasses desc ;
This does assume that subjects(subjectid) is unique (or a primary key). If not, the two might return different results.

Add logic inside of a SELECT

Note in the below query that the first two queries inside of the parenthesis, I've added two repeated queries, I'm sure that this is not a good practice. I need to repeat this query anytime where I need the value.
SQL Server is throwing an exception about not to write DECLARE inside of the SELECT keyword. What can I do or what I'm missing to refactor it?
SELECT A.StudentId,
(
CASE WHEN (SELECT B.OverwrittenScore
FROM dbo.OverwrittenScores AS B
WHERE B.StudentId = A.StudentId
AND B.AssignmentId = #assignmentId
) IS NOT NULL
THEN (
SELECT B.OverwrittenScore
FROM dbo.OverwrittenScores AS B
WHERE B.StudentId = A.StudentId
AND B.AssignmentId = #assignmentId)
ELSE (-- ANOTHER QUERY, BY THE MOMENT: SELECT 0 )
END
) AS FinalScore
FROM dbo.Students AS A
My suggestion would be to look at using a JOIN:
SELECT A.StudentId,
case
when B.OverwrittenScore is not null
then B.OverwrittenScore
else 0
end AS FinalScore
FROM dbo.Students AS A
LEFT JOIN dbo.OverwrittenScores B
ON B.StudentId = A.StudentId
AND B.AssignmentId = #assignmentId
If you want to use another select in the else, then you could add more joins as needed:
SELECT A.StudentId,
case
when B.OverwrittenScore is not null
then B.OverwrittenScore
else c.whatever
end AS FinalScore
FROM dbo.Students AS A
LEFT JOIN dbo.OverwrittenScores B
ON B.StudentId = A.StudentId
AND B.AssignmentId = #assignmentId
LEFT JOIN anothertable c
ON a.col = c.col
Or even you could use COALESCE to replace the null values:
SELECT A.StudentId,
coalesce(B.OverwrittenScore, 0) as FinalScore
FROM dbo.Students AS A
LEFT JOIN dbo.OverwrittenScores B
ON B.StudentId = A.StudentId
AND B.AssignmentId = #assignmentId
Not sure where error regarding DECLARE is coming from, but you could change what you do show to
SELECT
A.StudentId,
COALESCE(B.OverwrittenScore, 0) AS FinalScore
FROM dbo.Students AS A
LEFT JOIN dbo.OverwrittenScores AS B
ON A.StudentId = B.StudentId AND B.AssignmentId = #assignmentId