Case Statement Sql Multiple column check for dates - sql

I have a stored procdure that uses case statement as follows: What I am trying to do is evaluate 2 columns in the testTable for dates. So the below case statement says that if stop_date is null or greater than current date then set is_active cloumn is Y else N
What I am trying to do is also evaluate another date column say another_stop_date and check if it is null or has a date greater then today and use same logic to update the is_active column
I am not sure if we can use multiple case statement logic to update a single column?
I have commented the code below where I am not getting the right results
Basically need to evaluate stop_dt and another_stop_date columns from testTable!
USE [Test]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[p_test]
#Blank_Row CHAR(1) = 'N'
AS
BEGIN
SET NOCOUNT ON
DECLARE #TD DATETIME
SELECT #TD = GETDATE()
DECLARE #tempTable TABLE (
ID INT,
c_id INT,
desc varchar(40),
date datetime,
s_col TinyINT,
is_active char(1),
stuff VARCHAR(8))
INSERT INTO #tempTable
SELECT id, c_id, desc, max( date ), 1,
CASE WHEN (stop_dt IS NULL OR stop_dt > #TD) THEN 'Y'
--//Case When (another_stop_date is NULL or another Stop_date > #TD) THEN 'Y'<-----confused here
ELSE 'N' END,
stuff
FROM testTable
GROUP BY id, stop_dt, c_id, desc, stuff, another_stop_date
Select * from tempTable

You can combine clauses in a case statement with the usual logical operators, as well as having separate cases:
Case
When
(stop_dt is null or stop_dt > #td) and
(another_stop_date is null or another_stop_date > #td)
Then 'Y'
Else 'N'
End

Case statement operate close to if statements and can have multiple clauses.
Case when condition_1 > 1 then 'hi'
when condition_1 < 14 then 'no'
when condition_89 > 12 then 'why is this here'
else 1
end
Apply it to your statement:
CASE WHEN (stop_dt IS NULL OR stop_dt > #TD) THEN 'Y'
When (another_stop_date is NULL or another Stop_date > #TD) THEN 'Y'<-----confused here
ELSE 'N' END

Related

Add column name to a variable and use it in later calculation in WHERE clause

I have a problem. I need to determine the name of the column under which the calculations will continue. So I wrote a select:
DECLARE #column VARCHAR(MAX)
DECLARE #ColumnA VARCHAR(MAX)
DECLARE #ColumnB VARCHAR(MAX)
SET #ColumnA = 'RegistrationDate'
SET #ColumnB = 'EntryDate'
SET #column = CASE
WHEN CONVERT(DATE,GETDATE()) NOT IN (
'2021-08-04','2021-08-05','2021-08-06','2021-08-07','2021-08-08','2021-08-09','2021-08-10','2021-09-07','2021-09-08','2021-09-09','2021-09-10','2021-09-11',
'2021-09-12','2021-09-13','2021-10-05','2021-10-06','2021-10-07','2021-10-08','2021-10-09','2021-10-10','2021-10-11','2021-11-09','2021-11-10','2021-11-11','2021-11-12','2021-11-13','2021-11-14','2021-11-15','2021-12-07',
'2021-12-08','2021-12-09','2021-12-10','2021-12-11','2021-12-12','2021-12-13'
) THEN
QUOTENAME(#Column)
ELSE
QUOTENAME(#ColumnB)
END
SELECT #column
which returns me [RegistrationDate] or [EntryDate] and stores this in variable #column. Now, when I know under which column should I calculate, I want to insert this variable #column in to my main select one of the WHERE clause:
DECLARE #column VARCHAR(MAX)
DECLARE #ColumnA VARCHAR(MAX)
DECLARE #ColumnB VARCHAR(MAX)
SET #ColumnA = 'RegistrationDate'
SET #ColumnB = 'EntryDate'
SET #column = CASE
WHEN CONVERT(DATE,GETDATE()) NOT IN (
'2021-08-04','2021-08-05','2021-08-06','2021-08-07','2021-08-08','2021-08-09','2021-08-10','2021-09-07','2021-09-08','2021-09-09','2021-09-10','2021-09-11',
'2021-09-12','2021-09-13','2021-10-05','2021-10-06','2021-10-07','2021-10-08','2021-10-09','2021-10-10','2021-10-11','2021-11-09','2021-11-10','2021-11-11','2021-11-12','2021-11-13','2021-11-14','2021-11-15','2021-12-07',
'2021-12-08','2021-12-09','2021-12-10','2021-12-11','2021-12-12','2021-12-13'
) THEN
QUOTENAME(#Column)
ELSE
QUOTENAME(#ColumnB)
END
SELECT
CASE WHEN final.Branch IS NULL THEN 'Total'
ELSE final.Branch
END AS 'Branch',
final.TR
FROM
(
SELECT
CASE
WHEN main.BRANCHNO = 1 THEN 'One'
WHEN main.BRANCHNO = 2 THEN 'Two'
WHEN main.BRANCHNO = 3 THEN 'Three'
WHEN main.BRANCHNO = 4 THEN 'Four'
WHEN main.BRANCHNO = 5 THEN 'Five'
WHEN main.BRANCHNO = 6 THEN 'Six'
END AS 'Branch',
COUNT(*) AS 'TR'
FROM
(
SELECT
*
FROM
[TABLE]
WHERE
Status = 100
AND
BRANCHNO IN (1,2,3,4,5,6)
AND
Type = 'TR'
AND
**#column** = CONVERT(DATE, CASE WHEN DATENAME(dw, getdate()) = 'Monday' THEN getdate()-3 ELSE getdate()-1 END
)
) AS main
GROUP BY
main.BRANCHNO WITH ROLLUP
) AS final
But when I execute query it returns me an error:
Msg 241, Level 16, State 1, Line 11 Conversion failed when converting
date and/or time from character string.
I imagined everything very simple: I put a column name into a variable, and then, that name placed at the beginning of the WHERE clause will be recognized as the column name and then *= CONVERT(DATE, CASE WHEN DATENAME(dw, getdate()) etc will do all work.
But that did not happen. Maybe someone knows why and maybe they know how to solve this task?
You can't use a variable to reference a column name. #column is just a piece of data, which just so happens to contain a column name as a string, but it's still just a string, not actually a reference to a column in a table.
Some options you have seem to be...
AND CASE #column WHEN 'RegistrationDate' THEN RegistrationDate
WHEN 'EntryDate' THEN EntryDate
END
=
CONVERT(DATE, CASE WHEN DATENAME(dw, getdate()) = 'Monday' THEN getdate()-3 ELSE getdate()-1 END)
Or, have two queries which only differ in the column being referenced...
IF (#column = 'RegistrationDate')
<query1>
ELSE IF (#column = 'EntryDate')
<query2>
Or "Dynamic SQL" where you build up a new string with your SQL code and execute that by call sp_executesql (assuming this is SQL Server, which it appears to be).
I recommend reading this : https://www.sommarskog.se/dyn-search.html
EDIT: A pure SQL alternative, assuming SQL Server
DECLARE #mode INT = CASE
WHEN CONVERT(DATE,GETDATE()) NOT IN (
'2021-08-04','2021-08-05','2021-08-06','2021-08-07','2021-08-08','2021-08-09','2021-08-10','2021-09-07','2021-09-08','2021-09-09','2021-09-10','2021-09-11',
'2021-09-12','2021-09-13','2021-10-05','2021-10-06','2021-10-07','2021-10-08','2021-10-09','2021-10-10','2021-10-11','2021-11-09','2021-11-10','2021-11-11','2021-11-12','2021-11-13','2021-11-14','2021-11-15','2021-12-07',
'2021-12-08','2021-12-09','2021-12-10','2021-12-11','2021-12-12','2021-12-13'
) THEN
0
ELSE
1
END;
DECLARE #filter_date DATE = CONVERT(DATE, CASE WHEN DATENAME(dw, getdate()) = 'Monday' THEN getdate()-3 ELSE getdate()-1 END;
WITH
source AS
(
SELECT
*
FROM
[TABLE]
WHERE
Status = 100
AND BRANCHNO IN (1,2,3,4,5,6)
AND Type = 'TR'
),
filtered_source AS
(
SELECT 0 AS mode, * FROM source WHERE RegistrationDate = #filter_date
UNION ALL
SELECT 1 AS mode, * FROM source WHERE EntryDate = #filter_date
)
SELECT
COALESCE(
CASE
WHEN BRANCHNO = 1 THEN 'One'
WHEN BRANCHNO = 2 THEN 'Two'
WHEN BRANCHNO = 3 THEN 'Three'
WHEN BRANCHNO = 4 THEN 'Four'
WHEN BRANCHNO = 5 THEN 'Five'
WHEN BRANCHNO = 6 THEN 'Six'
END,
'Total'
)
AS 'Branch',
COUNT(*) AS 'TR'
FROM
filtered_source
WHERE
mode = #mode
GROUP BY
GROUPING SETS (
(mode),
(mode, BRANCHNO)
);
By always including mode in the GROUPING SETS, the optimiser might be able to yield a better execution plan for the two scenarios.
Still read the link given above though, at the very least to understand why this is necessary, or perhaps why it doesn't quite manage to yield the best execution plan.

How to replace Null with "N/A" in column with DATETIME data type in PostgreSQL

I am trying to replace all null values in a column of a table with "N/A", but the column has a data types of DATETIME, and I am unsure of how to alter my CASE statement so that all NULL values are replaced with "N/A". Relevant line is starred. Please help!
SELECT encounters.patient, encounters.encounterid, encounters.start,
extract(year from age(patients.birthdate, encounters.start)),
CASE
WHEN deathdate BETWEEN start AND stop THEN 1
WHEN deathdate NOT BETWEEN start AND stop THEN 0
WHEN deathdate IS NULL THEN NULL
ELSE '0'
END,
CASE
WHEN v1 IS NOT NULL THEN 1
WHEN v1 IS NULL THEN 0
END,
CASE
WHEN v1_90 IS NOT NULL THEN 1
WHEN v1_90 IS NULL THEN 0
END,
CASE
WHEN startdate_90 IS NOT NULL THEN startdate_90
**WHEN startdate_90 IS NULL THEN NULL**
END
FROM encounters JOIN
patients
ON encounters.patient = patients.id
If you need to replace the values, then the columns need to be strings. This should work:
(CASE WHEN deathdate BETWEEN start AND stop THEN '1'
WHEN deathdate NOT BETWEEN start AND stop THEN '0'
WHEN deathdate IS NULL THEN 'N/A' -- could just be ELSE
END)
If your THEN clauses need to refer to a number or date, then you need to convert that value to a string.

Change aggregate functions to output NULL when a element is NULL

Every question I search for about the warning
Warning: Null value is eliminated by an aggregate or other SET operation.
Typically people want to treat the NULL values as 0. I want the opposite, how do I modify the following stored procedure to make it return NULL instead of 1?
CREATE PROCEDURE TestProcedure
AS
BEGIN
select cast(null as int) as foo into #tmp
insert into #tmp values (1)
select sum(foo) from #tmp
END
GO
I thought it would be SET ANSI_NULLS ON (I tried before the declaration, within the procedure itself, and before executing the procedure in my test query) but that did not appear to change the behavior of SUM(.
The sum() function automatically ignores NULL. To do what you want, you need an explicit checK:
select (case when count(foo) = count(*) then sum(foo) end)
from #tmp;
If you want to be explicit, you could add else NULL to the case statement.
The logic behind this is that count(foo) counts the number of non-NULL values in foo. If this is equal to all the rows, then all the values are non-NULL. You could use the more verbose:
select (case when sum(case when foo is null then 1 else 0 end) > 0
then sum(foo)
end)
And, I want to point out that the title is quite misleading. 1 + NULL = NULL. The issue is with the aggregation functions, not the arithmetic operators.
Looking for a null value with EXISTS may be the fastest:
SELECT
CASE WHEN EXISTS(SELECT NULL FROM tmp WHERE foo IS NULL)
THEN NULL
ELSE (SELECT sum(foo) from tmp)
END
Just say
select case sign(sum(case when foo is null then 1 else 0 end))
when 1 then null
else sum(foo)
end
from some_table
...
group by
...
That's about all you need.

SQL Create View and using it in Function

I have the following function and I need to take out the SELECT part and create a separate view.
CREATE FUNCTION dbo.dbf_get_penalty_points
( #pn_seq_party_id NUMERIC(18),
#pv_penalty_points_code CHAR(1) = 'Y') -- Use 'N' for total points, otherwise will return Current Penalty Points
RETURNS NUMERIC(18,0)
AS
BEGIN
DECLARE #n_penalty_points NUMERIC(18),
#d_latest_points_date DATETIME
SELECT #d_latest_points_date = dbo.dbf_trunc_date(DateAdd(mm, - Abs(Convert(NUMERIC(18,0),dbo.dbf_get_sys_param('CMS2', 'PP_MONTHS'))), GetDate()))
SELECT #n_penalty_points = IsNull(Sum(penalty_points_amount),0)
FROM dbo.ar_penalty_point WITH(NOLOCK)
WHERE seq_party_id = #pn_seq_party_id
AND 1 = CASE
WHEN #pv_penalty_points_code = 'N' THEN 1
WHEN #pv_penalty_points_code = 'Y' AND added_date >= #d_latest_points_date AND reset_date IS NULL THEN 1
ELSE 0
END
RETURN #n_penalty_points
END
GO
SET QUOTED_IDENTIFIER OFF
GO
GRANT EXECUTE ON dbo.dbf_get_penalty_points TO standard
GO
I have tried and got this,
SELECT SUM(CASE WHEN added_date >=dbo.dbf_trunc_date(DateAdd(mm, - Abs(Convert(NUMERIC(18,0),dbo.dbf_get_sys_param('CMS2', 'PP_MONTHS'))), GetDate()))
AND reset_date IS NULL THEN 1
ELSE 0) current_points,
IsNull(Sum(penalty_points_amount),0) total_points,
seq_party_id
FROM dbo.ar_penalty_point WITH(NOLOCK)
GROUP BY seq_party_id
Now I need to get rid of
dbo.dbf_trunc_date(DateAdd(mm, - Abs(Convert(NUMERIC(18,0),dbo.dbf_get_sys_param('CMS2', 'PP_MONTHS'))), GetDate()))
From the SELECT part of the query. I am struck is there a better way to write my view ?
EDIT
The objective is to create a view that returns total_points and current_points. For better understanding refer the CREATE part following
CREATE FUNCTION dbo.dbf_get_penalty_points
( #pn_seq_party_id NUMERIC(18),
#pv_penalty_points_code CHAR(1) = 'Y') -- Use 'N' for total points, otherwise will return Current Penalty Points
Refer to -- Use 'N' for total points, otherwise will return Current Penalty Points in the comments
This is what I came up with
SELECT SUM(CASE WHEN (t.added_date >= t.target_date
AND t.reset_date IS NULL) THEN 1
ELSE 0 END) current_points,
IsNull(Sum(t.penalty_points_amount),0) total_points,
t.seq_party_id
FROM (
SELECT dbo.dbf_trunc_date(DateAdd(mm, - Abs(Convert(NUMERIC(18,0),dbo.dbf_get_sys_param('CMS2', 'PP_MONTHS'))), GetDate())) as target_date,
u.reset_date, u.penalty_points_amount,u.seq_party_id,u.added_date FROM
dbo.ar_penalty_point as u ) as t GROUP BY t.seq_party_id

SQL server IF begin statement in where function

I'm trying to create the following in a where statement.
where Registration_Date >=#StartDate and Registration_Date < dateadd(day,1,#EndDate)
and if #affiliateid is null begin Channel in (select Value from dbo.Split(',',#Channel)) end
else Affiliate_Id in (select Value from dbo.Split(',', #AffiliateId))
and Registration_Channel in (select Value from dbo.Split(',', #Channel))
So, I am trying to say that if #AffiliateId is null, use the #Channel input for all Affiliate ids, if not then use the specific #AffiliateId, irrespective of channels.
Is there any way I could make this work?
SELECT *
FROM mytable
WHERE DATE(registration_date) BETWEEN #startDate AND #endDate
AND EXISTS
(
SELECT NULL
FROM dbo.split(',', COALESCE(#affiliateId, #channel))
WHERE value = CASE WHEN #affiliateId IS NULL THEN channel ELSE affiliateId END
)
AND EXISTS
(
SELECT NULL
FROM dbo.split(',', #channel)
WHERE value = registration_channel
)
In SQL Server 2008, DATE(column) is a sargable expression.