case inside where clause which contains where conditions in db2 - sql

I have use case where I need to write a query which contains case statement in where clause with conditions like below :
I have column in 'SAMPLE_TABLE' called 'BIRTHDATE'
1) if only from date (which is a param i.e., :from) is given, then I need to get the records from SAMPLE_TABLE whose BIRTHDATE >= :from
2) if only to date (which is a param i.e., :to) is given,then get records whose BIRTHDATE <= :to
3) if both from and to dates are given, then get the records between those dates.
Below is the query I tried. But couldn't get the solution.
SELECT BIRTHDATE FROM SAMPLE_TABLE
WHERE
(CASE
WHEN (:from AND NOT :to)
THEN BIRTHDATE >= :receivefrom
WHEN (:to AND NOT :from)
THEN BIRTHDATE <= :to
WHEN (:to AND :from)
THEN BIRTHDATE BETWEEN :from AND :to
END)
Please provide a working query. Thanks in advance.

This code would do, when a date is not included leave it as blank. This was tested in SQL Server
declare #from date='1998-12-07'
declare #to date ='2000-12-07'
SELECT [Birthdate] --when only from is available
FROM SAMPLE_TABLE
where (case when (#to='' and #from !='') then 1 else 0 end)=1
and BIRTHDATE >= #from
UNION
SELECT [Birthdate] --when only to is available
FROM SAMPLE_TABLE
where (case when (#to!='' and #from ='') then 1 else 0 end)=1
and BIRTHDATE <= #to
UNION
SELECT [Birthdate] --when both from and to are avilable
FROM SAMPLE_TABLE
where (case when (#to!='' and #from !='') then 1 else 0 end)=1
and BIRTHDATE between #from and #to
Or else you can try this
declare #from date='1998-12-07'
declare #to date ='2000-12-07'
IF (#from!='' and #to!='')
SELECT [Birthdate]
FROM SAMPLE_TABLE
Where [Birthdate] between #from and #to
ELSE IF (#from='' and #to!='')
(SELECT [Birthdate]
FROM SAMPLE_TABLE
Where [Birthdate]<= #to)
ELSE IF (#from!='' and #to='')
(SELECT [Birthdate]
FROM SAMPLE_TABLE
Where [Birthdate]>= #from)

I think you simply want:
SELECT BIRTHDATE
FROM SAMPLE_TABLE
WHERE (BIRTHDATE >= :from OR :from IS NULL) AND
(BIRTHDATE <= :to OR :to IS NULL)

I assume that if some parameter is not supposed to be passed, then it has NULL value.
where birthdate between coalesce(:from, date('0001-01-01')) and coalesce(:to, date('9999-12-31'))

Related

Add a date range to SQL query

I have simple SQL Server view that I need to make amends to:
CREATE VIEW [dbo].[ApplicantStat]
AS SELECT ISNULL(CONVERT(VARCHAR(50), NEWID()), '') AS Pkid,
AVG(ApplicationTime) AS 'AvgApplicationTime',
AVG(ResponseTime) AS 'AvgResponseTime',
CAST(ROUND(100.0 * count(case when [IsAccepted] = 1 then 1 end) / count(case when [IsValid] = 1 then 1 end), 0) AS int) AS 'AcceptRate'
FROM [Application]
It works as planned, but I need to add a date range to it. It's not quite as simple as Where > this date and < that date, instead I need to create a range.
Suppose I have a 'CreatedOn' date in my Application table. I want to be able to include all rows from the last full day (yesterday) and work back 30 days (inclusive).
I'm using SQL Server 2014.
Use :
where CreatedOn between cast(getdate()-30 as date) and cast(getdate()-1 as date)
Please notice CAST is used, it is because to get the full day ignoring the time part.
Something like this:
where MyColumn between dateadd(dd, -1, convert(date, getdate())) and dateadd(dd, -30, convert(date, getdate()))
It's a bit beyond the scope of this question, but maybe useful to some. I like this way of creating a table with date range, to use in queries:
USE MyDataBase
DECLARE #StartDate DATE
DECLARE #EndDate DATE
SET #StartDate = '2014-01-01' -- << user input >> --
SET #EndDate = '2036-12-31' -- << user input >> --
IF OBJECT_ID ('TEMPDB..#Date') IS NOT NULL DROP TABLE #Date
IF OBJECT_ID ('TEMPDB..#Date') IS NULL CREATE TABLE #Date (DateOne DATE)
INSERT INTO #Date VALUES (#StartDate)
WHILE #StartDate < #EndDate
BEGIN
INSERT INTO #Date
SELECT DATEADD (DD, 1, #StartDate) AS Date
SET #StartDate = DATEADD (DD, 1, #StartDate)
END
SELECT * FROM #Date
You should be able to just stick a WHERE with a BETWEEN clause on the end.
CREATE VIEW [dbo].[ApplicantStat]
AS SELECT ISNULL(CONVERT(VARCHAR(50), NEWID()), '') AS Pkid,
AVG(ApplicationTime) AS 'AvgApplicationTime',
AVG(ResponseTime) AS 'AvgResponseTime',
CAST(ROUND(100.0 * count(case when [IsAccepted] = 1 then 1 end) / count(case when [IsValid] = 1 then 1 end), 0) AS int) AS 'AcceptRate'
FROM [Application]
WHERE CreatedOn BETWEEN GETDATE()-1 AND GETDATE()-30

RUN Query based on a given condition

Hi Guys i want to run this query based on condition following condition. if parameter date is less than 01/01/16 it should run the first CTE else the 2nd CTE thanks
DECLARE #Date AS DATET
SET #Date = '2015-10-31'
;WITH OLDVTE AS (SELECT
SalesID,
City,
Amount,
SalesDate
FROM INFO.SALESOLD
),
NEWVTE AS (
SalesID,
City,
Amount,
SalesDate
FROM INFO.SALESNEW
)
SELECT
CASE WHEN GETDATE() <= #Date THEN
(SELECT * FROM OLDVTE)
ELSE
(SELECT * FROM NEWVTE)
END AS DD
You might try something like this:
DECLARE #Date AS DATE = {d'2015-10-31'}
DECLARE #CurrentDateTime DATETIME = GETDATE();
SELECT
SalesID,
City,
Amount,
SalesDate
FROM INFO.SALESOLD
WHERE #CurrentDateTime <= #Date
UNION ALL
SELECT
SalesID,
City,
Amount,
SalesDate
FROM INFO.SALESNEW
WHERE #CurrentDateTime > #Date
The UNION ALL will return both results in one. But there will be only one of the selects returning rows actually...
Hint
I actually doubt, that your filter against GETDATE() would work as you expect it...
You could try with IF ELSE like below..
DECLARE #Date AS DATETIME
SET #Date = '2015-10-31'
If GETDATE() <= #Date--will this clause work..
select * FROM INFO.SALESOLD
Else
select * FROM INFO.SALEnew
You could also join the two tables if they contain any common field and pass date

SQL Server - find people between date range excluding the year

I have a table EMPLOYEE containing Employee info as mentioned below:
ID NAME DOB
1 ABC 1974-01-01
2 BDS 1984-12-31
3 QWE 1959-05-27
and so on
I want to list all the employees whose DOB is in the given range.
select * from EMPLOYEE where DOB BETWEEN '1970-01-01' AND '1980-02-27'
I have a filter condition to 'include year in date comparison', which when selected 'NO', the employee DOB day and DOB month
only should be considered for comparison. and not the year.
For example: If I enter the date range as '1970-01-01' and '1980-02-27' and the filter is selected as 'NO' then it should search for only those employees
whose DOB is greater than equal to JAN-01 and less than equal to FEB-27.
When selected 'Yes', it is simply date range as mentioned in above query.
Here is what I have tried so far:
select * from EMPLOYEE where DOB BETWEEN '1970-01-01' AND '1980-02-27'
AND MONTH(DOB) >= CASE WHEN 'NO'='NO' THEN MONTH('1970-01-01')
ELSE MONTH(DOB) END
AND MONTH(DOB) <= CASE WHEN 'NO'='NO' THEN MONTH('1980-02-27')
ELSE MONTH(DOB) END
AND DAY(DOB) >= CASE WHEN 'NO'='NO' THEN DAY('1970-01-01')
ELSE DAY(DOB) END
AND DAY(DOB) <= CASE WHEN 'NO'='NO' THEN DAY('1980-02-27')
ELSE DAY(DOB) END
It works when I pass the date range where the FROM date has smaller number month than the TO date month.
For example:
It doesnt work when I pass the date range as '1970-12-01' to '1980-01-31'.
It should list the employees whose DOB is in DEC and JAN month.
Need help please.
Sample Data;
DECLARE #Date_From date; SET #Date_From = '1970-12-01'
DECLARE #Date_To date; SET #Date_To = '1974-01-31'
DECLARE #IncludeYear bit; SET #IncludeYear = 0
CREATE TABLE #Employee (ID int, Name varchar(10), DOB date)
INSERT INTO #Employee (ID, Name, DOB)
VALUES
(1,'ABC','1974-01-01')
,(2,'BDS','1984-12-31')
,(3,'QWE','1959-05-27')
This is the query I've made. Tried to cover for every eventuality.
SELECT
e.ID
,e.Name
,e.DOB
FROM #Employee e
WHERE
(
#IncludeYear = 1
AND
DOB BETWEEN #Date_From AND #Date_To
)
OR
(
#IncludeYear = 0
AND
(
(
DATEPART(DAYOFYEAR, #Date_From) = DATEPART(DAYOFYEAR, #Date_To)
AND
DATEPART(DAYOFYEAR, DOB) = DATEPART(DAYOFYEAR, #Date_To)
)
OR
(
DATEPART(DAYOFYEAR, #Date_From) < DATEPART(DAYOFYEAR, #Date_To)
AND
DATEPART(DAYOFYEAR, DOB) BETWEEN DATEPART(DAYOFYEAR, #Date_From) AND DATEPART(DAYOFYEAR, #Date_To)
)
OR
(
DATEPART(DAYOFYEAR, #Date_From) > DATEPART(DAYOFYEAR, #Date_To)
AND
(
DATEPART(DAYOFYEAR, DOB) > DATEPART(DAYOFYEAR, #Date_From)
OR
DATEPART(DAYOFYEAR, DOB) < DATEPART(DAYOFYEAR, #Date_To)
)
)
)
)
First part of the where clause checks if the #date_from and #date_to
are the same date, then only returns these.
Second part checks if the day of year for #date_from comes before
#date_to. If it does then return everything between these days of the
year.
Final part checks if the day of year for #date_to comes before
#date_from then it gets everything with the day of year after
#date_from or before #date_to
The results for this one come out as this;
ID Name DOB
1 ABC 1974-01-01
2 BDS 1984-12-31
DECLARE #includeYear bit = 0, -- if 0 - we don't include year, 1 - include
#dateFrom date ='1970-12-01',
#dateTo date ='1980-05-30'
IF #includeYear = 1
BEGIN
SELECT e.*
FROM EMPLOYEE e
INNER JOIN (SELECT #dateFrom as dF, #dateTo as dT) d
ON e.DOB BETWEEN dF AND dT
END
ELSE
BEGIN
SELECT e.*
FROM EMPLOYEE e
INNER JOIN (SELECT #dateFrom as dF, #dateTo as dT) d
ON e.DOB BETWEEN
(CASE WHEN MONTH(dF) > MONTH(dT)
THEN DATEADD(year,YEAR(e.DOB)-YEAR(d.dF)-1,dF)
ELSE DATEADD(year,YEAR(e.DOB)-YEAR(d.dF),dF) END)
AND DATEADD(year,YEAR(e.DOB)-YEAR(d.dT),dT)
OR e.DOB BETWEEN DATEADD(year,YEAR(e.DOB)-YEAR(d.dF),dF) AND
(CASE WHEN MONTH(dF) > MONTH(dT)
THEN DATEADD(year,YEAR(e.DOB)-YEAR(d.dT)+1,dT)
ELSE DATEADD(year,YEAR(e.DOB)-YEAR(d.dT),dT) END)
END
For
dateFrom dateTo
1970-12-01 1980-01-30
Output:
ID NAME DOB
1 ABC 1974-01-01
2 BDS 1984-12-31
For
dateFrom dateTo
1970-05-01 1980-06-30
Output:
ID NAME DOB
3 QWE 1959-05-27
For
dateFrom dateTo
1970-05-01 1980-05-30
Output:
ID NAME DOB
3 QWE 1959-05-27
etc
Try the function DATEPART(dayofyear, date)
In case the day-of-year of the first date is smaller than the day-of-year of the second date, then the day-of-year of the DOB should be between the specified days-of-year.
Otherwise, the day-of-year of the DOB should be either smaller than the day-of-year of the second date or greater than the day-of-year of the first date.
I hope I expressed myself well.
Rather than working case-by-case and disassembling and reassembling parts of dates, I've tried to make life easier:
declare #t table (ID int not null, Name varchar(17) not null, DOB date not null)
insert into #t(ID,NAME,DOB) values
(1,'ABC','19740101'),
(2,'BDS','19841231'),
(3,'QWE','19590527')
declare #Start date
declare #End date
declare #IncludeYear bit
select #Start='19701201',#End='19800131',#IncludeYear=0
;With Normalized as (
select
ID,
Name,
CASE WHEN #IncludeYear=1 THEN DOB
ELSE DATEADD(year,DATEDIFF(year,DOB,'20000101'),DOB)
END as DOB,
CASE WHEN #IncludeYear=1 THEN #Start
ELSE DATEADD(year,DATEDIFF(year,#Start,'20000101'),#Start)
END as StartRange,
CASE WHEN #IncludeYear=1 THEN #End
ELSE DATEADD(year,DATEDIFF(year,#End,'20000101'),#End)
END as EndRange
from
#t
)
select * from Normalized
where
DOB between StartRange and EndRange or
(
#IncludeYear=0 and StartRange>EndRange and
(
DOB < EndRange or DOB > StartRange
)
)
We create the Normalized CTE that, does nothing if #IncludeYear is 1 or, if it is zero, it resets all dates so that they occur in 2000 (arbitrarily selected).
We then do the straightforward query based on the CTE. The one circumstance where it won't correctly match is when you have your range defined over a year-end transition and we don't care about years - which we can check specifically for and cater for within the end of the WHERE clause.
Results:
ID Name DOB StartRange EndRange
----------- ----------------- ---------- ---------- ----------
1 ABC 2000-01-01 2000-12-01 2000-01-31
2 BDS 2000-12-31 2000-12-01 2000-01-31
Results with #Start='19700101',#End='19800227',#IncludeYear=1:
ID Name DOB StartRange EndRange
----------- ----------------- ---------- ---------- ----------
1 ABC 1974-01-01 1970-01-01 1980-02-27
Here is another solution
DECLARE #employee table(EmployeeID varchar(10), DOB date);
INSERT INTO #employee(EmployeeID, DOB)
VALUES('0001', '01-Dec-1990'),
('0002', '06-Jan-1993'),
('0003', '04-Mar-1987'),
('0004', '12-Feb-1996');
DECLARE #dateStart date = '01-Jan-1990';
DECLARE #dateEnd date = '27-Feb-1997';
DECLARE #includeYear bit = 0;
If #includeYear = 0
Begin
SET #dateStart = CAST(('2000-' + CAST(MONTH(#dateStart) AS varchar(10)) + '-' + CAST(DAY(#dateStart) as varchar(10))) AS date);
SET #dateEnd = CAST(('2000-' + CAST(MONTH(#dateEnd) AS varchar(10)) + '-' + CAST(DAY(#dateEnd) as varchar(10))) AS date);
End
If #includeYear = 1
Begin
SELECT *
FROM #employee
WHERE DOB BETWEEN #dateStart AND #dateEnd
End
Else
Begin
SELECT *
FROM #employee
WHERE CAST(('2000-' + CAST(MONTH(DOB) AS varchar(10)) + '-' + CAST(DAY(DOB) as varchar(10))) AS date) BETWEEN #dateStart AND #dateEnd
End
As you can see we are just making the year part of the query a constant if you don't want to include year. This query seems to be a bit slower but if you add another computed column in your table, where you save the date with a constant year then you just need to put where criteria on that particular column.
Try this.
declare #flag varchar(3) ='NO';
declare #sd date ='1980-02-27';
declare #ed date ='1970-01-01';
select tt.*
from (select sd = month(#sd)*100 + day(#sd),
ed = month(#ed)*100 + day(#ed)
) prm
cross join
-- test data, place real table here
(
values
(1,'ABC', cast('1974-01-05' as date)),
(2,'BDS','1984-12-31'),
(3,'QWE','1959-05-27')
) tt(ID,NAME, DOB)
cross apply (select md = month(DOB)*100 + day(DOB)) tx
where #flag ='YES' and DOB between #sd and #ed
or #flag ='NO' and (prm.sd<=prm.ed and tx.md between prm.sd and prm.ed
or prm.sd>prm.ed and (tx.md <= prm.ed or tx.md >= prm.sd));
Always use a SARG in your predicate. Any answer that fails to do this only results in lost performance and your DBA getting upset.
What you want is two different queries to run depending on the answer to the Procedure. Since this is a proc that likely runs a lot, store the answers into a variable in your PROC and run any adjusting code from there. Not only will this make your code more robust by flushing out errors beforehand, but SQL Server has a better chance of guessing the variables to use with your indexes.
The following PROC will work. Feel free to use part or all of it:
CREATE TABLE #table_E (ID INT, Name VARCHAR(3), DOB DATE)
INSERT INTO #table_E (ID , Name, DOB)
VALUES (1, 'ABC', '1997-01-02' )
, (2, 'BDS', '1984-12-31' )
, (3, 'QWE', '1993-03-22' )
GO
CREATE PROC USP_EmpCompare (#Date_1 DATE, #Date_2 DATE, #Compare_Year VARCHAR(3))
AS
BEGIN
DECLARE #MONTH_1 INT
, #Month_2 INT
, #Day_1 INT
, #Day_2 INT
, #Date1 DATE
, #Date2 DATE
SET #Date1 = CASE WHEN #Date_1 > #Date_2 THEN #Date_2 ELSE #Date_1 END
SET #Date2 = CASE WHEN #Date_1 > #Date_2 THEN #Date_1 ELSE #Date_2 END
SET #Month_1 = CASE WHEN DATEPART(MM, #Date2) > DATEPART(MM, #Date1) THEN DATEPART(MM, #Date1) ELSE DATEPART(MM, #Date2) END
SET #Month_2 = CASE WHEN DATEPART(MM, #Date1) > DATEPART(MM, #Date2) THEN DATEPART(MM, #Date1) ELSE DATEPART(MM, #Date2) END
SET #Day_1 = CASE WHEN DATEPART(DD, #Date2) > DATEPART(DD, #Date1) THEN DATEPART(DD, #Date1) ELSE DATEPART(DD, #Date2) END
SET #Day_2 = CASE WHEN DATEPART(DD, #Date1) > DATEPART(DD, #Date2) THEN DATEPART(DD, #Date1) ELSE DATEPART(DD, #Date2) END
-- SELECT #Date1, #Date2
IF #Compare_Year = 'no'
BEGIN
;WITH C AS (SELECT ID
, Name
, DATEPART(DD, DOB) AS Day
, DATEPART(MM, DOB) AS Month
FROM #table_E)
SELECT ID, Name, #Date1, #Date2
FROM C
WHERE C.Month >= #MONTH_1
AND C.Month <= #Month_2
AND C.Day >= #Day_1
AND C.DAy <= #Day_2
END
IF #Compare_Year = 'yes'
BEGIN
SELECT ID, Name, DOB
FROM #table_E
WHERE DOB <= #Date2
AND DOB >= #Date1
END
ELSE
PRINT WHAT! FOLLOW THE RULES YOU FOOL!!!
END
jk. that last part about fools is probably not included in your final draft. ;)
I would make it very simple not so much complex.
SELECT * FROM EMPLOYEE WHERE DOB >= '1970-01-01' AND DOB <= '1980-02-27'
This filters all dates between these two dates

SQL Server : CASE in WHERE clause

I need to create a SQL statement to check where condition if the column is null, it will do other condition. Below is my example:
SELECT
REPLACE(xxShip.Shipment_Date, '/', '-') AS ShipDate,xxShip.Customer,
xxShip.TravelSheetNo
FROM
(SELECT
RANK() OVER (PARTITION BY TravelSheetNo ORDER BY created_on DESC) AS shipwocode,
ShipDate, ShipTime, Shipment_Date,
Customer, Product_no, TravelSheetNo,
[status], ProcessLotNo
FROM
xxDEShipment) xxShip
WHERE
xxShip.shipwocode = 1
AND xxShip.TravelSheetNo = 'ABC'
--here i need to check the condition----------------------------
AND (CASE
WHEN ISNULL(xxShip.Shipment_Date,'') != '' OR xxShip.Shipment_Date != ''
THEN
xxShip.Shipment_Date IS NULL OR xxShip.Shipment_Date = ''
ELSE
CONVERT(DATETIME, xxShip.Shipment_Date) >= #DATESTART and CONVERT(DATETIME, xxShip.Shipment_Date) <= #DATEEND
END)
Please help. Thank you
This should be something like this:
AND(
(ISNULL(xxShip.Shipment_Date,'') <> '' AND
CONVERT(DATETIME, xxShip.Shipment_Date) >= #DATESTART AND
CONVERT(DATETIME, xxShip.Shipment_Date) <= #DATEEND
)
OR ISNULL(xxShip.Shipment_Date,'') == ''
)
Note that storing dates as strings is a bad practice.
If you need to select dates between #DATESTART and #DATEEND or null or empty date, use below:
ISNULL(CONVERT(DATETIME, NULLIF(xxShip.Shipment_Date, '')), #DATESTART)
BETWEEN #DATESTART AND #DATEEND

Case Statement With Between Clause In Sql Server

I am used to get parameters to my Stored Procedure with a default value ''. I want to check whether if the Date is selected and , if so I want to Execute My Between Clause. In Normal Scenario I am gonna do like this
SELECT tbl1.Column1,tbl1.Column2 FROM table1 tbl1
WHERE tbl1.Column1 = CASE WHEN #Column1Val = '' THEN tbl1.Column1 ELSE #Column1Val END
But I can't Do this with Between Clause. I can't figure a way to do this other than dynamic query. Is there a way other than Dynamic Query?
This is what I am Trying to do
SELECT tbl1.Column1,tbl1.Column2 FROM table1 tbl1
WHERE tbl1.txnDate = CASE WHEN #DateTo = '1900-01-01' AND #DateFrom = '1900-01-01'
THEN CAST(tbl1.txnDate AS DATE)
ELSE CAST(tbl1.txnDate AS DATE) BETWEEN #DateTo AND #DateFrom
END
Try this:
SELECT tbl1.Column1,tbl1.Column2
FROM table1 tbl1
WHERE
(#DateTo = '1900-01-01' AND #DateFrom = '1900-01-01')
OR
(
NOT (#DateTo = '1900-01-01' AND #DateFrom = '1900-01-01')
AND (CAST(tbl1.txnDate AS DATE) BETWEEN #DateFrom AND #DateTo)
)
From what I understand, '1900-01-01' is your default value for start and end dates, so you only need a filter if the user has selected some non-default values for start and end dates. Please let me know if this is what you need.
Demo