Apply Different WHERE clause depending on value of one field - sql-server

i'm trying to build a query in which I need to apply 2 different where clauses, depending on the value of Current Month. In this case, I need to show data from the last 2 years, only of the months before the current month:
Example 1:
Current Date is: 01-01-2017
Need to show data from:
01/2015; 02/2015; 03/2015; 04/2015; 05/2015; 06/2015;
07/2015; 08/2015; 09/2015; 10/2015; 11/2015; 12/2015;
01/2016; 02/2016; 03/2016; 04/2016; 05/2016; 06/2016;
07/2016; 08/2016; 09/2016; 10/2016; 11/2016; 12/2016.
Example 2:
Current Date is: 01-03-2017
Need to show data from: 01/2016; 02/2016; 01/2017; 02/2017.
So I built the following query:
SELECT *
FROM TABLE1
WHERE
CASE MONTH(GETDATE())
WHEN 1
THEN YEAR(Data)>=YEAR(GETDATE())-2 and YEAR(data)<YEAR(GETDATE())
ELSE YEAR(Data)>=YEAR(GETDATE())-1 and YEAR(data)<=YEAR(data) and MONTH(data)<MONTH(GETDATE())
END
I'm getting an error.
Can you please help me?
Thank you.

Your syntax is incorrect for sure. THEN is not a logical expression - it is supposed to return value. So you can't write logical expression in THEN/ELSE blocks as you have attempted to. Instead you might try something like:
WHERE
#date >= CASE WHEN a=b THEN '20150101' ELSE '20160202' END
Another thing is: conversions and functions in predicate are very bad for performance. When working with dates you might want to prepare filter predicate before the query when possible, e.g.:
declare
#date_begin date,
#date_end date
set #date_end = DATEADD(..., #arg_date)
set #date_begin = DATEADD(YEAR, -2, #date_end)
select ...
where date between #date_begin and #date_end
in your case it could be something like:
declare
#arg_date DATE = GETDATE(),
#date_begin DATE,
#date_end DATE,
#max_month INT
set #max_month = MONTH(#date)
if #max_month = 1
begin
set #date_end = DATEADD(dd, 1-DATEPART(dy, #arg_date), #arg_date) /* first day of year */
set #date_begin = dateadd(YY, -2, #date_end)
end
else
begin
set #date_end = #arg_date
set #date_begin = dateadd(YY, -1, DATEADD(dd, 1-DATEPART(dy, #date_end), #date_end)) /* first day of year_begin */
end
SELECT *
FROM TABLE1 t
WHERE t.date >= #date_begin and t.date < #date_end
AND (#max_month = 1 OR MONTH(t.date) < #max_month)
another (a better) way is to prepare #periods table variable, put each (date_begin, date_end) pair you need into it and join with TABLE1 - you'll get rid of all function calls from within WHERE clause.
You should realize: you know exactly which periods of each year you need in the result set. There is nothing to compute from stored TABLE1->date column. Just filter it with precomputed date intervals. Don't convert or modify date column - it is already ready to use. Merely apply appropriate filters. MONTH(date) <= 3 is date <= 20170331. Don't torture left part - prepare appropriate right part of such predicates.

The easiest way would be something like:
SELECT *
FROM TABLE1
WHERE
(YEAR(Data)>=YEAR(GETDATE())-2 and YEAR(data)<YEAR(GETDATE()) AND MONTH(GETDATE()) = 1)
OR (YEAR(Data)>=YEAR(GETDATE())-1 and MONTH(data)<MONTH(GETDATE()) and MONTH(GETDATE()) <> 1)
(Note I removed the superfluous and YEAR(data)<=YEAR(data).).
Personally I prefer (and I think it's generally advised) AND/OR logic to a CASE in a WHERE clause.
The error with your CASE statement is caused by the fact that CASE returns an atomic value. It cannot be used in the same way as if in procedural languages.

You can't swap in additional statements to your where clause using case statements. Instead, you need to resolve the case to an equality:
select *
from Table1
where case month(getdate()) -- You want to avoid using functions on fields in your WHERE claises, as this can reduce performance.
when 1 then case when Data >= dateadd(year,datediff(year,0,getdate())-2,0)
and Data < dateadd(year,datediff(year,0,getdate()),0)
then 1 -- Data rows the meet the criteria will return 1.
else 0 -- Data rows that do not will return 0.
end
else case when (Data >= dateadd(year,datediff(year,0,getdate())-1,0)
and Data < dateadd(m,datediff(m,0,getdate())-12,0)
)
or (Data >= dateadd(year,datediff(year,0,getdate()),0)
and Data < dateadd(m,datediff(m,0,getdate()),0)
)
then 1
else 0
end
end = 1 -- Then limit the results to only those rows that returned a 1.
In your specific instance however, this can be simplified to a standard or:
select *
from Table1
where (month(getdate()) = 1
and Data >= dateadd(year,datediff(year,0,getdate())-2,0)
and Data < dateadd(year,datediff(year,0,getdate()),0)
)
or (month(getdate()) <> 1
and (Data >= dateadd(year,datediff(year,0,getdate())-1,0)
and Data < dateadd(m,datediff(m,0,getdate())-12,0)
)
or (Data >= dateadd(year,datediff(year,0,getdate()),0)
and Data < dateadd(m,datediff(m,0,getdate()),0)
)
)
Note the use of brackets above to separate out the logical tests. Where a Data row meets either one of those criteria it will be returned in your query.

Related

Join columns from different query

I want to do the same query using a variable at the where condition from 0 to 24 and join the results at the same column.
DECLARE #x AS INT
SET #x = 0
WHILE #x < 24
BEGIN
SELECT #x, COUNT(trans_num)
FROM [RealTimeVending].[dbo].[rtv_transactions]
WHERE CONVERT(DATE, trans_date) >= '2018-03-28'
AND DATEPART(HH, trans_date) = #x
AND cliente_id = 13
GROUP BY cliente_id
SET #x = #x + 1
END
This is what I get :
The problem with my solution is that I have different querys and I want the results at the same column.
You don't need a while loop for the required output, following simple query should give you desired output.
SELECT COUNT(trans_num), DATEPART(HH,trans_date)
FROM [RealTimeVending].[dbo].[rtv_transactions]
where convert(date,trans_date) >= '2018-03-28'
and cliente_id=13
GROUP BY DATEPART(HH,trans_date)
ORDER BY DATEPART(HH,trans_date)
If you are fetching the data for a single client cliente_id=13, in that case you don't need to put group by client_id. It will only be required if you fetching the data for multiple clients.
PSK's answer is dead on. A minor variation to that to avoid duplicating the DATEPART function is to use a sub-query. Note that I also removed the conversion of trans_date to date portion as it is redundant. You can directly compare the time to the date which will yield a faster query also.
SELECT t.Hour, COUNT(*) AS Count
FROM (
SELECT
DATEPART(HH,trans_date) AS Hour
FROM [RealTimeVending].[dbo].[rtv_transactions]
WHERE
trans_date >= '2018-03-28'
AND cliente_id = 13
) AS t
GROUP BY Hour
ORDER BY Hour

Query using a date range but end date may be null

sql server 2014
I am trying to query data using a date range. Data in the table is datetime datatype.
so I want to use as parameters #IncidentDate and #IncidentEndDate.
Issues are that the #IncidentEndDate may be null. Also each row in the data may or may not have an end_datetime (null if no date)
In my where clause I have
(end_datetime IS NULL) AND (#IncidentDate >= CAST(start_datetime AS DATE) AND #IncidentDate <= DATEADD(d,0,DATEDIFF(d,0,start_datetime)))
OR
(#IncidentDate <= end_datetime) AND (#IncidentEndDate >= start_datetime)
However I am not sure if this si working properly. I would expect rows that have no end_datetime to appear in the results but they don't seem to be .
EDIT: In the end I came up with the following after reading everybody's replies...
WHERE (
#IncidentDate <= isnull(end_datetime, dateadd(day,1,start_datetime))
)
AND (
isnull(#IncidentEndDate,dateadd(day,1,#IncidentDate)) >= start_datetime
)
This seems to me to be a tidier way to satisfy my requirements - it looks after the possiblity of both end_datetime being null and #IncdidentEndDate being Null
Also, you can use the ISNULL() function to help handle NULL values. The ISNULL() function checks the value and, if it's NULL, replaces it with a supplied value. So if you wanted the NULLs, you could put in a value that would match or, if not, something that would definitely be outside your range like 01/01/1900 or the MIN Date (varies depending on DATETIME or DATETIME2).
To exclude:
SELECT * FROM mySweetTable WHERE ISNULL(createdDate, '01-JAN-1900') >= '01-JAN-2015'
To include:
SELECT * FROM mySweetTable WHERE ISNULL(createdDate, GETDATE()) >= '01-JAN-2015'
Although, I don't recommend ACTUALLY leaving GETDATE() in the WHERE clause, that's bad news bears; replace it with a variable or specific value.
--This should get you what you want
start_datetime >= #IncidentDate and (#IncidentEndDate is null or end_datetime is null or end_datetime < dateadd(day,1,#IncidentEndDate))
Unless, of course, you want to exclude ones where the end_datetime is null and if that's the case just let me know!

selecting records from a table where a given date falls between 2 dates (columns) from a table in SQL Server

EDIT -----------------
I'm going to try to clarify.
I have this table:
A user will input a start date and an end date.
Let's say the user inputs 2011-10-21 for the start and 2011-12-18 for the end.
This will bring back all the records:
But what if the user inputs 2011-10-22 and 2011-12-18?
The 2011-10-22 is BETWEEN the date range of the FIRST ROW 2011-10-21 (start) & 2011-10-23 (end).
SINCE IT IS BETWEEN THE DATE RANGE OF THE FIRST RECORD, I WANT to return that row as well. I DO NOT WANT TO EXCLUDE IT.
So if if I run a query like this:
select * from table where PayPeriodStart between '2011-10-22' and '2011-12-18'
I WANT to see this:
and NOT this:
PLEASE NOTE... the user can enter in any start or end date and it'll be at least 1 week.
So they can pick 2011-11-09 (start) and 2011-12-04 (end) for example.
You'll want the search criteria to return the rows where the period start date is before the end date criteria and the period end date is after the start date criteria. In other words:
declare #Table table (PayPeriodStart smalldatetime, PayPeriodEnd smalldatetime)
insert into #Table (PayPeriodStart, PayPeriodEnd)
select '2010-01-01', '2010-12-31' -- test before the criteria; is not returned
union select '2011-10-21', '2011-10-23' -- overlaps start date; is returned
union select '2011-10-24', '2011-11-06' -- fully in date range; is returned
union select '2011-11-07', '2011-11-20' -- fully in date range; is returned
union select '2011-11-21', '2011-12-04' -- fully in date range; is returned
union select '2011-12-05', '2011-12-18' -- overlaps end date; is returned
union select '2012-01-01', '2012-12-31' -- test after the criteria; is not returned
-- This yields all but the first row
select * from #Table where PayPeriodStart between '2011-10-22' AND '2011-12-10'
-- This will yield all the rows that overlap the search criteria
select * from #Table
where PayPeriodStart <= '2011-12-10' -- the end search date
and PayPeriodEnd >= '2011-10-22' -- the start search date
If you are looking for all records that are valid between '2011-10-22' AND '2011-12-10' , this is the query you should use
select * from table where ('2011-10-22' >= PayPeriodStart AND '2011-10-22' <= PayPeriodEnd) Or ('2011-10-22' < PayPeriodStart AND '2011-12-10' > PayPeriodEnd)
I have never been a fan of the between syntax for the edge case reason, so I tend to use this syntax instead:
select * from table where ('2011-10-22' <= PayPeriodStart AND PayPeriodStart <= '2011-12-10')
Same end result, but I can ensure I include the boundaries as needed.
Are you asking for
select * from table where '2011-10-22' between PayPeriodStart AND PayPeriodEnd
AND '2011-12-10' between PayPeriodStart AND PayPeriodEnd
If you want to get data based on first of the month and last of the month or for fixed time interval like 30 day window, you may have to use DateAdd, DateDiff functions to calculate dates.
Following example is based on FirstOfTheMonth, LastOfTheMonth calculation
your reference data
declare #refDate date = getdate()
select #min = dateadd(mm, datediff(mm, 0, #refDate), 0) -- First of the month
, #max = dateadd(dd, -1, dateadd(mm, datediff(mm, 0, #refDate) + 1, 0)) --Last of the month

Multiple result sets excluding column

I have a collection of entries in a table, table is joined with another table and together I need to return a resultset excluding entries by a particular date value.
Table 1
I need to return a collection of entries based on a query and find the value, along with a collection of other items where the date as per the screenshot is <= GETDATE()
Results should be
As you can see, the resultset returns all three of the General Worker items but should only return where the date time is <= GetDate().
I have tried various approaches, from the (SELECT .. (PARTITION)) approach to sub-value table results and none of them return the resultset I need.
I need all other rows intact with only the General Worker where date <= GETDATE() and I'm stuck.
UPDATE
My T-SQL statement before modifications:
SELECT
T0.nContractID,
T1.sJobCatNo,
T1.nJobCatID,
T1.sJobCatDesc,
T1.nDeleted,
T1.nAdminLocked,
T1.nClientDefault,
T1.nRateNT,
CASE
WHEN (T0.sDistributionCode IN ('Nails', 'Board'))
THEN 1
ELSE 0
END AS 'ShowRate'
FROM
[dbo].[Contract] AS T0
INNER JOIN [dbo].[JobCategoryRates] AS T1 ON T1.nContractID = T0.nContractID
WHERE
T1.nContractID = 200198
AND T1.nDeleted = 0
ORDER BY
T1.sJobCatDesc
UPDATE 2
I need the results to look like this:
UPDATE 3
Maybe this might help?
Table 1, for nContractID returns 19 results (3 of which are the same), the only distinct value is the dEndDate column should should be <= GETDATE(). I need to extract all values where dEndDate is null and dEndDate <= GETDATE(). Everything I've tried thus far brings back only one result, but logic in my head says I should have 17 results, if the dEndDate items >= GETDATE() is removed?
Need to clean up the query and your thought process
If you want to debug dEndDate then include it in the output
All values where dEndDate is null and dEndDate <= GETDATE() is always false.
A value cannot be null and have a value.
In the default configuration a comparison to null is always false.
null <= 1/1/2000 is false
null >= 1/1/2000 is false
null = null is false
If you want null OR dEndDate <= GETDATE() then:
where dEndDate is null or dEndDate <= GETDATE()
Why would you expect this not to return one row?
dEndDate <= GETDATE()

SQL CASE Statement (keeping the CASE statement within the WHERE clause)

I have a table named:
Ext_Meeting_Status
This has fields
Ext_Meeting_Status_ID
TEXT
The values are:
EXT_Meeting_Status_ID Text
1 Draft
2 Published
3 Cancelled
4 Closed
How do i return the "Text" field of "Published" if date is today, else return "Close".
I tried using:
select * from Ext_Meeting_Status
where
GETDATE() = CASE
WHEN ( GETDATE() = '2010-12-13 10:02:31.560' )
THEN ( Ext_Meeting_Status_ID=2)
ELSE ( Ext_Meeting_Status_ID=4)
END
select * from Ext_Meeting_Status
where Ext_Meeting_Status_ID =
CASE WHEN (GETDATE() = '2010-12-13 10:02:31.560')
THEN (2)
ELSE (4)
END
I believe this should work..
One more note : Comparing current date to the exact millisecond level might not work as the query may not get executed at that time...
You may try something like this.
Select getdate(), * from #Temp
Where ID = Case when getdate() between '2010-12-13 05:21:08.240' and '2010-12-13 05:22:08.240'
Then 1
Else 2
End
I don't think it's possible. You may want to try to put it all into a sub-select and use the CASE there.

Resources