I have the following sample query within a stored procedure where the #StartDate, #EndDate and #ClientID parameters are all optional.
What is the best way to handle that within the query to ensure I get a result depending on whether 1 or many parameters have values?
select * from table
WHERE
StartDate >= #StartDate and
StartDate <= #EndDate and
CE.ClientID = #ClientID
For Example, someone could just enter a Start Date or just enter an End Date or select a particular ClientID or do a combination of all 3.
If you're willing to sacrifice a tiny amount of time on each execution, OPTION(RECOMPILE) will provide the performance equal to dynamic SQL but without all the perils of it.
select * from table
WHERE
(StartDate >= #StartDate or #StartDate is null) and
(StartDate <= #EndDate or #EndDate is null) and
(CE.ClientID = #ClientID or #ClientID is null)
option(recompile)
You can do something like this -
SELECT * FROM table
WHERE
(#StartDate IS NULL OR StartDate >= #StartDate) AND
(#EndDate IS NULL OR StartDate <= #EndDate) AND
(#ClientID IS NULL OR CE.ClientID = #ClientID)
The best way is to use dynamic SQL. Something like this:
declare #sql nvarchar(max);
set #sql = 'select * from table';
declare #where nvarchar(max);
set #where = (case when #StartDate is not null then ' and StartDate >= #StartDate' else '' end) +
(case when #EndDate is not null then ' and EndDate >= #EndDate' else '' end) +
(case when #ClientID is not null then ' and ClientID = #ClientID' else '' end);
set #where = stuff(#where, 1, 5, '');
set #sql = #sql + (case when len(#where) > 0 then ' where ' + #where' else '');
exec sp_executesql #sql,
N'#StartDate date, #EndDate date, #ClientId int',
#StartDate = #StartDate, #EndDate = #EndDate, #ClientId = ClientId;
The reason this is better is because each possible combination of inputs results in a different query. SQL Server can optimize the queries using appropriate indexes, and such optimization can be important when using optional parameters.
Related
I wrote this query:
declare #startdate nvarchar(10) = '2016/01/01',
#enddate nvarchar(10) = '2019/10/04',
#cols nvarchar(max),
#strsql nvarchar(max);
select
#cols =
(select distinct QUOTENAME(maintenancename)+','
from (select distinct maintenancename
from Table_maintenancetype where coalesce(maintenancename, '') != '') AS t1
for xml path('')
)
set #cols=LEFT(#cols,LEN(#cols)-1)
set #strsql=N'select *
from
(select sum(timedurationok) as wt, maintenancetype,tcode
from Table_maintenancereport
where ((svok=1 and need_netok=0) or (svok=1 and netok=1 and need_netok=1))and tcode<>-1
and dateendrole >='+ #startdate+ 'and dateendrole<='+ #enddate+'
group by maintenancetype,tcode
) d
pivot
(sum(wt)
for maintenancetype in ('+#cols+')
) piv
'
exec sp_executesql #strsql
When execute this no record return but when replace #startdate and #enddate by '2016/01/01','2019/10/04'
The query works fine I don't know what the problem is... and when use between in query I get error for can not convert nvarchar to data int 2016
first, do a print #strsql to show your dynamic query and check.
You will see that your query is dateendrole >= 2016/01/01
the date should be in single quote dateendrole >= 2016/01/01'
your query should be
and dateendrole >= '''+ #startdate+ ''' and dateendrole<= '''+ #enddate+'''
a better way is to use parameter with sp_executesql
declare both the variable as date data type since it is going to hold date value
declare #startdate date = '20160101',
#enddate date = '20191004'
and in your dynamic query, just use the variable
and dateendrole >= #startdate and dateendrole <= #enddate
finally when calling sp_executesql, pass in the variable. And don't forget to print out the verify your query
print #strsql
exec sp_executesql #strsql, N'#startdate date, #enddate date', #startdate, #enddate
Given:
CREATE PROCEDURE [dbo].[sp_func]
#StartDate DATE
, #EndDate DATE
AS
select * from [TRANS] where
(#StartDate IS NULL OR [TransDate] >= #StartDate) AND (#EndDate IS NULL OR [TransDate] <= #EndDate)
Both this:
declare #StartDate DATE = '2015-01-01'
declare #EndDate DATE = '2015-12-31'
exec dbo.sp_func #StartDate, #EndDate
And this:
declare #StartDate DATE = NULL
declare #EndDate DATE = NULL
exec dbo.sp_func #StartDate, #EndDate
...return rows as expected.
However, both of these calls return 0 rows:
exec dbo.sp_func '2015-01-01', '2015-12-31'
exec dbo.sp_func NULL, NULL
Am I fundamentally misunderstanding something here? As far as I can recall, I’ve always called sp’s passing dates like 'YYYY-MM-DD' with absolutely no issues.
Possibly related...this:
select CAST('2015-01-01' AS DATE), CAST('2015-12-31' AS DATE)
returns:
2015-01-01 2015-12-31
Whereas this:
exec dbo.sp_func CAST('2015-01-01' AS DATE), CAST('2015-12-31' AS DATE)
returns:
Incorrect syntax near '2015-01-01'.
I feel like I'm taking crazy pills.
I discovered the problem, it was stupidity.
Not shown in the example, I had other parameters in my production code, one of which was:
#JobClass VARCHAR(10) = 'PRODUCTION'
What I had been doing wrong in my test code was passing NULL to this, and incorrectly thinking that a passed NULL to this would instead default to 'PRODUCTION', whereas this defaulting only happens when the parameter is not passed at all. Which I knew of course. One of those days.
A couple of things , I would re-write the whole proc as below:
Procedure Definition
CREATE PROCEDURE [dbo].[usp_Proc] --<-- do not use sp_
#StartDate DATE = NULL
, #EndDate DATE = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX);
SET #sql = N' select * from [TRANS] where 1 = 1 '
+ CASE WHEN #StartDate IS NOT NULL
THEN N' AND [TransDate] >= #StartDate' ELSE N' ' END
+ CASE WHEN #EndDate IS NOT NULL
THEN N' AND [TransDate] <= #EndDate ' ELSE N' ' END
Execute sp_executesql #sql
,N'#StartDate DATE,#EndDate DATE '
,#StartDate
,#EndDate
END
Execute Procedure
exec dbo.usp_Proc '20150101', '20151231' --<-- Pass date in ANSI format 'YYYYMMDD'
How about specifying the variable names and values like this:
EXECUTE dbo.sp_func #StartDate='2015-01-01',#EndDate='2015-12-31'
Works on my machine... :)
In SQL, I can use wildcard '%' on string and Select statement will show all the records like
select * from tablea where title like '%'
My question is what if both the start and end date are empty in the BETWEEN & AND function in Select statement.
Example
`select * from table where inputdate between '2014-01-01'` and '2014-01-31'
It works fine in this statement because there are start and end date.
What if there isn't any start and end date, and I want to show all the records?
select * from table where inputdate between '%' and '%' <--- not working.
Many thanks.
You can write your query in a way that inputdate column is evaluated against the value passed or if the no value is passed to #StartDate variable just pick earliest possible date,
I have select 1900-01-01 as the earliest date but depending on your column type you can pick a more appropriate substitute start and end dates.
DECLARE #StartDate DATE = '2014-01-01'
DECLARE #EndDate DATE = '2014-01-31'
select * from table
where inputdate >= COALESCE(#StartDate, '19000101')
and inputdate <= COALESCE(#EndDate , '99991231')
Dynamic SQL
Another better way of handling this kind of query with optional parameters will be using dynamic sql something like this...
DECLARE #StartDate DATE = '2014-01-01'
DECLARE #EndDate DATE = '2014-01-31'
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT * FROM Table 1 = 1 '
+ CASE WHEN #StartDate IS NOT NULL THEN
N' AND inputdate >= #StartDate ' ELSE N' ' END
+ CASE WHEN #EndDate IS NOT NULL THEN
N' AND inputdate <= #EndDate ' ELSE N' ' END
Execute sp_executesql #Sql
,N'#StartDate DATE, #EndDate DATE'
,#StartDate
,#EndDate
wildcard can only be used on string, But since you mentioned the column is Date, you can do something better. Date min/max has been defined in almost all DBMS, IDK about the max but the min is usually January 1, 1753. so you can try between ' January 1, 1753' and 'date max'
SELECT * FROM table t
WHERE t.Date >= IIF(#FromDate IS NULL, t.Date, #FromDate)
AND t.Date <= IIF(#ToDate IS NULL, t.Date, #ToDate)
works fine.
I have three parameters #startDate, #endDate, #name. The query I need to create a query that works like this.
If #startDate is not empty, WHERE #startDate <= time
If #endDate is not empty, add WHERE #endDate >= time
If #name is not empty, add WHERE name LIKE #name
That's what I want to do.... and this is the crappy code I've written.
ALTER blahblah...
#startDate varchar(10),
#endDate varchar(10),
#name varchar(7)
AS
BEGIN
SELECT *
FROM st.TB_RETENTION
WHERE
CASE WHEN #startDate != '' THEN #startDate <= time AND
CASE WHEN #endDate != '' THEN #endDate >= time AND
CASE WHEN #GameCode != '' THEN name LIKE #name
END
I think I'm quite close... but not sure how to add AND condition... Any advice for me? :(
You actually almost got it correct in your initial description:
where (#startDate is null or #startDate <= time)
and (#endDate is null or #endDate >= time)
and (#gameCode is null or name like #name)
I have a procedure to fetch data as per the criteria passed. I works fine with #month, #year, #quater.
ALTER PROCEDURE dbo.SPReportTimeSpan
(
#Week int = null,
#Month int = null,
#Year int = null,
#Quater int = null
)
AS
SET NOCOUNT ON
DECLARE #sql nvarchar(4000)
If (#Week) IS NOT NULL
BEGIN
DECLARE #startdate Date, #enddate Date
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
SELECT #sql='SELECT
CRMDR.Id as Id,.
CRMDR.Request_For_Id as Request_For_Id
From [CRM].[dbo].[CRM_Doctor_Request] AS CRMDR
WHERE CRMDR.Is_Deleted=0 '
If (#Month) IS NOT NULL
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) like (#Month) '
If (#Year) IS NOT NULL
SELECT #sql=#sql + ' AND YEAR(CRMDR.Date_Created) like (#Year) '
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN (#startdate) AND (#enddate) '
If (#Quater) IS NOT NULL
IF (#Quater = 1)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (4,5,6) '
IF (#Quater = 2)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (7,8,9) '
IF (#Quater = 3)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (10,11,12) '
IF (#Quater = 4)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (1,2,3) '
SELECT #sql=#sql + ' ORDER BY CRMDR.Id DESC '
EXEC sp_executesql #sql, N'#Month int, #Year int, #Quater int',
#Month, #Year, #Quater
RETURN
But for #week it gives error.
Running [dbo].[SPReportTimeSpan] ( #Week = 3, #Month = <NULL>, #Year = <NULL>, #Quater = <NULL> ).
Must declare the scalar variable "#startdate".
Column1 Column2
------------------------------ ------------------------------
2013-02-10 00:00:00.0000000 2013-02-17 00:00:00.0000000
No rows affected.
(1 row(s) returned)
#RETURN_VALUE = 0
Finished running [dbo].[SPReportTimeSpan].
I am not getting why its not working with week criteria. Please help if anyone have idea.
#startdate and #enddate are only accessible within the scope of your IF statement:
If (#Week) IS NOT NULL
BEGIN
DECLARE #startdate Date, #enddate Date
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
Move the declaration outside of the IF statement if you wish to reference these variables elsewhere:
DECLARE #startdate Date, #enddate Date
If (#Week) IS NOT NULL
BEGIN
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
FYI: The area where you make reference to these variables outside of the IF statement is here:
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN (#startdate) AND (#enddate) '
I got solution of my problem. To solve the problem, I need to explicitly cast datetime values to a character type so the query string can be concatenated as expected.
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN ''' + convert(VARCHAR,#startdate) + ''' AND ''' +convert(VARCHAR,#enddate) + ''''
or this (More Optimized)
SELECT #sql=#sql + ' AND (CRMDR.Date_Created BETWEEN #startdate AND #enddate)'
which needs to add something to sp_executesql
EXEC sp_executesql #sql, N'#Month int, #Year int, #Quater int, #Week int, #startdate datetime, #enddate datetime ',
#Month, #Year, #Quater, #Week, #startdate, #enddate
Your variables are out of scope.