SQL- Subquery with aggregate functions (SQL Server) - sql-server

My set up is as thus: I have three tables-
Students (StudentID, FirstName, LastName etc.),
StudentSemesters(StudentID,SemID etc.), and
Semesters(SemID, Year)
My requirement is to, get the details for each student but only for their last semester. Logically this implies the semester with the highest year number. I cannot seem to get the Query right. For simplicity 'Year' is just an integer (e.g. 2000, 1998). Below is the current query that I have been stuck at for some time:
SELECT dbo.Student.LastName + ' , ' + dbo.Student.FirstName AS Student, dbo.Student.Defence1Date, dbo.Student.Defence2Date, COUNT(StudentSemesters_1.SemID)
AS SemesterCount, dbo.Student.EntrySemester + ' - ' +
(SELECT dbo.StudentSemesters.SemID
FROM dbo.StudentSemesters INNER JOIN
dbo.ListSemesters ON dbo.StudentSemesters.SemID = dbo.ListSemesters.SemID
WHERE (dbo.Student.StudentCode = dbo.StudentSemesters.StudentCode)
GROUP BY dbo.StudentSemesters.SemID, dbo.ListSemesters.Year
HAVING (dbo.ListSemesters.Year = MAX(dbo.ListSemesters.Year))) AS Expr1
FROM dbo.Student INNER JOIN
dbo.StudentSemesters AS StudentSemesters_1 ON dbo.Student.StudentCode = StudentSemesters_1.StudentCode
GROUP BY dbo.Student.LastName, dbo.Student.FirstName, dbo.Student.Defence1Date, dbo.Student.Defence2Date, dbo.Student.EntrySemester,
dbo.Student.StudentCode

You can do this to get the details for only the last semester for each student:
SELECT
s.LastName + ' , ' + s.FirstName AS Student,
s.Defence1Date,
s.EntrySemester + ' - ' + s1.SemId AS Expr1
FROM dbo.Student AS s
INNER JOIN dbo.StudentSemesters AS S1 ON s.StudentCode = S1.StudentCode
INNER JOIN dbo.ListSemesters AS lm ON lm.SemID = s1.SemId
INNER JOIN
(
SELECT SemId, MAX(Year) AS MaxYear
FROM dbo.StudentSemesters
GROUP BY SemId
) AS s2 ON s2.semId = lm.SemId AND ls.Year = s2.MaxYear;
However, if you're using SQL Server 2005+, you can use the window function to do so:
WITH CTE
AS
(
SELECT
s.LastName + ' , ' + s.FirstName AS Student,
s.Defence1Date,
s.EntrySemester + ' - ' + s1.SemId AS Expr1,
ROW_NUMBER() OVER(PARTITION BY s1.SemId
ORDER BY Year DESC) AS RowNumber
FROM dbo.Student AS s
INNER JOIN dbo.StudentSemesters AS S1 ON s.StudentCode = S1.StudentCode
INNER JOIN dbo.ListSemesters AS lm ON lm.SemID = s1.SemId
)
SELECT
Student,
Defence1Date,
Expr1,
FROM CTE
WHERE RN = 1;
But this won't get the SemesterCount, but you might be able to make it like this (this is just a guess):
SELECT
s.LastName + ' , ' + s.FirstName AS Student,
s.Defence1Date,
s1.SemCount,
s.EntrySemester + ' - ' + s1.SemId AS Expr1
FROM dbo.Student AS s
INNER JOIN
(
SELECT StudentCode, COUNT(SemId) AS SemCount
FROM dbo.StudentSemesters
GROUP BY StudentCode
) AS S1 ON s.StudentCode = S1.StudentCode
INNER JOIN dbo.ListSemesters AS lm ON lm.SemID = s1.SemId
INNER JOIN
(
SELECT SemId, MAX(Year) AS MaxYear
FROM dbo.StudentSemesters
GROUP BY SemId
) AS s2 ON s2.semId = lm.SemId AND ls.Year = s2.MaxYear;

Related

Order by with UNION statement

I have a little issue with the ORDER BY statement.
Here is my query (sorry I know is a little bit long, but I alredy cut a lot of the code)
(SELECT AssAttrezzi.ID, AssBombole.matricola, CONCAT(AssBombole.matricola, ' - ', Bombole.nome) AS Descrizione
FROM AssAttrezzi INNER JOIN
AssBombole
ON AssBombole.matricola = AssAttrezzi.attrezzoID INNER JOIN
Bombole
ON Bombole.ID = AssBombole.tipoBombolaID
WHERE AssAttrezzi.cantiereID=1
) UNION
(SELECT AssAttrezzi.ID, Saldatrici.matricola,CONCAT(Saldatrici.matricola, ' - ', Saldatrici.nome) AS Descrizione
FROM AssAttrezzi INNER JOIN
Saldatrici
ON Saldatrici.matricola = AssAttrezzi.attrezzoID
WHERE AssAttrezzi.cantiereID = 1
) UNION
(SELECT AssAttrezzi.ID, AssBanchiGlad.idAtrezzo, CONCAT(AssBanchiGlad.idAtrezzo, ' - ', BanchiGladiator.nome) AS Descrizione
FROM AssAttrezzi INNER JOIN
AssBanchiGlad
ON AssBanchiGlad.idAtrezzo = AssAttrezzi.attrezzoID INNER JOIN
BanchiGladiator
ON BanchiGladiator.ID = AssBanchiGlad.bancoID
WHERE AssAttrezzi.cantiereID = 1
) ORDER BY LEN(matricola), matricola
The problem is when I put ORDER BY at the end of each SELECT It gives me error:
Incorrect syntax near UNION
But when I try to put the ORDER BY at the end of all it gives me another error:
If the ORDER BY statement includes the UNION, INTERSECT or EXCEPT operator, the elements of the instruction must be specified in the selection list.
What? I would like to order by *.matricola basically matricola for me is the important one.
Try this:
(
SELECT AssAttrezzi.ID,
AssBombole.matricola,
CONCAT(AssBombole.matricola, ' - ', Bombole.nome) AS Descrizione,
LEN(AssBombole.matricola) AS matricola_len
FROM AssAttrezzi
INNER JOIN AssBombole ON AssBombole.matricola = AssAttrezzi.attrezzoID
INNER JOIN Bombole ON Bombole.ID = AssBombole.tipoBombolaID
WHERE AssAttrezzi.cantiereID=1
)
UNION
(
SELECT AssAttrezzi.ID,
Saldatrici.matricola,
CONCAT(Saldatrici.matricola, ' - ', Saldatrici.nome) AS Descrizione,
LEN(Saldatrici.matricola) AS matricola_len
FROM AssAttrezzi
INNER JOIN Saldatrici ON Saldatrici.matricola = AssAttrezzi.attrezzoID
WHERE AssAttrezzi.cantiereID = 1
)
UNION
(
SELECT AssAttrezzi.ID,
AssBanchiGlad.idAtrezzo,
CONCAT(AssBanchiGlad.idAtrezzo, ' - ', BanchiGladiator.nome) AS Descrizione,
LEN(AssBanchiGlad.idAtrezzo) AS matricola_len
FROM AssAttrezzi
INNER JOIN AssBanchiGlad ON AssBanchiGlad.idAtrezzo = AssAttrezzi.attrezzoID
INNER JOIN BanchiGladiator ON BanchiGladiator.ID = AssBanchiGlad.bancoID
WHERE AssAttrezzi.cantiereID = 1
)
ORDER BY LEN(matricola),
matricola
You can use custom sort :
SELECT . . . , 1 AS ID
UNION
SELECT . . . , 2
UNION
SELECT . . . , 3
ORDER BY ID;
You can't add separate order by clause when you use UNION/UNION ALL
EDIT :
SELECT AssAttrezzi.ID, AssBombole.matricola,
CONCAT(AssBombole.matricola, ' - ', Bombole.nome) AS Descrizione,
LEN(matricola) AS SortLen
FROM AssAttrezzi INNER JOIN
AssBombole
ON AssBombole.matricola = AssAttrezzi.attrezzoID INNER JOIN
Bombole
ON Bombole.ID = AssBombole.tipoBombolaID
WHERE AssAttrezzi.cantiereID = 1
UNION
SELECT AssAttrezzi.ID, Saldatrici.matricola,
CONCAT(Saldatrici.matricola, ' - ', Saldatrici.nome) AS Descrizione,
LEN(matricola)
FROM AssAttrezzi INNER JOIN
Saldatrici
ON Saldatrici.matricola = AssAttrezzi.attrezzoID
WHERE AssAttrezzi.cantiereID = 1
UNION
SELECT AssAttrezzi.ID, AssBanchiGlad.idAtrezzo,
CONCAT(AssBanchiGlad.idAtrezzo, ' - ', BanchiGladiator.nome) AS Descrizione,
LEN(matricola)
FROM AssAttrezzi INNER JOIN
AssBanchiGlad
ON AssBanchiGlad.idAtrezzo = AssAttrezzi.attrezzoID INNER JOIN
BanchiGladiator
ON BanchiGladiator.ID = AssBanchiGlad.bancoID
WHERE AssAttrezzi.cantiereID = 1
ORDER BY SortLen, matricola;

Stuff function for multiple columns in SQL Server

I have some queries to stuff a specific column.
Query 1:
with cte as
(
select distinct
w.Work_WorkID, k.Name as "Secretariat_Attendees"
from
d, m, s, w, a, k
where
d.DataID = m.Map_MapObjID
and m.Map_MapID = s.SubWork_MapID
and s.SubWork_WorkID = w.Work_WorkID
and w.Work_WorkID = a.WF_ID
and d.DataID = 35269818
and a.WF_AttrID = 17
and k.ID = a.WF_ValInt
)
select distinct
t2.Work_WorkID,
stuff((select N' ; ' + Secretariat_Attendees
from (select Work_WorkID, Secretariat_Attendees
from cte t1
where t1.Work_WorkID = t2.Work_WorkID) AS t
for xml path('')), 1, 2, '') + N'' Secretariat_Attendees
from cte t2;
Query 1 output:
Work_WorkID Secretariat_Attendees
----------------------------------------------------
35273587 Admin CEO Office ; BD_TestUser ; Diana
35277687 10023165 ; 10036755 ; 10075193
Query 2:
with cte as (
select Distinct w.Work_WorkID,k.Name as "Committee_Attendees" from d, m, s, w, a, k
where d.DataID=m.Map_MapObjID and m.Map_MapID=s.SubWork_MapID and s.SubWork_WorkID=w.Work_WorkID
and w.Work_WorkID=a.WF_ID and d.DataID=35269818 and a.WF_AttrID=5 and k.ID=a.WF_ValInt
)
select distinct t2.Work_WorkID,
stuff((select N' ; ' + Committee_Attendees
from (select Work_WorkID, Committee_Attendees
from cte t1 where t1.Work_WorkID = t2.Work_WorkID) AS t
for xml path('')
), 1, 2, '') + N'' Committee_Attendees from cte t2;
Query 2 Output:
Work_WorkID Committee_Attendees
35273587 Deva ; User 1
35277687 User3 ; User 4
How do I combine both these query into one to get the below desired output based on Work_WorkID?
Work_WorkID Secretariat_Attendees Committee_Attendees
35273587 Admin CEO Office ; BD_TestUser ; Diana Deva ; User 1
35277687 10023165 ; 10036755 ; 10075193 User3 ; User 4
The only difference between the two queries looks like WF_AttrID column. I moved this predicate to the sub queries. you can try this. And I also changed your table joins syntax to JOIN
with cte as
(
select distinct
w.Work_WorkID, k.Name as "Attendees", a.WF_AttrID
from
DTree d
INNER JOIN WMap m ON d.DataID = m.Map_MapObjID
INNER JOIN WSubWork s ON m.Map_MapID = s.SubWork_MapID
INNER JOIN WWork w ON s.SubWork_WorkID = w.Work_WorkID
INNER JOIN WFAttrData a ON w.Work_WorkID = a.WF_ID
INNER JOIN KUAF k ON k.ID = a.WF_ValInt
where
d.DataID = 35269818
and a.WF_AttrID IN( 5 ,17 )
)
select distinct
t2.Work_WorkID,
stuff((select N' ; ' + Secretariat_Attendees
from (select Work_WorkID, t1.Attendees AS Secretariat_Attendees
from cte t1
where t1.Work_WorkID = t2.Work_WorkID AND t1.WF_AttrID = 17) AS t
for xml path('')), 1, 2, '') + N'' Secretariat_Attendees,
stuff((select N' ; ' + Committee_Attendees
from (select Work_WorkID, t1.Attendees AS Committee_Attendees
from cte t1 where t1.Work_WorkID = t2.Work_WorkID AND t1.WF_AttrID = 5) AS t
for xml path('')
), 1, 2, '') + N'' Committee_Attendees
from cte t2;
This seems to be a simple join. If your difficulty stems from combining them into a single query, all you have to do is replace the second query's cte name to cte2, put the ctes together in the beginning, and use both queries as derived tables:
with cte as(
....first query cte....
)
,cte2 as(
....second query cte
)
select *
from
(
....query 1.....
)t1
inner join
(
......query 2 but "cte2" instead of "cte"....
)t2 on t1.Work_WorkID=t2.Work_WorkID
This is a fast, unoptimized answer. I get the feeling the ctes and the queries have common, optimizable parts. Also, please stop using the onld join notation (from table1,table2,table3.....) and start using join, you can find reasons all over the internet.

Using Left Join instead of Union

Currently I am using UNION statement to retrieve data from two tables. Can I use LEFT JOIN instead of UNION?
SELECT count(*), emp
FROM
(
SELECT *, name + '|' + age as emp
FROM Employee As E With(NoLock)
WHERE name = 'X'
And age In (1, 2)
AND active = 'Y'
UNION ALL
SELECT H.*, H.name + '|' + H.age AS emp
FROM Employee_History As H With(NoLock)
Inner Join Employee As E With(NoLock) On E.id = H.id
WHERE H.name = 'X'
And H.age In (1, 2)
And E.active != 'Y'
AND H.salary in
(
SELECT MAX(H2.salary)
From Employee_History As H2 With(NoLock)
group by H2.id
)
) AS A
GROUP BY A.emp
ORDER BY A.emp

SQL Pivot dynamic query error

I tried to convert rows values into columns...Below is my query...
Select * from (SELECT *
FROM ( SELECT PROJ.PROJ_ID,
PROJ.PROJ_NM AS [Project Name],
PROJ.PROJ_DS AS [Project Description],
convert(varchar(10), PROJ.PROJ_ACTL_LNCH_DT, 110) [Actual Completed Date],
PROJ.PROJ_SMRY_DS AS [Project Summary],
dbo.udf_BankContacts(PROJ.PROJ_ID) AS [BankContact],
convert(varchar(10), PROJ.PROJ_EST_LNCH_DT, 110) AS [Estimated Launch Date],
PROJ.ENTER_DT AS [Begin Date],
PROJ_STA.PROJ_STA_DS AS [Project Status],
SFTW_DEV_MTHD.SFTW_DEV_MTHD_NM AS [Software Development Method],
PROJ_PHASE.PROJ_PHASE_DS AS [Phase],
PROJ_CTGY.PROJ_CTGY_DS AS [Project Category],
(CASE WHEN PROJ.ARCH_IN='0' THEN 'N' ELSE 'C' END) AS [Archive],
PROJ.PHASE_CMNT_TX AS [Phase Comment],
PROD_TYPE_DS
FROM dbo.Project PROJ
left join dbo.PROJ_PROD PP on PROJ.PROJ_ID = PP.PROJ_ID
left join dbo.PROD_TYPE PT on PP.PROD_TYPE_ID = PT.PROD_TYPE_ID
LEFT JOIN DBO.PROJ_STA ON PROJ.PROJ_STA_ID = PROJ_STA.PROJ_STA_ID
left join dbo.SFTW_DEV_MTHD on PROJ.SFTW_DEV_MTHD_ID = SFTW_DEV_MTHD.SFTW_DEV_MTHD_ID
left join dbo.PROJ_PHASE on PROJ.PROJ_PHASE_ID = PROJ_PHASE.PROJ_PHASE_ID
left join dbo.PROJ_CTGY on PROJ.PROJ_CTGY_ID = PROJ_CTGY.PROJ_CTGY_ID
) data
PIVOT
( MAX(PROD_TYPE_DS)
FOR PROD_TYPE_DS IN ([PT1],[PT2])
) pvt2
where PROJ_ID is not null) AS ProdType
LEFT JOIN (SELECT *
FROM
(
select PROJ_ID,
PROJ_APRV_TYPE_DS = case
when col ='PROJ_APRV_TYPE_DS'
then PROJ_APRV_TYPE_DS
else PROJ_APRV_TYPE_DS+col end,
value
from
(
SELECT PROJ.PROJ_ID,
PAT.PROJ_APRV_TYPE_DS PROJ_APRV_TYPE_DS,
convert(varchar(10), PATS.APRV_EXPT_BY_DT, 120) APRV_EXPT_BY_DT,
convert(varchar(10), PATS.APRV_CMPL_DT, 120) APRV_CMPL_DT
FROM dbo.Project PROJ
left join [dbo].[PROJ_APRV_TYPE_STA] PATS
on PROJ.PROJ_ID = PATS.PROJ_ID
left join [dbo].[PROJ_APRV_STA] PAS
on PATS.PROJ_APRV_STA_ID = PAS.PROJ_APRV_STA_ID
right outer join dbo.PROJ_APRV_TYPE PAT
on PATS.PROJ_APRV_TYPE_ID = PAT.PROJ_APRV_TYPE_ID
) s
cross apply
(
select 'PROJ_APRV_TYPE_DS', PROJ_APRV_TYPE_DS union all
select ' Expected Date', APRV_EXPT_BY_DT union all
select ' Completed Date', APRV_CMPL_DT
) c (col, value)
) data
PIVOT
(
MAX(value)
FOR PROJ_APRV_TYPE_DS IN ([RMC Approval],[RMC Approval Expected Date],[RMC Approval Completed Date],[BOD Approval],[BOD Approval Expected Date],[BOD Approval Completed Date])
) pvt1 where PROJ_ID is not null ) AS Approval ON ProdType.PROJ_ID = Approval.PROJ_ID
LEFT JOIN (SELECT *
FROM ( SELECT PROJ.PROJ_ID,
ORG_SHRT_NM
FROM dbo.Project PROJ
left join dbo.PROJ_LGL_ENT_IMPCT PLEI on PROJ.PROJ_ID = PLEI.PROJ_ID
right outer join dbo.LGL_ENT LE on PLEI.LGL_ENT_ID = LE.LGL_ENT_ID
) data
PIVOT
( MAX(ORG_SHRT_NM)
FOR ORG_SHRT_NM IN ([AECB],[FSB],[TRS])
) pvt3
where PROJ_ID is not null) AS LegalEntity ON ProdType.PROJ_ID = LegalEntity.PROJ_ID LEFT JOIN
(;with cte as
(
SELECT PCGU.PROJ_ID,
name = u.USER_LST_NM + ', '+ u.USER_FIRST_NM,
CTC_GRP_DS
FROM dbo.[user] u
left join dbo.PROJ_CTC_GRP_USER PCGU
on u.USER_ID = PCGU.USER_ID
left join dbo.CTC_GRP CG
on PCGU.CTC_GRP_ID = CG.CTC_GRP_ID
)
select *
from
(
select c1.proj_id,
c1.CTC_GRP_DS,
STUFF(
(SELECT ', ' + c2.name
FROM cte c2
where c1.proj_id = c2.proj_id
and c1.CTC_GRP_DS = c2.CTC_GRP_DS
FOR XML PATH (''))
, 1, 1, '') AS name
from cte c1
) d
pivot
(
max(name)
for CTC_GRP_DS in ([Bank Contact],[Dept2])
) piv
where PROJ_ID is not null)
AS Dept ON ProdType.PROJ_ID = Dept.PROJ_ID
I am getting error
Msg 102, Level 15, State 1, Line 84
Incorrect syntax near ';'.
Msg 102, Level 15, State 1, Line 115
Incorrect syntax near ')'.
I am totally confused what i am missing it. I recently started pivot & dynamic query concepts...Please guide on it...
Your error is here.
(;with cte as
(
SELECT PCGU.PROJ_ID,
name = u.USER_LST_NM + ', '+ u.USER_FIRST_NM,
CTC_GRP_DS
FROM dbo.[user] u
left join dbo.PROJ_CTC_GRP_USER PCGU
on u.USER_ID = PCGU.USER_ID
left join dbo.CTC_GRP CG
on PCGU.CTC_GRP_ID = CG.CTC_GRP_ID
)
select *
from
(
select c1.proj_id,
c1.CTC_GRP_DS,
STUFF(
(SELECT ', ' + c2.name
FROM cte c2
where c1.proj_id = c2.proj_id
and c1.CTC_GRP_DS = c2.CTC_GRP_DS
FOR XML PATH (''))
, 1, 1, '') AS name
from cte c1
) d
You cannot have a CTE in sub selects like that.
You could try to put the CTE at the very top, or create a temp table for the select.
The error is with your CTE. You can't stick a CTE inside a query like that
(;with cte as

Stored Procedure with CTE to query Category , Sub Category , Sub Sub Category

I have written an SP with CTE.
CREATE PROC [dbo].[CategoryListShow]
#id AS INT
AS
WITH CategoryList
AS
(
SELECT parent.Categoryid, CONVERT(varchar(50),parent.CategoryName)
as
Name, parent.CategoryParentid
FROM Category as parent
WHERE parent.CategoryParentid IS NULL
UNION ALL
SELECT child.Categoryid, CONVERT(varchar(50),CL.Name + ' > ' + child.CategoryName)
as Name, child.CategoryParentid
FROM Category as child
INNER JOIN CategoryList as CL ON child.CategoryParentid = CL.Categoryid
WHERE child.CategoryParentid IS NOT NULL
)
SELECT Name from CategoryList option (maxrecursion 0)
How can I achieve the desired output? For example, if user types id = 14111 then output should be this: Everything Else > Test Auctions > General
My table structure:
Thanks
You can do this
;with
CTE_Data as
(
select C.CategoryID, cast(C.CategoryName as nvarchar(max)) as CategoryName
from Category as C
where C.CategoryID = C.CategoryParentId
union all
select C.CategoryID, CD.CategoryName + ' > ' + C.CategoryName
from Category as C
inner join CTE_Data as CD on CD.CategoryID = C.CategoryParentId
where C.CategoryID <> C.CategoryParentId
)
select *
from CTE_Data
where CategoryID = #ID
or the other way around:
;with
CTE_Data as
(
select C.CategoryID, cast(C.CategoryName as nvarchar(max)) as CategoryName, C.CategoryParentId
from Category as C
where C.CategoryID = #ID
union all
select C.CategoryID, cast(C.CategoryName as nvarchar(max)) + ' > ' + CD.CategoryName, C.CategoryParentId
from Category as C
inner join CTE_Data as CD on CD.CategoryParentId = C.CategoryID
where CD.CategoryID <> C.CategoryID
)
select CategoryName
from CTE_Data
where CategoryID = CategoryParentId
SQL FIDDLE

Resources