T SQL use case when in parameter - tsql

I'm trying to set the variable value with a case statement to determine the financial year, depending on the month but I get a Null value returned for the financial year:
declare
#Costcentre varchar(50)
,#dt date
,#dty int
,#dtm int
select #Costcentre = 'CAM'
SELECT #dt = '2012-09-30'
select #dtm = DATEPART(month,#dt)
select
#dty = case when #dtm between 4 and 12 then DATEPART(year,#dt) + 1 end
,#dty = case when #dtm between 1 and 3 then DATEPART(year,#dt) end
select #dty

You only need to assign #dty once:
select #dty = case
when #dtm between 4 and 12 then DATEPART(year,#dt) + 1
when #dtm between 1 and 3 then DATEPART(year,#dt)
end
Otherwise, you're just overwriting #dty if #dtm isn't between 1 and 3.

Related

DB2 - use field as a labeled duration for time calculation

Given a table that looks like this:
id task scheduled_date reminder
-- ----------------------- ---------------- --------
1 mail january newsletter 2022-01-01 15 days
I had planned on executing a query to mimic date addition as in
SELECT TASK, SCHEDULED_DATE + 15 DAYS FROM ...
==> 2022-01-16
Unfortunately, using the REMINDER field gives an error:
SELECT TASK, (SCHEDULED_DATE + REMINDER) FROM ...
==>[Code: -182, SQL State: 42816] [SQL0182] A date, time, or timestamp expression not valid.
Is there any way to accomplish using the reminder field as a labeled duration? (I'm using IBMi DB2)
You'll need to convert the string "15 days" into an actual duration.
A date durration is a decimal(8,0) number representing YYYYMMDD
So 15 days would be 00000015
1 year, 00010000
1 year 1 month, one day '00010101`
create table testdur (
datedur decimal(8,0)
);
insert into testdur
values (15), (10000), (10101), (90), (300);
select current_date as curDate
, dateDur
,current_date + dateDur
from testdur;
Results
There is an attempt to implement the interval function available in Db2 for LUW. It supports string expression as a parameter, not just string constant as the built-in one.
The result of this function can participate in whatever allowed date arithmetic.
This works on Db2 for LUW v11.1+ and Db2 for IBM i v7.5+ at least.
create or replace function interval_d (p_interval varchar (100))
returns dec (8)
contains sql
deterministic
no external action
begin atomic
declare v_sign dec (1) default 0;
declare v_pattern varchar (100) default '([+-]? *[0-9]+) *(\w+)';
declare v_y int default 0;
declare v_m int default 0;
declare v_d int default 0;
declare v_occ int default 1;
declare v_num int;
declare v_kind varchar (10);
l1: while 1=1 DO
set v_kind =
lower
(
regexp_substr
(
p_interval
, v_pattern
, 1, v_occ, '', 2
)
);
if v_kind is null then leave l1; end if;
set v_num =
int
(
replace
(
regexp_substr
(
p_interval
, v_pattern
, 1, v_occ, '', 1
)
, ' ', ''
)
);
if sign (v_num) * v_sign < 0 then
signal sqlstate '75001' set message_text = 'Sign of all operands must be the same';
end if;
if v_sign = 0 then set v_sign = sign (v_num); end if;
if v_kind in ('d', 'day', 'days')
then set v_d = v_d + v_num;
elseif v_kind in ('mon', 'mons', 'month', 'months')
then set v_m = v_m + v_num;
elseif v_kind in ('y', 'year', 'years')
then set v_y = v_y + v_num;
else
signal sqlstate '75000' set message_text = 'wrong duration';
end if;
set v_occ = v_occ + 1;
end while l1;
if abs (v_d) > 99 then
set v_m = v_m + v_d / 30, v_d = mod (v_d, 30);
end if;
if abs (v_m) > 99 then
set v_y = v_y + v_m / 12, v_m = mod (v_m, 12);
end if;
return v_y * 10000 + v_m * 100 + v_d;
end
select interval_d (i) as d
from
(
values
('4 years 2 months 3 days')
, ('3 day 4 year 2 month')
, ('-4y -2mon -3d')
) t (i)
D
40203
40203
-40203
fiddle

T-SQL: Aggregate based on condition

there is a table:
start | end | zone
------|-----|-----
1 | 5 | 3
3 | 6 | 2
1 | 3 | 1
4 | 7 | 4
Start and end represent a range. I'd like to get a zone for value of 4, like
declare #value as int = 4
select zone from table where #value >= start and #value <= end
, and I obtain
3
2
4
, but I need only one zone from predefined range, for example 2 or 3 and 3 has priority, so if 2 and 3 are present, I'd like to get 3. So, zone 3 is desired result in my example.
How can I solve it?
If it's hard coded you can do something like this:
DECLARE #Value int = 4;
SELECT TOP 1 zone
FROM table
WHERE zone IN (3,2)
AND #Value >= start
AND #Value <= [end]
ORDER BY CASE WHEN zone = 3 THEN 0 ELSE 1 END
See a live demo on rextester
Update
SELECT TOP 1 CASE WHEN zone IN (3,2) THEN zone ELSE NULL END AS zone
FROM #T
WHERE #Value >= start
AND #Value <= [end]
ORDER BY CASE WHEN zone = 3 THEN 0 ELSE 1 END
Live demo for updated version
Update 2
Even though you wrote in your comment that you found a solution thanks to my post, I still want to post a solution here for future readers that might need it:
;WITH CTE AS
(
SELECT TOP 1 zone
FROM #T
WHERE zone IN (3,2)
AND #Value >= start
AND #Value <= [end]
ORDER BY CASE WHEN zone = 3 THEN 0 ELSE 1 END
)
SELECT zone
FROM CTE
UNION ALL
SELECT NULL
WHERE NOT EXISTS(
SELECT 1
FROM CTE
)

PostgreSQL: using where clause to change select value

I'm wanting to change a select value depending on the where clause. Is something like the following possible in any way?
SELECT b
FROM table
WHERE
IF date = '2016-03-24'
b = 1
ELSE IF date > date '2016-03-24' - 7 AND date < '2016-03-24'
b = 2
ELSE
b = 1
ORDER BY date
LIMIT 1
Use CASE WHEN:
SELECT CASE WHEN date = '2016-03-24' THEN 1
WHEN date > '2016-03-17' AND date < '2016-03-24' THEN 2
ELSE 1
END AS b
FROM table
ORDER BY b
LIMIT 1

Find total number in a specific period of time SQL

I am trying to find the total number of members in a given period. Say I have the following data:
member_id start_date end_date
1 9/1/2013 12/31/2013
2 10/1/2013 11/12/2013
3 12/1/2013 12/31/2013
4 5/1/2012 8/5/2013
5 9/1/2013 12/31/2013
6 7/1/2013 12/31/2013
7 6/6/2012 12/5/2013
8 10/1/2013 12/31/2013
9 7/8/2013 12/31/2013
10 1/1/2012 11/5/2013
In SQL I need to create a report that will list out the number of members in each month of the year. In this case something like the following:
Date Members Per Month
Jan-12 1
Feb-12 1
Mar-12 1
Apr-12 1
May-12 2
Jun-12 3
Jul-12 3
Aug-12 3
Sep-12 3
Oct-12 3
Nov-12 3
Dec-12 3
Jan-13 3
Feb-13 3
Mar-13 3
Apr-13 3
May-13 3
Jun-13 3
Jul-13 5
Aug-13 4
Sep-13 6
Oct-13 8
Nov-13 6
Dec-13 6
So there is only 1 member from Jan-12 (member id 10) until May-12 when member id 4 joins making the count 2 and so on.
The date range can be all over so I can't specify the specific dates but it is by month, meaning that even if someone ends 12-1 it is considered active for the month for Dec.
I was able to create the following stored procedure that was able to accomplish what I needed:
USE [ValueBasedSandbox]
GO
/****** Object: StoredProcedure [dbo].[sp_member_count_per_month] Script Date: 01/08/2015 12:02:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Create date: 2015-08-01
-- Description: Find the counts per a given date passed in
-- =============================================
CREATE PROCEDURE [dbo].[sp_member_count_per_month]
-- Add the parameters for the stored procedure here
#YEAR int
, #ENDYEAR int
AS
DECLARE #FIRSTDAYMONTH DATETIME
DECLARE #LASTDAYMONTH DATETIME
DECLARE #MONTH INT = 1;
--Drop the temporary holding table if exists
IF OBJECT_ID('tempdb.dbo.##TEMPCOUNTERTABLE', 'U') IS NOT NULL
DROP TABLE dbo.##TEMPCOUNTERTABLE
CREATE TABLE dbo.##TEMPCOUNTERTABLE (
counter INT
, start_date DATETIME2
, end_date DATETIME2
)
--Perform this loop for each year desired
WHILE #YEAR <= #ENDYEAR
BEGIN
--Perform for each month of the year
WHILE (#MONTH <= 12)
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET #FIRSTDAYMONTH = DATEADD(MONTH, #MONTH - 1, DATEADD(YEAR, #YEAR-1900, 0))
SET #LASTDAYMONTH = DATEADD(MONTH, #MONTH, DATEADD(YEAR, #YEAR-1900, 0)-1)
INSERT INTO dbo.##TEMPCOUNTERTABLE(counter, start_date, end_date)
SELECT COUNT(*) AS counter
, #FIRSTDAYMONTH AS start_date
, #LASTDAYMONTH AS end_date
FROM dbo.member_table
WHERE start_date <= #LASTDAYMONTH
AND end_date >= #FIRSTDAYMONTH
--Increment through all the months of the year
SET #MONTH = #MONTH + 1
END -- End Monthly Loop
--Reset Month counter
SET #MONTH = 1
--Increment the desired years
SET #YEAR = #YEAR + 1
END -- End Yearly Loop
--Display the results
SELECT *
FROM dbo.##TEMPCOUNTERTABLE
-- Drop the temp table
IF OBJECT_ID('tempdb.dbo.##TEMPCOUNTERTABLE', 'U') IS NOT NULL
DROP TABLE dbo.##TEMPCOUNTERTABLE
GO
This should do the trick
with datesCte(monthStart,monthEnd) as
(
select cast('20120101' as date) as monthStart, cast('20120131' as date) as monthEnd
union all
select DATEADD(MONTH, 1, d.monthStart), dateadd(day, -1, dateadd(month, 1, d.monthStart))
from datesCte as d
where d.monthStart < '20140101'
)
select *
from datesCte as d
cross apply
(
select count(*) as cnt
from dbo.MemberDates as m
where m.startDate <= d.monthEnd and m.endDate > d.monthStart
) as x
order by d.monthStart

Adding a Date into a Table TSQL using Weighting

I have a Site Table where each site has a Rating between 1 and 4, I also have a capacity table where I want to add a start date using the rating from the site table, for example
A site with a rating of 1 the Start Date should be 01/05/2010 and a End Date 30/09/2010
A site with a rating of 2 and 3 the Start Date should be 01/04/2010 and a End Date 01/11/2010
A site with a rating of 4 the Start Date should be 01/01/2010 and a End Date 31/12/2010
I have had help with the first two columns already see code below, I now need to add the start date and end date. Can you help?
USE OCCUPANCY
CREATE TABLE Cap1
(PitchType_Skey int,
Site_Skey int)
DECLARE #PitchType_Skey INT
DECLARE #Site_Skey INT
SET #PitchType_Skey = 1
SET #Site_Skey = 1
WHILE (#Site_Skey < 127)
BEGIN
IF #PitchType_Skey = 8
BEGIN
SET #PitchType_Skey = 1
SET #Site_Skey = #Site_Skey + 1
END
IF (#Site_Skey < 127)
BEGIN
INSERT INTO dbo.Cap1 (PitchType_Skey, Site_Skey)
SELECT #PitchType_Skey, #Site_Skey
END
SET #PitchType_Skey = #PitchType_Skey + 1
END
In one command and using ISO dates
INSERT INTO dbo.Cap1 (PitchType_Skey, Site_Skey, StartDate, EndDate)
SELECT
ROW_NUMBER() OVER (ORDER BY Site_Skey),
Site_Skey /*from site table column*/,
CASE rating
WHEN 1 THEN '20100501'
WHEN 4 THEN '20100101'
ELSE '20100401'
END,
CASE rating
WHEN 1 THEN '20100930'
WHEN 4 THEN '20101231'
ELSE '20101101'
END
FROM Site