Querying null datetime field with parameters - sql-server

I have a table on my database (SQL Server 2016):
ID (numeric(10,0), not null)
Price (numeric(12,2), not null)
StartDate (datetime, null)
EndDate (datetime, null)
(There are more fields, but this is enough to get the point across)
One particular record has 'NULL' values for StartDate and EndDate:
ID 15
Price 25
StartDate NULL
EndDate NULL
If I run the query:
SELECT * FROM [Table]
Then the record is return as expected
However the following query:
DECLARE #StartDate datetime = NULL
DECLARE #EndDate datetime = NULL
SELECT * FROM [Table]
WHERE StartDate = #StartDate AND EndDate = #EndDate
I get no records!
What am I missing?
The following does work, but it just 'feels' wrong!
DECLARE #StartDate datetime = NULL
DECLARE #EndDate datetime = NULL
SELECT * FROM [Table]
WHERE (StartDate = #StartDate OR (StartDate IS NULL AND #StartDate IS NULL))
AND (EndDate = #EndDate OR (EndDate IS NULL AND #EndDate IS NULL))
For context, the query will ultimately be run via a VB.Net application, but I'm getting this problem running the query via SSMS, so it's definitely a SQL issue.

Related

Running data for 1 year dates in SQL Server

I have a procedure where I have calls data for each day. From this procedure I wrote another procedure to insert and update in the destination table.
We have a job which runs for every day. But now I need to run the data for past 1 year (each day).
Below are my parameters in insert update procedure - if I need to get data for 18th my parameters values are like below
,#StartDate datetime = '2020-04-18'
,#EndDate datetime = '2020-04-19'
,#SkillLevel varchar(10)='Total'
,#Region varchar(20) = 'US'
If I need to get data for 17th my parameters values are like below
,#StartDate datetime = '2020-04-17'
,#EndDate datetime = '2020-04-18'
,#SkillLevel varchar(10)='Total'
,#Region varchar(20) = 'US'
Like this to get data for last 1 year I need to run the code for 365 days which takes huge effort
Could anyone suggest how to pass dates individually for 1 year with some loop.
Try this:
DECLARE #DataSource TABLE
(
[StartDate] DATE
);
DECLARE #BegDate DATE
,#EndDate DATE;
SELECT #BegDate = '2020-01-01'
,#EndDate = '2020-12-31';
DECLARE #EndNumber BIGINT;
SELECT #EndNumber = DATEDIFF(DAY, #BegDate, #EndDate);
INSERT INTO #DataSource ([StartDate])
SELECT TOP (#EndNumber) DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY T1.[number])-1, #BegDate)
FROM master..spt_values T1
CROSS JOIN master..spt_values T2;
WHILE EXISTS(SELECT 1 FROM #DataSource)
BEGIN;
SELECT TOP 1 #BegDate = [StartDate]
,#EndDate = DATEADD(DAY, 1, [StartDate])
FROM #DataSource;
--
EXEC [stored_precedure] #StartDate = #BegDate
,#EndDate = #EndDate
,#SkillLevel ='Total'
,#Region = 'ES'
--
DELETE FROM #DataSource
WHERE [StartDate] = #BegDate;
END;

Get monthly attendance for an employee

I have a query that a guy helped here in developing which gives fine result. I want to use this query in a function which later I want to use in a procedure.
CREATE FUNCTION [dbo].[fnGetWorkedDays] (#StartDate datetime, #EndDate datetime)
RETURNS int
AS
BEGIN
DECLARE #dateFrom datetime
DECLARE #dateTo datetime
SET #dateFrom = #StartDate
SET #dateTo = #EndDate
DECLARE #DAYSWORKED INT
SELECT #DAYSWORKED = (
SELECT EmpId, COUNT(*) as DaysWorked
FROM
(
SELECT DISTINCT EmpId,CAST(TimeIn AS DATE) AS [Date]
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
)T
GROUP BY EmpId
ORDER BY EmpId)
RETURN ISNULL(#DAYSWORKED,0)
END
The very first error I am getting is that
Msg 1033, Level 15, State 1, Procedure fnGetWorkedDays, Line 24
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.
I removed ORDER BY then I got another error says
Msg 116, Level 16, State 1, Procedure fnGetWorkedDays, Line 25
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
I suggest you to go though this to understand more on table valued functions vs scalar valued function Difference between scalar, table-valued, and aggregate functions in SQL server?
Regarding your scenario, you should be writing a table values function for this like following.
CREATE FUNCTION [dbo].[fnGetWorkedDays]
(
#StartDate AS DATETIME,
#EndDate datetime
)
RETURNS TABLE
AS
RETURN
SELECT EmpId, COUNT(*) as DaysWorked
FROM
(
SELECT DISTINCT EmpId,CAST(TimeIn AS DATE) AS [Date]
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
)T
GROUP BY EmpId
To use the function you can write your queries like following.
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2018-01-01'
SET #EndDate = '2018-01-31'
select * from [dbo].[fnGetWorkedDays](#StartDate,#EndDate)
If you want this to work as a scalar value function, in that case you need to pass EmpId as parameter like following.
CREATE FUNCTION [dbo].[fnGetWorkedDaysEmpWise]
(
#StartDate AS DATETIME,
#EndDate datetime,
#EmpId INT
)
RETURNS INT
AS
BEGIN
DECLARE #RetunCount INT
SELECT #RetunCount = COUNT(DISTINCT CAST(TimeIn AS DATE))
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
AND EmpID=#EmpId
RETURN #RetunCount
END
And you can use it directly in your select clause like following.
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2018-01-01'
SET #EndDate = '2018-01-31'
SELECT [dbo].[fnGetWorkedDaysEmpWise](#StartDate,#EndDate,1)
Is this what you are looking for .?
CREATE FUNCTION [dbo].[fnGetWorkedDays] (#StartDate datetime, #EndDate datetime)
RETURNS #DaysWorked TABLE
(
Empid Int,
DaysWorked Int
)
AS
BEGIN
insert into #DaysWorked (Empid,DaysWorked)
SELECT EmpId, COUNT(*) as DaysWorked
FROM
(
SELECT DISTINCT EmpId,CAST(TimeIn AS DATE) AS [Date]
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
)T
GROUP BY EmpId
ORDER BY EmpId
RETURN
END
select * from [fnGetWorkedDays]('2018-01-01','2018-01-31')

Refine SQL date-range query

Attempting to query for when, given parameters #startDate and #endDate, a record occurs in as much of the time frame as is supplied. I have a functional where clause below, but I suspect it can be more direct.
If an end date is supplied, records will not be selected from after that date. If a start date is supplied, records will not be selected from before that date. If no dates are supplied all records will be selected.
SELECT *
FROM myTable
WHERE
(
(#startDate IS NULL AND ((#endDate IS NULL) OR (myTable.[recordDate] <= #endDate)))
OR
(#endDate IS NULL AND ((#startDate IS NULL) OR (myTable.[recordDate] >= #startDate)))
OR
(myTable.[recordDate] BETWEEN #startDate AND #endDate)
)
You could workaround with ISNULL function like below:
SELECT * FROM myTable
WHERE myTable.[recordDate] >= ISNULL(#startDate, '01/01/1900')
AND myTable.[recordDate] <= ISNULL(#endDate, getDate())
This query will select all the rows that are either:
Between #startDate and #endDate inclusive of both
Between #startDate and current date time if #endDate is null
Between 01/01/1900 if #startDate is null and todays' date time if #endDate is null
Between 01/01/1900 if #startDate is null and #endDate
Setup a temp tableceiling with floor and ceiling and join to table on date BETWEEN floor and ceiling. Setup your unbounded conditions(NULL)as 1/1/1900 and 12/31/9999. A colleague showed me this recently and it worked.

sql server optional parameters: syntax for between clause

#FromDate datetime = null
#ToDate datetime = null
SELECT * FROM TABLE
WHERE ....
AND [PI].Date BETWEEN #FromDate AND #ToDate
When any date is null, the records are not displayed. What is the correct syntax so that I can get all records if any of the dates are null.
I have thought of this:
#FromDate datetime = '01/01/1901',
#ToDate datetime = '12/31/9999'
Thanks.
Try something like
SELECT * FROM TABLE
WHERE ....
AND [PI].Date BETWEEN ISNULL(#FromDate, [PI].Date) AND ISNULL(#ToDate, [PI].Date)
you can always default the date values to some extreme value and make sure an index is used:
SELECT
*
FROM TABLE
WHERE ....
AND [PI].Date BETWEEN ISNULL(#FromDate,convert(datetime,'1/1/1800'))
AND ISNULL(#ToDate, convert(datetime,'1/1/2500'))
You can try something like this:
#FromDate datetime = null
#ToDate datetime = null
SELECT * FROM TABLE
WHERE ....
AND [PI].Date BETWEEN CASE WHEN #FromDate is null THEN '01/01/1901' ELSE #FromDate END AND
CASE WHEN #ToDate is null THEN '12/31/9999' ELSE #ToDate END
HTH
SELECT * FROM [PI]
WHERE ([PI].Date >= #FromDate OR #FromDate IS NULL)
AND ([PI].Date <= #ToDate OR #ToDate IS NULL)

SQL server SP : #Param 's with sometime NULL values

I am very new to SQL Server Stored Procedures,
I am trying to create a SP that will give return a list of records in a table by filter via StartDate and EndDate , but there will be 'View All' Option so sometime those #Param might not contain any values.
Currently my SP is Like
CREATE PROCEDURE [dbo].[spGetBonusRun]
(
#StartDate as DATETIME,
#EndDate as DATETIME
)
AS
SELECT [Id]
,[StartDateTime]
,[EndDate]
,[Status]
FROM [Valt].[dbo].[BonusRun]
WHERE StartDateTime <= #StartDate AND EndDate >= #EndDate
How to active that ?
Try this:
WHERE (StartDateTime <= #StartDate OR #StartDate IS NULL) AND (EndDate >= #EndDate OR #EndDate IS NULL)
Hope it helps.
/Klaus
You can try something like this
CREATE PROCEDURE [dbo].[spGetBonusRun]
(
#StartDate as DATETIME,
#EndDate as DATETIME
)
AS
SELECT [Id]
,[StartDateTime]
,[EndDate]
,[Status]
FROM [Valt].[dbo].[BonusRun]
WHERE StartDateTime <= ISNULL(#StartDate, StartDateTime)
AND EndDate >= ISNULL(#EndDate, EndDate)
Note the use of ISNULL

Resources