SQL Count entries with value per Date - sql-server

I have a table in SQL Server:
CREATE TABLE healthRegistration
(
ID uniqueidentifier PRIMARY KEY,
RegisterDateTime DateTime NOT NULL,
RUFeelingWell Bool NOT NULL
);
The RegisterDateTime is the date and time that the user created the register and RUFeelingWell is a boolean value.
I want to create a query that returns 3 columns: Register Date, Count of entries with RUFeelingWell = False on that date, Count of entries with RUFeelingWell = True on that date
How can I do this?

SELECT CAST(T.RegisterDateTime AS DATE)REGISTERDATE,
SUM
(
CASE
WHEN T.RUFeelingWell =1 THEN 1
ELSE 0
END
)AS TRUE_RECORDS,
SUM
( CASE
WHEN T.RUFeelingWell =0 THEN 1
ELSE 0
END
)AS FALSE_RECORDS
FROM healthRegistration AS T
GROUP BY CAST(T.RegisterDateTime AS DATE)

Try this query:
Select
Convert(char(8), RegisterDateTime, 112),
Sum(Case RUFeelingWell When 1 Then 1 Else 0 End) As wellnos,
Sum(Case RUFeelingWell When 0 Then 1 Else 0 End) As notwellnos
From healthRegistration
Group By Convert(char(8), RegisterDateTime, 112)
Order By Convert(char(8), RegisterDateTime, 112)
Here Convert(char(8), RegisterDateTime, 112) gives date in YYYYMMDD format so that entries for a day are summed together.
Boolean values are stored as 1 and 0 in the database

Related

Comparing SQL date ranges across multiple nullable date columns

I have a calendar type check I'm trying to do on SQL Server. For each month of the year, I need to check if the employee was hired or not. There can be an original hire date, a rehire date, a termination date, and the last termination date; other than the original hire date, which will always have a value, all of these date fields can be null.
Given the following data:
EmpID OrigHireDate TermDate LastTermDate RehireDate
42 2017-09-25 NULL 2019-03-26 2019-10-30
What I am trying to achieve is the following result for each month for last year (i.e. 2019) and having no luck in coming up with the right statement. Assume I already have a table containing each month's number along with the start/end date of the month that I can use to compare the date ranges.
EmpID Month EmployeeDuring
42 1 True
42 2 True
42 3 True
42 4 False
42 5 False
42 6 False
42 7 False
42 8 False
42 9 False
42 10 True
42 11 True
42 12 True
The following works. May need some minor adjustments to handle all possible combinations of EmpID, OrigHireDate, TermDate, LastTermDate, RehireDate
I apologize for posting mostly code. Will add more explanation and or comments tomorrow.
DECLARE #EmpID int, #OrigHireDate date, #TermDate date, #LastTermDate date, #RehireDate date
DECLARE #year int
SET #year = 2019
SET #EmpID = 42
SET #OrigHireDate = '2017-09-25'
SET #TermDate = NULL
SET #LastTermDate = '2019-03-26'
SET #RehireDate = '2019-10-30'
SET #OrigHireDate = DATEADD(day,-DAY(#OrigHireDate)+1, #OrigHireDate)
SET #LastTermDate = DATEADD(day,-DAY(ISNULL(#LastTermDate,GETDATE()))+1, #LastTermDate)
SET #RehireDate = DATEADD(day,-DAY(#RehireDate)+1, #RehireDate)
SET #TermDate = DATEADD(day,-DAY(ISNULL(#TermDate,GETDATE()))+1, #TermDate)
;WITH CTE_DATES_ORIGINAL([Date],[Level])
AS
(
SELECT #OrigHireDate AS [DATE],
1 AS [Level]
UNION ALL
SELECT
DATEADD(MONTH,1, [DATE] ) , [Level] + 1
FROM CTE_DATES_ORIGINAL
WHERE [DATE] < ISNULL(#LastTermDate,GETDATE())
),
CTE_DATES_REHIRE([Date],[Level])
AS
(
SELECT #RehireDate AS [DATE],
1 AS [Level]
UNION ALL
SELECT
DATEADD(MONTH,1, [DATE] ) , [Level] + 1
FROM CTE_DATES_REHIRE
WHERE [DATE] < ISNULL(#TermDate,GETDATE())
),
CTE_DATES_YEAR(m) AS
(
SELECT 1
UNION ALL
SELECT m+1
FROM CTE_DATES_YEAR
WHERE m < 12
)
SELECT #EmpID AS EmpID, m AS [Month], ISNULL(EmployeeDuring.EmployeeDuring,0) AS EmployeeDuring
FROM CTE_DATES_YEAR y
LEFT OUTER JOIN
(
SELECT
[Date], 1 AS EmployeeDuring
FROM
CTE_DATES_ORIGINAL
UNION
SELECT
[Date] , 1 AS EmployeeDuring
FROM
CTE_DATES_REHIRE
) employeeDuring
ON DATEADD(month,m-1, CAST(CAST(#year AS CHAR(4)) + '-1-1' AS DATE)) = employeeDuring.[Date]
ORDER BY m
OPTION (MAXRECURSION 5000)

Daily report with stored procedure

I have this sp to make a report which needs to be daily. How can I implement the day part? It is OK like I wrote or that is some easy way?!
ALTER PROCEDURE [dbo].[pr_Report]
#YearOfRegistration INT
AS
SELECT
peCountryID,
peCountryName as coName,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 1 THEN 1 ELSE 0 END ),0) AS rdValue1,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 0 THEN 1 ELSE 0 END ),0) AS rdValue2,
COUNT(*) AS Total
FROM
vPerson
WHERE
#YearOfRegistration = 0
OR peYearOfRegistration = #YearOfRegistration
AND (DATEPART(dd, peSubmitDate) = DATEPART(dd, GETDATE())
AND DATEPART(MM, peSubmitDate) = DATEPART(MM, GETDATE())
AND DATEPART(yy, peSubmitDate) = DATEPART(YY, GETDATE()))
GROUP BY
peCountryofResidencyID, peCountryOfResidencyName
The logic is correct, but it is a really bad way to do it. Wherever possible to you should avoid calling functions on your data, especially in the where clause, because it means that any indexes on the underlying columns can not be used.
Your predicate would be better written as:
WHERE peSubmitDate >= CAST(GETDATE() AS DATE)
AND peSubmitDate < DATEADD(DAY, 1, CAST(GETDATE() AS DATE));
This way indexes can be used, and your query is sargable
As it happens, converting DATETIME to DATE (and vice versa) is actually an exception to the rule of not using functions, so you can shorten this to:
WHERE CONVERT(DATE, peSubmitDate) = CONVERT(DATE, GETDATE())
Another point is that although it might look better to use OR to accommodate both your options (of filtering by year or returning all records), you will find that having two separate queries will perform better. so your final SP might be:
ALTER PROC [dbo].[pr_Report] #YearOfRegistration INT
AS
BEGIN
IF (#YearOfRegistration = 0)
BEGIN
SELECT peCountryID,
peCountryName as coName,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 1 THEN 1 ELSE 0 END ),0) AS rdValue1,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 0 THEN 1 ELSE 0 END ),0) AS rdValue2,
COUNT(*) AS Total
FROM vPerson
WHERE CONVERT(DATE, peSubmitDate) = CONVERT(DATE, GETDATE())
GROUP BY peCountryofResidencyID,peCountryOfResidencyName
END
ELSE
BEGIN
SELECT peCountryID,
peCountryName as coName,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 1 THEN 1 ELSE 0 END ),0) AS rdValue1,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 0 THEN 1 ELSE 0 END ),0) AS rdValue2,
COUNT(*) AS Total
FROM vPerson
WHERE CONVERT(DATE, peSubmitDate) = CONVERT(DATE, GETDATE())
AND peYearOfRegistration = #YearOfRegistration
GROUP BY peCountryofResidencyID,peCountryOfResidencyName;
END
END
getdate returns a datetime, so if you want to compare just the date and not the time of day, you could use cast. If peSubmitDate is of datatype Date , use this comparison:
peSubmitDate = cast(GetDate() as date)
If it is a datatime then use it like this:
cast(peSubmitDate as date) = cast(GetDate() as date)
The latter one gives worse performance, so only use this if is a datetime

Modify Date Function Based on Where Clause SQL Server

Currently my case statement is based on the first day of the current month.
SELECT
[TicketNbr] AS 'Ticket Nbr'
-- 1st day of this month
, ( CASE WHEN [date_entered] >= DATEADD(day,-1, GETDATE()) THEN 1 ELSE 0 END) AS '1=Opened Within Last 24HR 0=No'
FROM [v_rpt_Service] WITH(NOLOCK)
WHERE ([date_entered] >= '2017-04-01T11:24:00.000' AND [date_entered] < '2017-05-01T11:24:00.000')
GROUP BY [TicketNbr]
But I want to modify it so it will adjust so it will based on the date range
in the where clause. (And that can be any date ranges.)
How do I do this without variables?
Sounds like you just need variables
declare #startDate datetime = '2017-04-01 11:24:00'
declare #endDate datetime = '2017-05-01 11:24:00'
SELECT
[TicketNbr] AS 'Ticket Nbr'
-- 1st day of this month
, ( CASE WHEN [date_entered] between #startDate and #endDate THEN 1 ELSE 0 END) AS '1=Opened Within Date Range 0= No'
FROM [v_rpt_Service] WITH(NOLOCK)
WHERE ([date_entered] >= startDate AND [date_entered] < #endDate)
GROUP BY [TicketNbr]

NetWorkDays Function in SQL SERVER 2012

Is there a function in sql server 2012 which calculates only working days?
I have been searching but with no luck so far.
Thanks!
No, SQL Server doesn't have such functions, but you can use calendar table:
DECLARE #date_start date = '2016-01-01',
#date_end date = '2016-12-31';
WITH cte as (
SELECT #date_start as [d], 0 as Level
UNION ALL
SELECT DATEADD(day,1,[d]), [level] + 1 as [level]
from cte
WHERE [level] < DATEDIFF(day,#date_start,#date_end)
),
holidays as ( --table with holidays (USA)
SELECT * FROM (VALUES
('2016-01-01'),
('2016-01-18'),
('2016-02-15'),
('2016-05-30'),
('2016-07-04'),
('2016-09-05'),
('2016-10-10'),
('2016-11-11'),
('2016-11-24'),
('2016-12-26')) as t(d)
)
SELECT c.d,
CASE WHEN DATEPART(WEEKDAY,c.d) IN (1,7) THEN 0 --Saturday and Sunday, use (6,7) for Friday,Saturday
WHEN h.d IS NOT NULL THEN 0
ELSE 1 END as isWorking
FROM cte c
LEFT JOIN holidays h
ON c.d=h.d
OPTION (MAXRECURSION 1000);
It will generate a table with all dates in 2016 year and flag - is the day working or not.
Below is the high level overview of how you can do this..
Create a dummy table which holds dates in this format...
date isholiday
20160101 1
20160102 0
Now from your main table which holds employees attendance ,join above table like..
select empid,sum(Case when mt.datee is not null then 1 else 0 end) as workingdays
from
dummydatees dt
left join
maintable mt
on dt.datee=mt.datee
where dt.isholiday=0
This script will calculate the total working days excluding Saturday, Sunday and holidays. I have to list all the holidays since I don't have a table for holidays. You can modify it so that it will meet your requirements.
DECLARE #MyCounter int = 0, #TempDate datetime, #EndDate datetime;
SET #TempDate = DATEADD(d,1,'2017-5-27')
SET #EndDate = '2017-6-3'
WHILE #TempDate <= #EndDate
BEGIN
IF DATENAME(DW,#TempDate) = 'Sunday' OR DATENAME(DW,#TempDate) = 'Saturday'
SET #MyCounter = #MyCounter
ELSE IF #TempDate not in ('2017-1-1', '2017-1-16', '2017-2-20', '2017-5-29', '2017-7-4', '2017-9-4', '2017-10-9', '2017-11-11', '2017-12-25')
SET #MyCounter = #MyCounter + 1
SET #TempDate = DATEADD(d,1,#TempDate)
CONTINUE
END
PRINT #MyCounter
PRINT #TempDate

Operand data type varchar is invalid for sum operator using a sum function with an imbedded datediff in a case statement

I am trying to sum the total number of rows that have a '2' as the difference between col1 and col2. But when I use the below query, I get the "operand data type varchar is invalid for sum operator".
I am not sure if this matters but here are some of the facts;
col1 data type is datetime
col2 data type is datetime
col3 data type is datetime
Query:
SELECT
SUM(CASE WHEN datediff(day,_col1,_col2) = '0' THEN '1' ELSE '0' END) day0
FROM dbo1
WHERE year(_col3) between year(getdate())-'1' and year(getdate())
AND _col1 is not null
AND _col2 is not null
SELECT SUM(CASE WHEN datediff(day,_col1,_col2) = 0 THEN 1 ELSE 0 END) day0
FROM dbo1
WHERE _col3 >= dateadd(year, datediff(year, 0, getdate()) - 1, 0)
and _col3 < dateadd(year, datediff(year, 0, getdate()) + 1, 0)
AND _col1 is not null
AND _col2 is not null
You use chars as a argument for the sum method.
why u didn't use the NUMBER!?
try this:
SELECT
SUM(CASE WHEN datediff(day,_col1,_col2) = 0 THEN 1 ELSE 0 END) day0
FROM dbo1
WHERE year(_col3) between year(getdate())-1 and year(getdate())
AND _col1 is not null
AND _col2 is not null

Resources