SQL Case statement based on dates - sql

I need to create a query that includes a CASE statement that basically says
CASE if getdate() > datedue by 31 days
then status = 'Blocked
END
Does anybody know how to check if todays date is greater than say, 25/10/2012 by 31 days?
EDIT
Select co.OrderID, cu.FName + ' ' + cu.SName as 'Name',
cu.Address1 + ', ' + cu.Address2 + ', ' + cu.Address3 as 'Dispatch Address',
cu.PostCode,
ma.MaterialName as 'Item',
mi.Price as 'Item Price',
co.DateOrdered as 'Order Date',
pm.DueDate,
pm.Overdue,
HERE I NEED TO WRITE A CASE STATEMENT TO INSERT INTO A LOCKEDACCOUNT TABLE
so for example CASE WHEN DATEDIFF(dd, GETDATE(), pm.DueDate) >= 31 THEN INSERT INTO LOCKEDACCOUNT (id, status, datelocked, customerid) VALUES (.....)
END
from Customers cu

Like this:
SELECT
CASE
WHEN DATEDIFF(dd, GETDATE(), #duedate) >= 1 THEN 'blocked'
ELSE 'Not'
END AS Status;
SQL Fiddle Demo
Note that: If you didn't specify an ELSE clause, the default will be NULL.
Update: You can insert into a table with CASE expression like so:
INSERT INTO Statuses VALUES
(CASE
WHEN DATEDIFF(dd, GETDATE(), CAST('20121025' AS DATE)) >= 31 THEN 'Blocked'
ELSE 'Not'
END);
Updated SQL Fiddle Demo

select CASE
WHEN DATEDIFF(dd,#duedate,getdate()) >= 31 then 'Blocked' else 'NO' end

Related

simplify a SQL case statement in a case expression

How would I simplify this case statement in T-SQL? It provides the desired result, but it's very unwieldy and hard to read. I have to use the inner case statement to convert a Julian date (aka 6 digit number) into a regular date format.
Basically i'm doing a datediff( getdate(), case statement). Getdate() just returns the time now (ie. 2/27/2020) and the case statement converts a julian date (ie. 123456) into a normal date (ie, 1/1/2020).
Here's the expect output if the query was ran today on Feb 27.
Select CASE
WHEN Datediff(day, Getdate(), CASE
WHEN a.wadpl = 0
THEN NULL
ELSE Dateadd(d, Substring(Cast(wadpl AS VARCHAR(6)), 4, 3) - 1, CONVERT(DATETIME, CASE
WHEN LEFT(Cast(wadpl AS VARCHAR(6)), 1) = '1'
THEN '20'
ELSE '21'
END + Substring(Cast(wadpl AS VARCHAR(6)), 2, 2) + '-01-01'))
END) < 0
THEN 'Overdue Now'
WHEN Datediff(day, Getdate(), CASE
WHEN a.wadpl = 0
THEN NULL
ELSE Dateadd(d, Substring(Cast(wadpl AS VARCHAR(6)), 4, 3) - 1, CONVERT(DATETIME, CASE
WHEN LEFT(Cast(wadpl AS VARCHAR(6)), 1) = '1'
THEN '20'
ELSE '21'
END + Substring(Cast(wadpl AS VARCHAR(6)), 2, 2) + '-01-01'))
END) <= 30
THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM Table_X
Here is a easy one to understand, assuming a.wadpl is an integer:
SELECT CASE
WHEN DATEDIFF(DAY, GETDATE(), DATEADD(DAY, a.wadpl % 1000, DATEADD(YEAR,a.wadpl / 1000,'1899-12-31'))) <0 THEN 'Overdue now'
WHEN DATEDIFF(DAY, GETDATE(), DATEADD(DAY, a.wadpl % 1000, DATEADD(YEAR,a.wadpl / 1000,'1899-12-31'))) <= 30 THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM Table_X
or you can simplify by using a subquery (or you can use a WITH):
SELECT CASE
WHEN Age <0 THEN 'Overdue now'
WHEN Age <= 30 THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM (
SELECT DATEDIFF(DAY,GETDATE(),
DATEADD(DAY,wadpl%1000,DATEADD(YEAR,wadpl/1000,'1899-12-31'))) Age, *
FROM Table_X) a
This will of course cause you to do this arithmetic for each row, and you can't easily use any indexes. If you were asking about aggregates, then I would suggest doing the opposite, and pre-calculating the dates and use those in your query instead. You might also want to consider putting a persisted computed column on table_x:
ALTER TABLE TABLE_X
ADD wadpl_dt AS
(DATEADD(DAY,wadpl%1000,DATEADD(YEAR,wadpl/1000,'1899-12-31'))) PERSISTED;
Now you can just refer to table_x.wadpl_dt whenever you want the datetime, and your query would become:
SELECT CASE
WHEN Age <0 THEN 'Overdue now'
WHEN Age <= 30 THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM (
SELECT DATEDIFF(DAY,GETDATE(), wadpl_dt) Age, *
FROM Table_X) a
Here is the easy way to convert a date to what you refer to as the julian date:
SELECT (DATEPART(YEAR,GETDATE())-1900) * 1000 + DATEPART(DAYOFYEAR, GETDATE())
And this is how you can use it:
DECLARE #overdue int;
DECLARE #next30 int;
SET #overdue = (SELECT (DATEPART(YEAR,GETDATE())-1900) * 1000 + DATEPART(DAYOFYEAR, GETDATE()));
SET #next30 = (SELECT (DATEPART(YEAR,GETDATE()+30)-1900) * 1000 + DATEPART(DAYOFYEAR, GETDATE()+30));
SELECT CASE
WHEN wadpl < #overdue THEN 'Overdue now'
WHEN wadpl <= #next30 THEN 'Coming due in 01-30 days'
ELSE 'Not Overdue'
END [Overdue Status]
FROM Table_X

How to concatenate integer with string

i'm trying this
CAST(DATEDIFF(month,[patient_date_birth],getdate()) as varchar(10))+ 'month'
but not working !!
any help please
This is my select query
SELECT study_patient_name+' '+study_patient_prenom as Patient,
CASE
WHEN DATEDIFF(month,patient_date_birth,getdate()) > 12 THEN DATEDIFF(year,patient_date_birth,getdate())
ELSE CAST(DATEDIFF(month,patient_date_birth,getdate()) as varchar )+ ' month'
END as Age
from patient
This should work
CAST(DATEDIFF(month,CAST([patient_date_birth] AS DATE),getdate()) as varchar(10))+ ' month'
the error i made is i didn't cast the first part of case CAST(DATEDIFF(year,patient_date_birth,getdate()) as varchar)
You need to cast both elements of the CASE statement to varchar so the result is always varchar regardless of whether 'month' appears in the result:
SELECT
'dave'+' '+'rave' as Patient,
'ENT' as Service,
CASE
WHEN DATEDIFF(month, '2015-02-25', getdate()) > 12
THEN CAST(DATEDIFF(year, '2015-02-25', getdate()) as varchar(10))
ELSE CAST(DATEDIFF(month, '2015-02-25', getdate()) as varchar(10)) + ' month'
END as Age

sum (case when else end) statement

I have a SQL Server SQL statement that works but it is seemingly messy and inefficient. It checks each row of data for its data and groups it into Yesterday, WeektoDate, MonthtoDate, and YeartoDate and sums each field by the group. Is there a better way to do this?
Below is a sample of what I have, there are actually 15 or so fields in the actual query, which makes for a long SELECT query, Is there a better way to do this?
SELECT
sum(CASE when row_date BETWEEN #YesterdayMorning and #EndOfYesterday THEN Total_Calls ELSE 0 END) AS Calls_Yesterday
,sum(CASE when row_date BETWEEN #YesterdayMorning AND #EndOfYesterday THEN Total_Time ELSE 0 END) AS Time_Yesterday
,sum(CASE when row_date BETWEEN #WTDMorning and #EndOfYesterday THEN Total_Calls ELSE 0 END) AS Calls_WTD
,sum(CASE when row_date BETWEEN #WTDMorning AND #EndOfYesterday THEN Total_Time ELSE 0 END) AS Time_WTD
,sum(CASE when row_date BETWEEN #MTDMorning and #EndOfYesterday THEN Total_Calls ELSE 0 END) AS Calls_MTD
,sum(CASE when row_date BETWEEN #MTDMorning AND #EndOfYesterday THEN Total_Time ELSE 0 END) AS Time_MTD
,sum(CASE when row_date BETWEEN #YTDMorning and #EndOfYesterday THEN Total_Calls ELSE 0 END) AS Calls_YTD
,sum(CASE when row_date BETWEEN #YTDMorning AND #EndOfYesterday THEN Total_Time ELSE 0 END) AS Time_YTD
FROM TableName
One option is to get rid of the CASE, break them into multiple SELECT queries with WHERE conditions and then union them:
SELECT
'Yesterday' AS Range
SUM(Total_Calls) AS Calls,
SUM(Total_Time) AS Time
FROM TableName
WHERE row_date BETWEEN #YesterdayMorning AND #EndOfYesterday
UNION
SELECT
'WTD' AS Range,
SUM(Total_Calls) AS Calls,
SUM(Total_Time) AS Time
FROM TableName
WHERE row_date BETWEEN #WTDMorning AND #EndOfYesterday
UNION
SELECT
'MTD' AS Range,
SUM(Total_Calls) AS Calls,
SUM(Total_Time) AS Time
FROM TableName
WHERE row_date BETWEEN #MTDMorning AND #EndOfYesterday
UNION
SELECT
'YTD' AS Range,
SUM(Total_Calls) AS Calls,
SUM(Total_Time) AS Time
FROM TableName
WHERE row_date BETWEEN #YTDMorning AND #EndOfYesterday
I do not think your query is that inefficient, the query plan looks ok, but I agree the code is a bit messy. I would consider some thing like this to avoid the duplicated code:
Add all morning dates to a table
Join dates to the call table
SUM calls and call time
Query:
DECLARE #day VARCHAR(2) = FORMAT(DAY(GETDATE()), '00'), #month VARCHAR(2) = FORMAT(MONTH(GETDATE()), '00'), #year INT = YEAR(GETDATE());
DECLARE #weekday INT = DATEPART(dw, GETDATE());
DECLARE #morningTime TIME = '08:00';
DECLARE #EndOfYesterday DATETIME2 = DATEADD(dd, -1, CONVERT(DATETIME2, CONCAT(YEAR(GETDATE()), MONTH(GETDATE()), DAY(GETDATE()), ' 18:00')));
DECLARE #timeTable TABLE(ID INT IDENTITY(1,1) PRIMARY KEY, Name VARCHAR(255), FromDate DATETIME2);
INSERT INTO #timeTable
SELECT 'Yesterday', DATEADD(dd, -1, CONVERT(DATETIME2, CONCAT(#year, #month, #day, ' ', #morningTime)))
UNION ALL SELECT 'WTDMorning', DATEADD(dd, -(#weekday-1), CONVERT(DATETIME2, CONCAT(#year, #month, #day, ' ', #morningTime)))
UNION ALL SELECT 'MTDMorning', CONVERT(DATETIME2, CONCAT(#year, #month, '01', ' ', #morningTime))
UNION ALL SELECT 'YTDMorning', CONVERT(DATETIME2, CONCAT(#year, '01', '01', ' ', #morningTime))
SELECT t.Name, Total_Calls = SUM(c.Total_Calls), Total_Time = SUM(c.Total_Time)
FROM #timeTable t
LEFT JOIN TableName c ON c.row_date BETWEEN t.FromDate AND #EndOfYesterday
GROUP BY ID, t.Name
ORDER BY ID
On my test data your query actually have a 15% lower query cost that my query, but with an index on the "call table" my query is 20% cheaper then yours.
Index:
CREATE NONCLUSTERED INDEX idxRowDate
ON [dbo].[TableName] ([row_date])
INCLUDE ([Total_Calls],[Total_Time])
I am aware that this do not match your output 100%, but I do believe that rows are easier to work with than 15+ columns. This could also be converted to one row if needed.
One row support:
If it is needed, you could convert the rows to one line with dynamic sql. But it gets a bit more complex:
DECLARE #onRowconverter VARCHAR(MAX) = '';
SELECT #onRowconverter = #onRowconverter + CASE WHEN #onRowconverter > '' THEN ', ' ELSE 'SELECT ' END + CONCAT('[Calls_' + t.Name + '] = ', SUM(c.Total_Calls), ', [Time_' + t.Name + '] = ', SUM(c.Total_Time))
FROM #timeTable t
LEFT JOIN TableName c ON c.row_date BETWEEN t.FromDate AND #EndOfYesterday
GROUP BY ID, t.Name
ORDER BY ID
EXEC(#onRowconverter);

Case Statement in SQL giving error

I am trying to write a query which will give output as MONTH, YEAR.
When I write :
select CAST( tbl.MONTH as varchar(2) ) + ', ' + CAST ( tbl.YEAR as varchar(4) ) as [DATE]
from TABLE as tbl
I get the output as
1,2014
4,2014 (depending upon the input)
But, Now, I want to replace 1 with JAN, 4 with APRIL 12 with DEC
So, I tried to write a case statement as :
SELECT
case when tbl.MONTH ='1'
then ('JAN' + ', ' + CAST ( tbl.YEAR as varchar(4) )) as [DATE1]
from TABLE as tbl
and this gives syntax error.
Can anyone tell me, what I should be doing ?
The case needs an end:
SELECT (case when tbl.MONTH = '1' then 'JAN' + ', ' + CAST(tbl.YEAR as varchar(4) ))
end) as [DATE1]
from TABLE tbl;
If the values are being stored as numbers, then don't put single quotes around the constant value.
You are missing the end, which is needed to close the case statement:
case
when tbl.MONTH ='1'
then ('JAN' + ', ' + CAST ( tbl.YEAR as varchar(4) ))
end
as [DATE1]
from TABLE as tbl
Simple way is by using Datename Inbuilt function. No need of CASE statement to hardcode all the months
SELECT Datename(mm, tbl.[MONTH]) + ', '
+ CONVERT(VARCHAR(10), tbl.YEAR) AS [DATE]
FROM TABLE AS tbl
or if you are using sql server 2012
SELECT Choose(tbl.[MONTH], 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
+ ', ' + CONVERT(VARCHAR(10), tbl.YEAR) AS [DATE]
FROM TABLE AS tbl
Similar to Pradeep's answer, but using a handy little DATENAME fix from https://stackoverflow.com/a/188390/405180, and trimming to first 3 chars:
SELECT UPPER(LEFT(Datename(MONTH , DateAdd( MONTH , tbl.[MONTH] , -1 )), 3)) + ', ' + CONVERT(VARCHAR(10), tbl.[YEAR]) AS [DATE]
FROM TABLE AS tbl
TBH, I think from a performance point of view, you're better off using a CASE tbl.[MONTH] WHEN 1 THEN 'JAN' WHEN ... approach.

SQL SUM() function ignoring WHERE clause and CASE statement

SELECT u.FirstName + ' ' + u.LastName as 'User',
COUNT(*) as 'Number of Calls',
CONVERT(varchar(4), SUM(CASE WHEN c.FromTime BETWEEN #FromDate AND #ToDate
THEN DATEDIFF(mi, c.FromTime, c.ToTime) ELSE 0 END) / 60) + ':' +
CASE WHEN SUM(CASE WHEN c.FromTime BETWEEN #FromDate AND #ToDate
THEN DATEDIFF(mi, c.FromTime, c.ToTime) ELSE 0 END) % 60 < 10 THEN '0' ELSE '' END +
CONVERT(varchar(2), SUM(DATEDIFF(mi, c.FromTime, c.ToTime)) % 60) as 'Total Time Spent',
CONVERT(varchar(4), AVG(DATEDIFF(mi, c.FromTime, c.ToTime)) / 60) + ':' +
CASE WHEN AVG(DATEDIFF(mi, c.FromTime, c.ToTime)) % 60 < 10 THEN '0' ELSE '' END +
CONVERT(varchar(2), AVG(DATEDIFF(mi, c.FromTime, c.ToTime)) % 60) as 'Average Call Time'
FROM Calls c
JOIN Users u ON u.UserID = c.TakenBy
WHERE c.FromTime BETWEEN #FromDate AND #ToDate
GROUP BY u.UserID, u.FirstName, u.LastName
ORDER BY u.FirstName + ' ' + u.LastName
The preceding SQL query returns the correct "Number of Calls" but the "Total Time" and "Average Time" are always the same regardless of the # of calls (which is obviously wrong).
I've read and tried to implement using the CASE WHEN __ Then value ELSE 0 inside SUM but it still returns an incorrect value.
The only way I can get this query to return correct results is if I completely strip out all other info, e.g.
SELECT SUM(DATEDIFF(mi, FromTime, ToTime)) FROM Calls WHERE c.FromTime BETWEEN...
How can I still use my JOIN and GROUP BY and get the aggregate functions to give me the results I want?
Thanks for any and all help!
You're probably better off with a subquery, e.g. in the SELECT part, add something like (SELECT SUM(...) FROM ... WHERE ...) AS total_sum.