I think this should be simple, but I keep running into problems.
I simply want to return all data from a table that lies between a date range. But I want the date range to be optional.
ALTER PROCEDURE [dbo].[sp_ExistingPlacements_Get]
#DateFrom DATE = NULL,
#DateTo DATE = NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT *
FROM tblExistingPlacements
WHERE
CreatedDT > COALESCE(NULLIF(#DateFrom, ''), #DateFrom)
AND
CreatedDT < COALESCE(NULLIF(#DateTo, GETDATE()), #DateTo)
END
So, if no dates are passed in, we return the entire table.
If only the start date (DateFrom) is passed, we return rows > the start date and all the up to the current date.
If only the End date (DateTo) is passed then return all the rows < the End Date
And of course if both dates are passed, return all the rows inbetween those dates.
Am I going the wrong route with COALESCE ?
Use ISNULL(#parameter) OR (--your condition--) instead of COALESCE:
BEGIN
SET NOCOUNT ON;
SELECT *
FROM tblExistingPlacements
WHERE
((#DateFrom IS NULL) OR CreatedDT > #DateFrom)
AND
((#DateTo IS NULL) OR CreatedDT < #DateTo)
END
If parameter was not provided ISNULL return TRUE, so second part of OR won't matter.
Do not do this. SQL will have to create one execution plan that works in any situation. As unituitive as it sounds, is better to have three separate queries:
ALTER PROCEDURE [dbo].[sp_ExistingPlacements_Get]
#DateFrom DATE = NULL,
#DateTo DATE = NULL
AS
BEGIN
SET NOCOUNT ON;
IF (#DateFrom IS NULL and #DateTo IS NULL)
SELECT field, field, field
FROM tblExistingPlacements
WHERE CreatedDT < GETUTCDATE();
ELSE IF (#DateFrom IS NULL)
SELECT field, field, field
FROM tblExistingPlacements
WHERE CreatedDT < #dateTo;
ELSE IF (#DateTo IS NULL)
SELECT field, field, field
FROM tblExistingPlacements
WHERE CreatedDT BETWEEN #DateFrom AND GETUTCDATE();
ELSE
SELECT field, field, field
FROM tblExistingPlacements
WHERE CreatedDT BETWEEN #DateFrom AND #DateTo;
END
The wisdom of returning the entire table when no parameters are specified is highly questionable, but that is not the point. Besides:
never use * in queries, always specify the projection list explicitly
always use UTC times in the database
For a thorough discussion of this topic see Dynamic Search Conditions in T-SQL.
Related
How do I write a MS SQL statement for the below condition?
I have a form that allows users to enter dates (fromDate & toDate) and ID (fromID & toID) in range. The fields can be blank for all OR enter only either from or to field OR enter both from and to fields. Selection is based on the entered values to select. Below are the conditions checking in where clause for value entered.
no value entered => skip all conditions
value entered in fromDate only => Date = frDate
value entered in toDate only => Date <= toDate
value entered in both fromDate & toDate => Date between fromDate and toDate
Condition is applied to ID field as well.
Any advice is highly appreciated.
Thanks in advance.
You can solve your problem using dynamic query. Your question is not fully clear. Here i'm giving you a solution which will help you to solve your problem. Try this:
1. Create Dynamic query in a Store Procedure
CREATE PROCEDURE sp_YourSPName
/* Input Parameters */
#FromDate DATETIME ,
#ToDate DATETIME
AS
SET NOCOUNT ON
/* Variable Declaration */
DECLARE #SQLQuery AS NVARCHAR(4000)
DECLARE #ParamDefinition AS NVARCHAR(2000)
/* Build the Transact-SQL String with the input parameters */
SET #SQLQuery = 'Select * From YourTableName where (1=1) '
/* check for the condition and build the WHERE clause accordingly */
IF (#FromDate IS NOT NULL)
AND (#ToDate IS NOT NULL)
SET #SQLQuery = #SQLQuery +
' And (YourDate BETWEEN #FromDate AND #ToDate)'
IF (#FromDate IS NULL)
AND (#ToDate IS NOT NULL)
SET #SQLQuery = #SQLQuery + ' And (YourDate <= #ToDate)'
IF (#FromDate IS NOT NULL)
AND (#ToDate IS NULL)
SET #SQLQuery = #SQLQuery + ' And (YourDate = #FromDate)'
/* Specify Parameter Format for all input parameters included
in the stmt */
SET #ParamDefinition = '#StartDate DateTime,
#EndDate DateTime'
/* Execute the Transact-SQL String with all parameter value's Using sp_executesql Command */
EXECUTE sp_Executesql #SQLQuery,
#ParamDefinition,
#FromDate,
#ToDate
IF ##ERROR <> 0
GOTO ErrorHandler
SET NOCOUNT OFF
RETURN(0)
ErrorHandler :
RETURN(##ERROR)
GO
2. Execute Store Procedure:
EXEC sp_YourSPName '01 Oct 2018', '01 Oct 2018'
For more info see this link
You can use IS NULL to check param has value
SELECT * FROM Table
WHERE (#FromDate IS NULL OR Date > #FromDate) AND (#ToDate IS NULL OR Date < #ToDate)
Same type of query will be used for Id
Below is the EXAMPLE and not the exact query you can try to put it in your way using CASE statements
SELECT values
FROM Table
WHERE CASE
WHEN fromDate = null & todate = null THEN
WHEN fromDate != null & toDate != null THEN Date between fromDate and toDate
WHEN fromDate != null THEN Date = frDate
WHEN toDate != null THEN Date = toDate
I use SQL SERVER 2012.
I have stored prcedure:
ALTER PROCEDURE [dbo].[SP_TEST_TLP]
#DateFrom date,
#DateTo date
AS
BEGIN
SET NOCOUNT ON;
select *
from Clients
WHERE DateReview between (#DateFrom) and (#DateTo)
END
As you can see I pass two parameters to stored procedure above and those parameters I use to filter the result in where clause.
My problem is that I need to filter result only by month and year.
For example, if I have passed those parameters:
#DateFrom date = '2016-05-15' ,
#DateTo date = '2016-10-09'
According to stored procedure I will get result between dates above.But I need to get rows from start of the month 05 and end of the month 10 i,e the result should be equivalent to those params:
#DateFrom date = '2016-05-01'
#DateTo date = '2016-10-31'
How can I get the desired result?
You can also use EOMONTH function
select *
from Clients
WHERE DateReview between DATEADD(DAY,1,EOMONTH(#DateFrom,-1) ) and EOMONTH(#DateTO)
Try this :
Here
DATEADD(dd,-(DAY(#DateFrom)-1),#DateFrom) this will give months start date i.e '2016-05-01'
And
DATEADD(dd,-(DAY(DATEADD(mm,1,#DateTo))),DATEADD(mm,1,#DateTo)) will give month end date i.e '2016-10-31'
ALTER PROCEDURE [dbo].[SP_TEST_TLP]
#DateFrom date,
#DateTo date
AS
BEGIN
SET NOCOUNT ON;
SET #DateFrom = DATEADD(dd,-(DAY(#DateFrom)-1),#DateFrom)
SET #DateTo = DATEADD(dd,-(DAY(DATEADD(mm,1,#DateTo))),DATEADD(mm,1,#DateTo))
Updated ---^
select *
from Clients
WHERE DateReview between (#DateFrom) and (#DateTo)
END
Here is one way:
select *
from Clients
where DateReview >= dateadd(day, 1 - day(#DateFrom), #DateFrom) AND
DateReview < dateadd(month, 1, dateadd(day, 1 - day(#DateTo), #DateTo))
This method allows the query to make use of an index on DateReview. You could also do this as:
where year(DateReview) * 100 + month(DateReview)
between year(#DateFrom) * 100 + month(#DateFrom) and
year(#DateTo) * 100 + month(#DateTo)
You can use EOMonth() function
ALTER PROCEDURE [dbo].[SP_TEST_TLP]
#DateFrom date,
#DateTo date
AS
BEGIN
SET NOCOUNT ON;
select *
from Clients
WHERE DateReview between Dateadd(d,1,EOMonth(#DateFrom,-1)) and EOMonnth(#DateTo)
END
I am trying to create dynamic code for date calculation in SQL stored procedures but am having problems executing string expressions and parameters as date expressions.
I want to hold generic string expressions in a table that will create the dates according to the value of the parameters.
for example this is a generic expression :
DATEADD(#TimeResolution, -#IterationN, #CurrentCalc)
as you can see these generic expressions are composed out of parameters to.
in the stored procedures I intend to declare the variables that are in the expression and assign values to them using a select statement from a different table.
the problem is that after deriving these string values and writing the expression it does not give me the date I want but fails.
so for example if I write the following code
declare #Today date
declare #LastYear date
set #Today = getdate()
set #LastYear = DATEADD(year, -1, #Today)
select #Lastyear
it works fine and I will get last year's date.
but when I try something like this :
declare #Today date
declare #LastYear date
declare #Timeresolution varchar(5)
select #Timeresolution = [Timeresolution] from dbo.mytable where rule_id=1//Timeresolution is a varchar column in my table holding the values 'year' or 'month'
declare Iteration int
select #Iteration = [Iteration] from dbo.mytable where rule_id=1 //Iteration is a int column in my table holding the values 1 or 2, or 3
set #Today = getdate()
set #LastYear = DATEADD(Timeresolution , -Iteration , #Today)
select #Lastyear
this gives me a conversion error.
Is there a way to create such dynamic date expressions?
It isn't possible to use a variable for the interval in DATEADD, but you can do something like this:
IF #Timeresolution = 'year'
BEGIN
SET #LastYear = DATEADD(year, -#Iteration , #Today)
END
IF #Timeresolution = 'month'
BEGIN
SET #LastYear = DATEADD(month, -#Iteration , #Today)
END
I have a stored procedure that I pass 3 variables bankNumber, branchNumber and DateFrom to.
Based on these variables I want to query the table (seen in picture below stored procedure) to return all records that meet the criteria I pass (through variables).
Instead I am getting this error:
Conversion failed when converting date and/or time from character string
It seems to be failing when I pass it the DateFrom variable.
Thank you for your help
ALTER PROCEDURE [dbo].[Search_Records]
#bankNumber varchar(3),
#branchNumber varchar(3),
#dateCreated datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE
#Bank_Number varchar(3) = #bankNumber,
#Branch_Number varchar(3) = #branchNumber,
#DateFrom datetime = #dateCreated,
#DateTo datetime = #dateCreated
SELECT DISTINCT
A.bankNumber,
A.branchNumber,
A.dateCreated
FROM
dbo.CENSORED A
WHERE
(#Branch_Number IS NULL OR bankNumber LIKE #BankNumber + '%')
AND (#Branch_Number IS NULL OR branchNumber LIKE #Branch_Number + '%')
AND (#DateFrom IS NULL OR dateCreated LIKE + #DateFrom + '%')
AND (#DateTo IS NULL OR dateCreated LIKE + #DateTo + '%')
END
You cannot use the LIKE operator with Datetime value. If you are matching only on month you would need to use MONTH() function. LIKE operator can only be used with string data types.
Dont see the point of all these Variables declared in your stored procedure, a simplified version should look something like ....
ALTER PROCEDURE [dbo].[Search_Records]
#bankNumber varchar(3),
#branchNumber varchar(3),
#dateCreated datetime
AS
BEGIN
SET NOCOUNT ON;
SELECT DISTINCT
A.bankNumber,
A.branchNumber,
A.dateCreated
FROM
dbo.CENSORED A
WHERE
(#Branch_Number IS NULL OR bankNumber LIKE #bankNumber + '%')
AND (#Branch_Number IS NULL OR branchNumber LIKE #branchNumber + '%')
AND (#dateCreated IS NULL OR (MONTH(dateCreated) = MONTH(#dateCreated)
AND
YEAR(dateCreated) = YEAR(#dateCreated)))
END
Note
this will produce a very inefficient execution plan, consider using dynamic sql for queries with optional parameters like this one.
I have 2 computed columns, [StartTime] and [EndTime]
The [StartTime] and [EndTime] are calculated with a formula using the [Week] and [Year] columns.
Now I need another computed column, [Status] that is calculated using the first two. But it gives me an error in formula when I try to use one of them inside the formula of [Status]
I really need this to work as I have no alternative. But is this even possible?
Here you go Mr -1 :
(case when [IsVOR]=(1) then 'VOR'
when [MarkedAsCompleteOn] IS NULL AND [Year]<datepart(year,getdate()) then 'Overdue'
when [MarkedAsCompleteOn] IS NULL AND [Year]>datepart(year,getdate()) then 'Not Due'
when [MarkedAsCompleteOn] IS NULL AND [Year]=datepart(year,getdate()) AND [Week]<datepart(iso_week,getdate()) then 'Overdue'
when [MarkedAsCompleteOn] IS NULL AND [Year]=datepart(year,getdate()) AND [Week]=datepart(iso_week,getdate()) then 'Due'
when [MarkedAsCompleteOn] IS NULL AND [Year]=datepart(year,getdate()) AND [Week]>datepart(iso_week,getdate()) then 'Not Due'
when [MarkedAsCompleteOn] IS NOT NULL AND [Year]<datepart(year,[MarkedAsCompleteOn]) then 'Late'
when [MarkedAsCompleteOn] IS NOT NULL AND [Year]>datepart(year,[MarkedAsCompleteOn]) then 'Early'
when [MarkedAsCompleteOn] IS NOT NULL AND [Year]=datepart(year,[MarkedAsCompleteOn]) AND [Week]<datepart(iso_week,[MarkedAsCompleteOn]) then 'Late'
when [MarkedAsCompleteOn] IS NOT NULL AND [Year]=datepart(year,[MarkedAsCompleteOn]) AND [Week]=datepart(iso_week,[MarkedAsCompleteOn]) then 'On Time'
when [MarkedAsCompleteOn] IS NOT NULL AND [MarkedAsCompleteOn]<[AllocatedTimeStart] then 'Early' end)
The last part of it causes the error :
[MarkedAsCompleteOn]<[AllocatedTimeStart] then 'Early'
And the error is generic :
- Error validating the formula for column 'Status'.
No. One computed column can not be based on a different computed column. There's a specific error for this (1759):
Computed column '%s' in table '%s' is not allowed to be used in another computed-column definition.
You could create a view, based on the table, and perform a second round of computations within the view definition. Whether you then perform all activity against the view (adding triggers if required), or only use it for lookup is a design decision you'd need to make.
Of course, one you add the idea of using a view, you can build up multiple (not just 2) layers of computation by using a Common Table expression.
You do lose the ability to make the computed column persisted, unless the view is eligible for becoming an indexed view - not that this matters in this case, since it seems to be based on date calculations, so probably not persistable anyway.
I found my own solution in the end, by using a function, this kept my production app going without any changes.
USE [DBNAME]
GO
/****** Object: UserDefinedFunction [dbo].[GetStatus] Script Date: 01/02/2013 11:08:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[GetStatus](#CompletedOn DATETIME, #Year INT, #Week INT)
RETURNS NVARCHAR(MAX)
AS BEGIN
DECLARE #CurrentDate DATETIME
DECLARE #StartTime DATETIME
DECLARE #EndTime DATETIME
DECLARE #Status NVARCHAR(MAX)
SET #CurrentDate = GETDATE()
SET #StartTime = (dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT([varchar](4),#Year,(0))+'-01-01'),(1)))))
SET #EndTime = (dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT([varchar](4),#Year,(0))+'-01-01'),(8)))))
SET #Status = '-'
IF(#CompletedOn IS NULL)
BEGIN
IF(#CurrentDate < #StartTime)
SET #Status = 'Not Due'
IF(#CurrentDate > #StartTime AND #CurrentDate < #EndTime)
SET #Status = 'Due'
IF(#CurrentDate > #EndTime)
SET #Status = 'Overdue'
END
ELSE
BEGIN
IF(#CompletedOn < #StartTime)
SET #Status = 'Early'
IF(#CompletedOn > #EndTime)
SET #Status = 'Late'
ELSE
SET #Status = 'On Time'
END
RETURN #Status
END
And in the status field I put the formula :
([dbo].[GetStatus]([MarkedAsCompleteOn],[Year],[Week]))
This is my first ever function in sql server, so it may not be optimum.