I'm running a report on a Sales table:
SaleId INT | SalesUserID INT | SiteID INT | BrandId INT| SaleDate DATETIME
I'm having a nightmare trying to do something like this with a set of Nullable parameters #SalesUserID, #SiteId, #BrandID and two DateTime params.
Additional Point: Only ONE of the filter parameters will ever be passed as a Non-Null value.
SELECT * from Sales
WHERE
SaleDate BETWEEN #StartDate AND #EndDate
AND
SalesUserID IN
(
Select SalesUserID FROM Sales
WHERE
SaleDate BETWEEN #StartDate AND #EndDate
AND
CASE
WHEN #SalesUserId IS NOT NULL THEN SalesUserId = #SalesUserID
WHEN #SiteId Is Not Null THEN SiteId = #SiteId
ELSE BrandId = #BrandID
END
)
My use of CASE here smells bad but I'm not sure how to correct it. Can you please assist?
Thanks.
5arx
I don't think you want a CASE statement at all, but a compound conditional... Give this a shot and let me know:
select *
from Sales
where SaleDate between #StartDate and #EndDate
and
(
(#SalesUserId is not null and SalesUserId = #SalesUserID)
or (#SiteId is not null and SiteId = #SiteId)
or (BrandId = #BrandID)
)
If I understood you correctly, you want the three conditions either be NULL or checked for:
WHERE
/* ... */
AND SalesUserId = ISNULL(#SalesUserId, SalesUserId)
AND SiteId = ISNULL(#SiteId, SiteId)
AND BrandId = ISNULL(#BrandID, BrandID)
Be aware that this forces a table scan which might not be in your best interest.
This should work and use any index if you want to use CASE:
SELECT *
from Sales
WHERE SaleDate BETWEEN #StartDate AND #EndDate
AND SalesUserID = CASE WHEN #SalesUserID IS NULL THEN
SalesUserID ELSE #SalesUserID END
COALESCE() returns the 1st non NULL argument so you could;
...
WHERE SaleDate BETWEEN #StartDate AND #EndDate
AND SalesUserId = COALESCE(#SalesUserId, SalesUserId)
AND SiteId = COALESCE(#SiteId, SiteId)
AND BrandID = COALESCE(#BrandID, BrandId)
I would use a dynamic generated code in such a circumstance:
declare #SalesUserId int,#SiteId int,#StartDate datetime, #EndDate datetime,#BrandID int
declare #sql nvarchar(max)
set #sql = N'
SELECT * from Sales
WHERE
SaleDate BETWEEN #StartDate AND #EndDate
AND
SalesUserID IN
(
Select SalesUserID FROM Sales
WHERE
SaleDate BETWEEN #StartDate AND #EndDate AND
' +
CASE
WHEN #SalesUserId IS NOT NULL THEN 'SalesUserId = #SalesUserID'
WHEN #SiteId Is Not Null THEN 'SiteId = #SiteId'
ELSE 'BrandId = #BrandID'
END
+')'
print #sql
exec sp_executesql #sql
, N'#SalesUserId int,
#SiteId int,
#StartDate datetime,
#EndDate datetime,
#BrandID int'
,#SalesUserId
,#SiteId
,#StartDate
,#EndDate
,#BrandID
This is generally a job for dynamic SQl if you want performance.
http://www.sommarskog.se/dynamic_sql.html
Try this:
SELECT *
from Sales
WHERE SaleDate BETWEEN #StartDate AND #EndDate
AND SalesUserID = CASE WHEN #SalesUserID IS NULL THEN
SalesUserID ELSE #SalesUserID END
Related
I am trying to assign values to stored procedure when the passed arguments are null.
If startDate or endDate is null, I want to assign values to them
Like
#StartDate = '1990-01-01'
#EndDate = DATEADD(YEARS, 30, GETDATE()) -- 30 years from now.
How can I do this?
CREATE OR ALTER PROCEDURE dbo.GetByProductID
(
#ProductID INT,
#StartDate DATETIME = NULL
#EndDate DATETIME = NULL
)
AS
SELECT ProductId, Name, InsertedDate, UpdatedDate
FROM Products
WHERE ProductId = #ProductID
AND InsertedDate > #startDate
AND UpdatedDate < #EndDate
CREATE OR ALTER PROCEDURE dbo.GetByProductID
(
#ProductID INT,
#StartDate DATETIME = NULL
#EndDate DATETIME = NULL
)
AS
set #StartDate = coalesce(#StartDate,'1990-01-01')
set #EndDate = coalesce(#EndDate,DATEADD(YEARS, 30, GETDATE())) -- 30 years from now.
SELECT ProductId, Name, InsertedDate, UpdatedDate
FROM Products
WHERE ProductId = #ProductID
AND InsertedDate > #startDate
AND UpdatedDate < #EndDate
I have this scalar function:
ALTER FUNCTION [dbo].[Custom_SumKayakoMonthProfit]
(#ClientId int, #KKId int,
#StartDate SmallDateTime, #EndDate SmallDatetime,
#Month int)
RETURNS INT
AS
BEGIN
DECLARE #Total INT
SELECT
#Total = ((CAST(SUM(WorkedTimeInMinutes) AS FLOAT) / 60) * KayakoValue)
FROM
[VDBSERVER2012\SQLEXPRESS2014].PlaterITDB.dbo.KayakoTimeEntries
INNER JOIN
ClientContract ON ClientId = #ClientId
AND Workdate BETWEEN ClientContract.StartDt AND ClientContract.EndDt
WHERE
OrganizationID = #KKId
AND WorkDate BETWEEN #StartDate AND #EndDate
AND MONTH(WorkDate) = #Month
RETURN #Total
END
Whenever I try to execute it to use it on a stored procedure, I get this error.
Column 'ClientContract.KayakoValue' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I can't figure out how to solve it.
What don't you understand about the message? You have an aggregation query with no GROUP BY. Hence, all columns must be the arguments to aggregation functions.
I suspect that you intend:
SELECT #Total = CAST(SUM(WorkedTimeInMinutes * KayakoValue) AS FLOAT)/60
FROM [VDBSERVER2012\SQLEXPRESS2014].PlaterITDB.dbo.KayakoTimeEntries kte INNER JOIN
ClientContract cc
ON ClientId = #ClientId AND
Workdate BETWEEN cc.StartDt AND cc.EndDt
WHERE OrganizationID = #KKId AND
WorkDate BETWEEN #StartDate AND #EndDate AND MONTH(WorkDate) = #Month
Note that you should learn to use table aliases and to qualify all column names. This is a good practice for writing readable and maintainable code.
You should use 'KayakoValue' in aggregate function like sum,count, . . . or in group by at the end .
SELECT #Total = ((CAST(SUM(WorkedTimeInMinutes) AS FLOAT)/60) * KayakoValue)
FROM [VDBSERVER2012\SQLEXPRESS2014].PlaterITDB.dbo.KayakoTimeEntries
INNER JOIN ClientContract ON ClientId = #ClientId AND Workdate BETWEEN ClientContract.StartDt AND ClientContract.EndDt
WHERE OrganizationID = #KKId AND WorkDate BETWEEN #StartDate AND #EndDate AND MONTH(WorkDate) = #Month
group by KayakoValue
OR
SELECT #Total = ((CAST(SUM(WorkedTimeInMinutes * KayakoValue) AS FLOAT)/60))
FROM [VDBSERVER2012\SQLEXPRESS2014].PlaterITDB.dbo.KayakoTimeEntries
INNER JOIN ClientContract ON ClientId = #ClientId AND Workdate BETWEEN ClientContract.StartDt AND ClientContract.EndDt
WHERE OrganizationID = #KKId AND WorkDate BETWEEN #StartDate AND #EndDate AND MONTH(WorkDate) = #Month
I have this stored procedure:
ALTER PROCEDURE [dbo].[GetCalendarEvents]
(#StartDate datetime,
#EndDate datetime,
#Location varchar(250) = null)
AS
BEGIN
SELECT *
FROM Events
WHERE EventDate >= #StartDate
AND EventDate <= #EndDate
AND (Location IS NULL OR Location = #Location)
END
Now, I have the location parameter, what I want to do is if the parameter is not null then include the parameter in where clause. If the parameter is null I want to completely ignore that where parameter and only get the result by start and end date.
Because when I'm doing this for example:
EXEC GetCalendarEvents '02/02/2014', '10/10/2015', null
I'm not getting any results because there are other locations which are not null and since the location parameter is null, I want to get the results from all the locations.
Any idea how can I fix this?
ALTER PROCEDURE [dbo].[GetCalendarEvents]
( #StartDate DATETIME,
#EndDate DATETIME,
#Location VARCHAR(250) = NULL
)
AS
BEGIN
SELECT *
FROM events
WHERE EventDate >= #StartDate
AND EventDate <= #EndDate
AND Location = ISNULL(#Location, Location )
END
If a NULL column is a possibility, then this would work.
ALTER PROCEDURE [dbo].[GetCalendarEvents]
( #StartDate DATETIME,
#EndDate DATETIME,
#Location VARCHAR(250) = NULL
)
AS
BEGIN
IF ( #loc IS NULL )
BEGIN
SELECT *
FROM events
WHERE EventDate >= #StartDate
AND EventDate <= #EndDate
END
ELSE
BEGIN
SELECT *
FROM events
WHERE EventDate >= #StartDate
AND EventDate <= #EndDate
AND Location = #Location
END
END
As having an 'OR' clause should be reasonably avoided due to possible performance issues.
The part in the WHERE clause should then read
AND (#Location IS NULL OR Location=#Location)
Try this
SELECT *
FROM Events
WHERE EventDate >= #StartDate
AND EventDate <= #EndDate
AND Location = Case When LEN(#Location) > 0 Then #Location Else Location End
It can be easily done with a dynamic sql query.
ALTER PROCEDURE [dbo].[GetCalendarEvents]
(#StartDate datetime,
#EndDate datetime,
#Location varchar(250) = null)
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX);
DECLARE #PARAMETER_DEFIINITION NVARCHAR(MAX);
DECLARE #WHERE_PART NVARCHAR(MAX);
SET #PARAMETER_DEFIINITION =' #StartDate DATETIME, #EndDate DATETIME, #Location VARCHAR(250) '
SET #SQL ='SELECT *
FROM Events
WHERE EventDate >= #StartDate
AND EventDate <= #EndDate '
IF #Location IS NOT NULL
BEGIN
SET #WHERE_PART = ' AND Location = #Location '
END
SET #SQL = #SQL + #WHERE_PART
EXEC SP_EXECUTESQL #SQL, #PARAMETER_DEFIINITION, #StartDate, #EndDate, #Location
END
Query will be dynamically created according to the parameters. In here if #location is null then it will not add to the where part.
If you want more on writing dynamic queries please refer this article. http://codingpulse.blogspot.com/2015/02/dynamic-sql-in-stored-procedure-part-1.html
There are thee input parameter in my stored procedure. for example,
DECLARE #fromDate DateTime = NULL
DECLARE #toDate DateTime = NULL
DECLARE #Id int = NULL
I want to write a condition in where clause like...if fromDate is provided then searching must be done on #fromDate. if #fromDate is not provided then check for #Id variable if this is not null then search basis on #Id...
Something like...
where
CASE
when #fromDate is not null
THEN (#FromDate is null or ([Created] between #FromDate and #ToDate))
ELSE (#requestId is null or Id=#requestId)
there is one problem with below solution...if #fromDate and #Id both are provided then this will do intesect of them and nothing is return.....condition should be like...if #fromDate is given the priority gives to #fromDate even if #Id is provided and result must not be dependend to #Id parameter....
Because you depend on both parameters you than can use them both in condition:
where
(#FromDate is null or ([Created] between #FromDate and #ToDate))
or
((#requestId is null or Id=#requestId)
and #FromDate is null) ----mix #requestId & #FromDate
Do you mean this? Please check:
WHERE [Created] BETWEEN
(CASE WHEN #FromDate IS NOT NULL THEN #FromDate ELSE [Created] END) AND
(CASE WHEN #ToDate IS NOT NULL THEN #ToDate ELSE [Created] END) AND
Id=(CASE WHEN #requestId IS NOT NULL THEN #requestId ELSE Id END)
I have the following stored procedure.
ALTER PROCEDURE [dbo].[spList_Report]
#id INT,
#startDate DATETIME = NULL,
#endDate DATETIME = NULL,
#includeStatus1 BIT,
#includeStatus2 BIT,
#includeStatus3 BIT,
#includeStatus4 BIT
AS
SET NOCOUNT ON
SELECT *
FROM
tblProducts as products
WHERE
product.intID = #id
AND product.dateMain >= #startDate
AND product.dateMain <= #endDate
If #startDate AND #endDate are both null then I want it to return the rows ignoring the date check in the where clause.
How?
This should do
AND product.dateMain >= ISNULL( #startDate, 0)
AND product.dateMain <= ISNULL( #endDate, product.dateMain + 1)
ISNULL yields the second value, if the first value is null.
Thus:
if #startDate is null, then dateMain must be bigger than 0 (1900-01-01)
if #endDate is null, then dateMain must be smaller than dateMain + 1 day
you can try something like this
ALTER PROCEDURE [dbo].[spList_Report]
#id INT,
#startDate DATETIME = NULL,
#endDate DATETIME = NULL,
#includeStatus1 BIT,
#includeStatus2 BIT,
#includeStatus3 BIT,
#includeStatus4 BIT
AS
SET NOCOUNT ON
SELECT *
FROM
tblProducts as products
WHERE
product.intID = #id
AND product.dateMain >= ISNULL( #startDate, product.dateMain )
AND product.dateMain <= ISNULL( #endDate, product.dateMain )
You can utilize an "or" in your Sql, but since this is a stored procedure:
If #startdate is null Or #enddate is null
begin
select without using a date range
end
Else
begin
select using date range
end
I would use Kris Krause's solution, but change the "IF" statement to use "AND". I think if you use the first two solutions the query engine may perform a table/index scan on the date fields. You want to keep your queries as concise as possible for best performance, so don’t run queries on unnecessary columns.
IF #startdate IS NULL AND #enddate IS NULL
BEGIN
SELECT * FROM tblProducts as products WHERE
product.intID = #id
END
ELSE
BEGIN
SELECT * FROM tblProducts as products WHERE
product.intID = #id
AND product.dateMain >= #startDate
AND product.dateMain <= #endDate
END