Find Count With Pivot Data SQL Server - sql

I have been displaying Day Wise Attendance Data with Pivot SQL.
declare #startdate datetime = '2016-09-26'
declare #enddate datetime = '2016-10-01'
declare #CompanyID int = 1
DECLARE #COLUMN VARCHAR(MAX), #SQL NVARCHAR(MAX);
SET #COLUMN = N'';
DECLARE #DATERANGE TABLE (DateToCheck DATE)
;WITH Temp
AS
(
SELECT DT =DATEADD(DD,0, #startdate)
WHERE DATEADD(DD, 1, #startdate) <= #enddate
UNION ALL
SELECT DATEADD(DD, 1, DT)
FROM Temp
WHERE DATEADD(DD, 1, DT) <= #enddate
)
INSERT INTO #DATERANGE
SELECT DT From Temp
SELECT #COLUMN += N', T.' + QUOTENAME(DateRanges) FROM (SELECT CAST(CONVERT(DATE, T.DateToCheck) AS VARCHAR(10)) AS DateRanges FROM #DATERANGE T group by T.DateToCheck) AS A;
SET #SQL = '
DECLARE #DATERANGE TABLE (DateToCheck DATE)
;WITH Temp
AS
(
SELECT DT =DATEADD(DD,0, #startdate)
WHERE DATEADD(DD, 1, #startdate) <= #enddate
UNION ALL
SELECT DATEADD(DD, 1, DT)
FROM Temp
WHERE DATEADD(DD, 1, DT) <= #enddate
)
INSERT INTO #DATERANGE
SELECT DT From Temp
SELECT *
FROM (
SELECT E.FirstName, E.LastName, E.Email, T.DateToCheck, COALESCE(A.val, L.val, H.val, ''Absent'') val
FROM AspNetUsers E
CROSS APPLY (
SELECT DateToCheck FROM #DATERANGE
) T--(DateToCheck)
LEFT JOIN (SELECT ''Holiday'' val, HolidayDate, CompanyID FROM Holidays) H ON H.HolidayDate = T.DateToCheck AND H.CompanyID = #CompanyID
LEFT JOIN (SELECT ''In : '' + CONVERT(VARCHAR, MIN(AttendanceDateTime), 108) + '' / Out : '' + CONVERT(VARCHAR, MAX(AttendanceDateTime), 108) val, CAST(AttendanceDateTime As DATE) As AttendanceDate, UserID FROM Attendances GROUP BY CAST(AttendanceDateTime As DATE), UserID) A ON A.AttendanceDate = T.DateToCheck AND A.UserID = E.Id
LEFT JOIN (SELECT ''Leave'' val, LeaveDate, UserID FROM LeaveApplications) L ON L.LeaveDate = T.DateToCheck AND L.UserID = E.Id
WHERE E.CompanyID = #CompanyID
) T
PIVOT (MAX(val) FOR DateToCheck IN (' + STUFF(REPLACE(#COLUMN, ', T.[', ',['), 1, 1, '') + ')) P';
EXEC sp_executesql #SQL, N'#startdate DATE, #enddate DATE, #CompanyID INT', #startdate, #enddate, #CompanyID
And below is the result set of how it's now
Now I wish to add more field to the above SQL to display counts like PresentCount, AbsentCount, HolidayCount and LeaveCount
I have already written a SQL where I could easily display these counts but, I'm unable to make it work with the above PIVOT SQL.
So in this case, the result for first row would be PresentCount = 0, AbsentCount = 6, HolidayCount = 0 and LeaveCount = 0. For Row 2 it would be PresentCount = 4, AbsentCount = 2, LeaveCount and HolidayCount both is 0.

Related

Fill in the date gaps from the stuff

I have this result, [year.month week] with my query below
[2021.12 51],[2021.12 53],[2022.01 1],[2022.01 2],[2022.01 3],[2022.01 5],[2022.01 6],[2022.02 10]
My goal is to fill in the gaps of the week, for example:
[2021.12 51],**[2021.12 52]**,[2021.12 53],[2022.01 1],[2022.01 2],[2022.01 3],**[2022.01 4]**,[2022.01 5],[2022.01 6],**[2022.01 7]**,**[2022.01 8]**,**[2022.01 9]**,[2022.02 10]
How could I do this with my query below:
DECLARE
#startdate datetime,
#enddate datetime,
#paramdef nvarchar(max)
set #startdate = '2021-01-01 00:00:00.000';
set #enddate = GETDATE();
set #paramdef = '#startdate datetime, #enddate datetime';
SELECT STUFF
(
(
SELECT
',' + QUOTENAME( S_FORMATTED_WEEK_MONTH )
FROM
MONITORING
WHERE
S_DATE_TO >= #startdate AND
S_DATE_TO <= #enddate
GROUP BY
S_FORMATTED_WEEK_MONTH
ORDER BY
CAST( REPLACE( SUBSTRING(S_FORMATTED_WEEK_MONTH, 1, 7), '.', '' ) AS INT ),
CAST( REPLACE( SUBSTRING(S_FORMATTED_WEEK_MONTH, 8, 9), '.', '' ) AS INT )
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)') ,1,1,''
)
you can use the below calendar table approach to get the list of weeknumbers as per your format.
DECLARE #StartDate DATE = '2021-01-01'
DECLARE #EndDate DATE = getdate()
;WITH Cal(n) AS
(
SELECT 0 UNION ALL SELECT n + 1 FROM Cal
WHERE n < DATEDIFF(DAY, #StartDate, #EndDate)
),
FnlDt(d,y,m,w) AS
(
SELECT DATEADD(DAY, n, #StartDate)
, year(DATEADD(DAY, n, #StartDate))
, month(DATEADD(DAY, n, #StartDate))
, datepart(week,DATEADD(DAY, n, #StartDate)) FROM Cal
)
select
string_agg(weekarr,',') as concat_weekarr
from
(SELECT concat('[',y,'.',m,' ',w,']') as weekarr
FROM fnlDt
group by y,m,w
) as dt
option(maxrecursion 32767)
[2021.1 1],[2021.1 2],[2021.1 3],[2021.1 4],...

Student Attendance Report Month-wise SQL Query

There are 3 tables -
Attendance (EnrollmentNo,SubjectCode,Date,Attendance)
Student (EnrollmentNo, RollNo),
UserDetails(EnrollmentNo,FirstName,LastName).
Now what I want is to display the attendance month-wise taking Roll No, Name, dates as column and Student.RollNo, UserDetails.FirstName, UserDetails.LastName, Attendance.Attendance as the data for the columns respectively.
But the problem I am facing is how to generate date columns dynamically and put the attendance data in the respective date column.
Input - Startdate and Enddate
Expected Output -
-------------------------------------------------------
| Roll No | Name | 01-09-2018 | 01-12-2018|
-------------------------------------------------------
| 15 | Suyash Gupta | 1 | 0 |
-------------------------------------------------------
| 24 | Himanshu Shukla | 2 | 2 |
-------------------------------------------------------
| 32 | Dhruv Raj Sirohi | 1 | 1 |
-------------------------------------------------------
This is my approach -
DECLARE #startdate date
DECLARE #enddate date
SET #startdate = convert(date,'01-09-2018')
SET #enddate = convert(date,'01-12-2018')
;with cte (#startdate, #enddate) as /*I don't know how to pass my date range
in cte() as this takes table column*/
(
select 1
union all
select dateadd(dd, 1, startdate)
from cte
where startdate <= enddate
)
select c.startdate
into #tempDates
from cte c
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(convert(CHAR(10),
startdate, 120))
from #tempDates
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT RollNo,FirstName,LastName, ' + #cols + ' from
(
select S.RollNo,U.FirstName,U.LastName,
D.startdate,
convert(CHAR(10), startdate, 120) PivotDate
from #tempDates D
left join Attendance A
on D.startdate = A.Date
) x
pivot
(
count(startdate)
for PivotDate in (' + #cols + ')
) p '
execute(#query)
you have some issues with your code, look at the differences in my code:
DECLARE #startdate date = '20180109';
DECLARE #enddate date = '20180112';
DECLARE #cols as varchar(2000);
DECLARE #query as varchar(MAX);
WITH cte (startdate)
AS
(SELECT
#startdate AS startdate
UNION ALL
SELECT
DATEADD(DAY, 1, startdate) AS startdate
FROM cte
WHERE startdate < #enddate)
SELECT
#cols = STUFF((SELECT DISTINCT
',' + QUOTENAME(CONVERT(CHAR(10),
startdate, 120))
FROM cte
FOR XML PATH (''), TYPE)
.value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
SET #query = 'SELECT RollNo,FirstName,LastName, ' + #cols + ' from
(
select S.RollNo,U.FirstName,U.LastName,
D.startdate,
convert(CHAR(10), startdate, 120) PivotDate
from #tempDates D
left join Attendance A
on D.startdate = A.Date
) x
pivot
(
count(startdate)
for PivotDate in (' + #cols + ')
) p '
EXECUTE (#query)
Do you mean something like this:
DECLARE #StartDate AS DATETIME, #EndDate AS DATETIME
SET #StartDate='2018-01-01'
SET #EndDate='2018-01-10'
SELECT Student.RollNo, Student.FirstName, Student.LastName, Attendance.Date
FROM Attendance,Student,UserDetails
WHERE Attendance.EnrollmentNo=Student.EnrollmentNo
AND UserDetails.EnrollmentNo=Student.EnrollmentNo
AND Attendance.[Date] between #StartDate and #EndDate
UPDATE
Based on what you said in the question maybe the following code is what you need:
SELECT Student.RollNo, Student.FirstName, Student.LastName,
STUFF((SELECT ','+CAST([DATE] AS VARCHAR(30))
FROM Attendance
WHERE Attendance.EnrollmentNo=Student.EnrollmentNo
AND Attendance.[Date] between #StartDate AND #EndDate
FOR XML PATH('')
), 1, 1, '') Dates
FROM Student,UserDetails
WHERE UserDetails.EnrollmentNo=Student.EnrollmentNo
This is running code and giving me the desired output. I want to thank Lobo, who corrected me, and everyone else who had put in the efforts to help me. Thank you all and stackoverflow who provided me the platform to query the problem I was facing.
DECLARE #startdate date = '20180109';
DECLARE #enddate date = '20180112';
DECLARE #cols as varchar(2000);
DECLARE #query as varchar(MAX);
WITH cte (startdate)
AS
(SELECT
#startdate AS startdate
UNION ALL
SELECT
DATEADD(DAY, 1, startdate) AS startdate
FROM cte
WHERE startdate < #enddate
)
select c.startdate
into #tempDates
from cte c
SELECT
#cols = STUFF((SELECT DISTINCT
',' + QUOTENAME(CONVERT(CHAR(10),
startdate, 120))
FROM #tempDates
FOR XML PATH (''), TYPE)
.value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
SET #query = 'SELECT RollNo,FirstName,LastName, ' + #cols + ' from
(
select S.RollNo,U.FirstName,U.LastName,
D.startdate,
convert(CHAR(10), startdate, 120) PivotDate
from #tempDates D,Attendance A, Student S, UserDetails U
where D.startdate = A.Date and A.EnrollmentNo=S.EnrollmentNo and A.EnrollmentNo=U.userID
) x
pivot
(
count(startdate)
for PivotDate in (' + #cols + ')
) p '
EXECUTE (#query)
drop table #tempDates

Ignore Sunday while generating student attendance report in SQL

This code gives me the attendance report between two dates passed as a parameter.
I will pass dates respective of the month selected from the C# code.
But I want to skip Sundays while generating the attendance report. How can I achieve this?
DECLARE #startdate date = '20180109';
DECLARE #enddate date = '20180112';
DECLARE #cols as varchar(2000);
DECLARE #query as varchar(MAX);
WITH cte (startdate)
AS
(SELECT
#startdate AS startdate
UNION ALL
SELECT
DATEADD(DAY, 1, startdate) AS startdate
FROM cte
WHERE startdate < #enddate
)
select c.startdate
into #tempDates
from cte c
SELECT
#cols = STUFF((SELECT DISTINCT
',' + QUOTENAME(CONVERT(CHAR(10),
startdate, 120))
FROM #tempDates
FOR XML PATH (''), TYPE)
.value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
SET #query = 'SELECT RollNo,FirstName,LastName, ' + #cols + ' from
(
select S.RollNo,U.FirstName,U.LastName,
D.startdate,
convert(CHAR(10), startdate, 120) PivotDate
from #tempDates D,Attendance A, Student S, UserDetails U
where D.startdate = A.Date and A.EnrollmentNo=S.EnrollmentNo and A.EnrollmentNo=U.userID
) x
pivot
(
count(startdate)
for PivotDate in (' + #cols + ')
) p '
EXECUTE (#query)
drop table #tempDates
How about changing #TempDates?
select c.startdate
into #tempDates
from cte c
where datename(weekday, c.startdate) <> 'Sunday';
This assumes that your internationalization settings are set to "English".

Conditional Join or ignore results when second value is null

I've tried to focus on just the portion of my main query giving me trouble. This query is being used to populate an SSRS report which has an optional date parameter. This query snippet is used in a LEFT Join. If no date is specified, all matching records should be returned. That seems to work. But if I specify a date and no records are returned in the second column, no results for that row should be displayed. Not sure if I explained that well.
Here is the snippet:
DECLARE #DispoStartDt datetime, #DispoEndDt datetime, #DispoCode varchar(800)
SET #DispoStartDt = '5-1-2050'
--SET #DispoEndDt = '12-31-2014'
SET #DispoCode = '_NULL'
SELECT c1.CaseID,
STUFF((SELECT '; ' + CONVERT(VARCHAR(10), ct.DispoDt, 101)
FROM jw50_CountInvPers ct
WHERE ct.CaseID = '00-100'
AND (ct.DispoDt >= DATEADD(dd, DATEDIFF(dd, 0, ISNULL(#DispoStartDt, ct.DispoDt)), 0) AND ct.DispoDt < DATEADD(dd, DATEDIFF(dd, -1, ISNULL(#DispoEndDt, ct.DispoDt)), 0))
ORDER BY DispoDesc
FOR XML PATH('')), 1, 1, '') [DispoDts]
FROM jw50_Case c1
WHERE c1.CaseID = '00-100'
GROUP BY c1.CaseID
My results are:
CaseID DispoDts
00-100 NULL
I want no records returned if DispoDts is NULL. Since I cannot reference a column alias, is there another way than putting this in a sub select?
EDIT:
Here is the snippet used in my Join. I'm getting rows with NULL DispoDts because of the left join. If I specify a start date, I want the results to be treated like an inner join, only return results where they match a join row. If I don't specify a date, give me everything in the main table if a join exists or not.
DECLARE #DispoStartDt datetime, #DispoEndDt datetime, #DispoCode varchar(800)
SET #DispoStartDt = '5-1-2015'
--SET #DispoEndDt = '12-31-2014'
SET #DispoCode = '_NULL'
SELECT c.CaseID
, c.ProsNum AS FileNum
, c.CaseStatusDesc AS CaseStatus
, disp.DispoDts
FROM jw50_Case c
LEFT JOIN (SELECT CaseID, DispoDts
FROM (
SELECT c1.CaseID,
STUFF((SELECT '; ' + CONVERT(VARCHAR(10), ct.DispoDt, 101)
FROM jw50_CountInvPers ct
WHERE ct.CaseID = c1.CaseId
AND (ct.DispoDt >= DATEADD(dd, DATEDIFF(dd, 0, ISNULL(#DispoStartDt, ct.DispoDt)), 0) AND ct.DispoDt < DATEADD(dd, DATEDIFF(dd, -1, ISNULL(#DispoEndDt, ct.DispoDt)), 0))
ORDER BY DispoDesc
FOR XML PATH('')), 1, 1, '') [DispoDts]
FROM jw50_Case c1
WHERE c1.CaseID = c1.CaseId
GROUP BY c1.CaseID
) a
WHERE DispoDts IS NOT NULL
) disp ON disp.CaseID = c.CaseID
ORDER BY CaseID, DispoDts
EDIT 2:
Ok, feels dirty, but it works.
WHERE (#DispoStartDt IS NULL AND #DispoEndDt IS NULL)
OR (#DispoStartDt IS NOT NULL AND #DispoEndDt IS NOT NULL AND disp.DispoDts IS NOT NULL)
OR (#DispoStartDt IS NOT NULL AND #DispoEndDt IS NULL AND disp.DispoDts IS NOT NULL)
OR (#DispoStartDt IS NULL AND #DispoEndDt IS NOT NULL AND disp.DispoDts IS NOT NULL)
You can try:
DECLARE #DispoStartDt datetime, #DispoEndDt datetime, #DispoCode varchar(800)
SET #DispoStartDt = '5-1-2050'
--SET #DispoEndDt = '12-31-2014'
SET #DispoCode = '_NULL'
SELECT CaseID,
[DispoDts]
FROM (
SELECT c1.CaseID,
STUFF((SELECT '; ' + CONVERT(VARCHAR(10), ct.DispoDt, 101)
FROM jw50_CountInvPers ct
WHERE ct.CaseID = '00-100'
AND (ct.DispoDt >= DATEADD(dd, DATEDIFF(dd, 0, ISNULL(#DispoStartDt, ct.DispoDt)), 0) AND ct.DispoDt < DATEADD(dd, DATEDIFF(dd, -1, ISNULL(#DispoEndDt, ct.DispoDt)), 0))
ORDER BY DispoDesc
FOR XML PATH('')), 1, 1, '') [DispoDts]
FROM jw50_Case c1
WHERE c1.CaseID = '00-100'
GROUP BY c1.CaseID
) as tbaux
WHERE [DispoDts] IS NOT NULL
This way you get the same values ​​as before, but the result is passed to a query above which validates the DispoDts
And this way ?
DECLARE #DispoStartDt datetime, #DispoEndDt datetime, #DispoCode varchar(800)
SET #DispoStartDt = '5-1-2015'
--SET #DispoEndDt = '12-31-2014'
SET #DispoCode = '_NULL'
SELECT c.CaseID
, c.ProsNum AS FileNum
, c.CaseStatusDesc AS CaseStatus
, disp.DispoDts
FROM jw50_Case c
LEFT JOIN (SELECT c1.CaseID,
STUFF((SELECT '; ' + CONVERT(VARCHAR(10), ct.DispoDt, 101)
FROM jw50_CountInvPers ct
WHERE ct.CaseID = c1.CaseId
AND (ct.DispoDt >= DATEADD(dd, DATEDIFF(dd, 0, ISNULL(#DispoStartDt, ct.DispoDt)), 0) AND ct.DispoDt < DATEADD(dd, DATEDIFF(dd, -1, ISNULL(#DispoEndDt, ct.DispoDt)), 0))
ORDER BY DispoDesc
FOR XML PATH('')), 1, 1, '') [DispoDts]
FROM jw50_Case c1
WHERE c1.CaseID = c1.CaseId
GROUP BY c1.CaseID
) disp ON disp.CaseID = c.CaseID
WHERE #DispoEndDt IS NULL OR disp.DispoDts IS NOT NULL
ORDER BY CaseID, DispoDts

Getting null value in the Grand Total column in the Pivot Table in sql

I have the query which returning me the number of categories in one cloumn and other column are dynamic column they are giving me the the months between start date & end date and this column are returning me the amount of the categories sold on that month.
I want to add the Grand Total at the end of the row in the query
Here's My Query
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#subtotal AS FLOAT,
#startdate as datetime,
#enddate as datetime
DECLARE #ColumnsRollup AS VARCHAR (MAX)
set #startdate = '1-Mar-2014'
set #enddate = '1-Aug-2014'
;with cte (StartDate, EndDate) as
(
select min(#startdate) StartDate, max(#enddate) EndDate
union all
select dateadd(mm, 1, StartDate), EndDate
from cte
where StartDate < EndDate
)
select StartDate
into #tempDates
from cte
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(convert(CHAR(10), StartDate, 120))
from #tempDates
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'') + ',[Total]'
SET #query = 'select ledger_name,
' + #cols + '
from
(
SELECT
CASE WHEN (GROUPING(ledger_name) = 1) THEN ''Grand Total''
ELSE ledger_name END AS ledger_name,
ISNULL(SUM(amount),0) Amount,
CASE WHEN (GROUPING(StartDate) = 1) THEN ''Total''
ELSE convert(CHAR(10), StartDate, 120) END StartDate
FROM #tempDates d
left join Rs_Ledger_Master AS LM on d.StartDate between '''+convert(varchar(10), #startdate, 120)+''' and '''+convert(varchar(10), #enddate, 120)+'''
LEFT OUTER JOIN RS_Payment_Master AS PM ON PM.ledger_code = LM.ledger_code and month(paid_date) = month(StartDate) and year(paid_date) = year(StartDate)
group by
ledger_name,StartDate WITH ROLLUP
) d
pivot
(
SUM(Amount)
for StartDate in (' + #cols + ')
) p
ORDER BY CASE WHEN ledger_name = ''Grand Total'' THEN 1 END'
execute sp_executesql #query;
drop table #tempDates
I think what you can do is, try to use the ROLLUP option in your inner query, which actually returns addtional row for the SUM(Amount), which you can call as Total and add this column to your column list.
Here is the change what I think you need to do
Add total column at the end of your column list
SET #cols= #cols + ',[Total]'
Add the rollup option to your inner query. Note that, the case statement is required to change the text as total for the row.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#subtotal AS FLOAT,
#startdate as datetime,
#enddate as datetime
DECLARE #ColumnsRollup AS VARCHAR (MAX)
set #startdate = '1-Mar-2014'
set #enddate = '1-Aug-2014'
;with cte (StartDate, EndDate) as
(
select min(#startdate) StartDate, max(#enddate) EndDate
union all
select dateadd(mm, 1, StartDate), EndDate
from cte
where StartDate < EndDate
)
select StartDate
into #tempDates
from cte
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(convert(CHAR(10), StartDate, 120))
from #tempDates
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'') + ',[Total]'
SET #query = 'select ledger_name,
' + #cols + '
from
(
SELECT
ledger_name,
ISNULL(SUM(amount),0) Amount,
CASE WHEN (GROUPING(StartDate) = 1) THEN ''Total''
ELSE convert(CHAR(10), StartDate, 120) END StartDate
FROM #tempDates d
left join Rs_Ledger_Master AS LM on d.StartDate between '''+convert(varchar(10), #startdate, 120)+''' and '''+convert(varchar(10), #enddate, 120)+'''
LEFT OUTER JOIN RS_Payment_Master AS PM ON PM.ledger_code = LM.ledger_code and month(paid_date) = month(StartDate) and year(paid_date) = year(StartDate)
group by
ledger_name,StartDate WITH ROLLUP
) d
pivot
(
SUM(Amount)
for StartDate in (' + #cols + ')
) p
WHERE ledger_name IS NOT NULL
UNION ALL
select ledger_name,
' + #cols + ' FROM
(SELECT ''Grand Total'' AS ledger_name,
ISNULL(SUM(amount),0) Amount,
CASE WHEN (GROUPING(StartDate) = 1) THEN ''Total''
ELSE convert(CHAR(10), StartDate, 120) END StartDate
FROM #tempDates d
left join Rs_Ledger_Master AS LM on d.StartDate between '''+convert(varchar(10), #startdate, 120)+''' and '''+convert(varchar(10), #enddate, 120)+'''
LEFT OUTER JOIN RS_Payment_Master AS PM ON PM.ledger_code = LM.ledger_code and month(paid_date) = month(StartDate) and year(paid_date) = year(StartDate)
group by StartDate WITH ROLLUP
) d
pivot
(
SUM(Amount)
for StartDate in (' + #cols + ')
) p
'
print #query
execute sp_executesql #query;
drop table #tempDates
You need additional union all to get the last summary row
You can refer the following article for using ROLLUP
http://technet.microsoft.com/en-us/library/ms189305(v=sql.90).aspx