How to derive new columns in YYYYMM format between two dates - sql-server

I have following table
ID
Name
StartDate
EndDate
1
Aa
2021-10-14
2021-12-22
2
Ab
2021-12-02
2022-10-05
The requirement is to add new columns in YYYYMM format consisting of all the months between min(StartDate) and max(EndDate), and assign values to the corresponding cells. The cell value should be 1 if the date lies between StartDate and EndDate in that row, and should be 0 if it does not fall within that date range.The final output should be like in the below table
ID
Name
StartDate
EndDate
202110
202111
202112
202201
202202
202203
202204
202205
202206
202207
1
Aa
2021-10-14
2021-12-22
1
1
1
0
0
0
0
0
0
0
2
Ab
2021-12-02
2022-07-05
0
0
1
1
1
1
1
1
1
1
For the 1st row, since the startdate=2021-10-14 & EndDate=2021-12-22, corresponding new columns should be 202110,202111 & 202112; and corresponding cell values are 1 for these columns while the other cells are 0. Same logic should be applied to other rows as well.
I could not figure out the logic to derive new tables with new columns and corresponding cell values.

How about this approach using dynamic SQL:
CREATE TABLE Data ( ID INT, Name VARCHAR(100), StartDate DATE, EndDate DATE )
INSERT Data
VALUES
('1', 'Aa', '2021-10-14', '2021-12-22'),
('2', 'Ab', '2021-12-02', '2022-10-05')
-- Extract overall date range
DECLARE #MinStartDate DATE, #MaxEndDate AS DATE
SELECT #MinStartDate = MIN(StartDate), #MaxEndDate = MAX(EndDate)
FROM Data
-- Convert to months (DATETRUNC() may be used instead with SQL Server 2022 and later)
DECLARE #StartMonth DATE = DATEADD(day, 1 - DAY(#MinStartDate), #MinStartDate)
DECLARE #EndMonth DATE = DATEADD(day, 1 - DAY(#MaxEndDate), #MaxEndDate)
-- Generate calendar of months within range
DECLARE #Months TABLE ( Month DATE)
;WITH Months AS (
SELECT #StartMonth AS Month
UNION ALL
SELECT DATEADD(month, 1, M.Month)
FROM Months M
WHERE M.Month < #EndMonth
)
INSERT #Months
SELECT M.Month
FROM Months M
-- Define SQL Templates
DECLARE #SqlTemplate VARCHAR(MAX) = '
SELECT ID, Name, StartDate, EndDate
<ColumnSql>
FROM Data D
ORDER BY D.Name
'
DECLARE #ColumnTemplate VARCHAR(MAX) = '
, CASE WHEN D.StartDate <= <MonthEnd> AND <MonthStart> <= D.EndDate THEN 1 ELSE 0 END AS <ColumnName>'
-- Build month-specific column select items from template
DECLARE #ColumnSql VARCHAR(MAX) = (
SELECT STRING_AGG(C.ColumnSql, '') WITHIN GROUP(ORDER BY M.Month)
FROM #Months M
CROSS APPLY (
SELECT
CONVERT(CHAR(6), M.Month, 112) AS ColumnName,
M.Month AS MonthStart,
EOMONTH(M.Month) AS MonthEnd
) MD
CROSS APPLY (
SELECT REPLACE(REPLACE(REPLACE(
#ColumnTemplate
, '<ColumnName>', QUOTENAME(MD.ColumnName))
, '<MonthStart>', QUOTENAME(CONVERT(CHAR(8), MD.MonthStart, 112), ''''))
, '<MonthEnd>', QUOTENAME(CONVERT(CHAR(8), MD.MonthEnd, 112), ''''))
AS ColumnSql
) C
)
--SELECT #ColumnSql
-- Build final SQL
DECLARE #Sql VARCHAR(MAX) = REPLACE(#SqlTemplate, '<ColumnSql>', #ColumnSql)
SELECT #Sql
-- Deliver
EXEC (#Sql)
Generated SQL:
SELECT ID, Name, StartDate, EndDate
    , CASE WHEN D.StartDate <= '20211031' AND D.EndDate >= '20211001' THEN 1 ELSE 0 END AS [202110]
    , CASE WHEN D.StartDate <= '20211130' AND D.EndDate >= '20211101' THEN 1 ELSE 0 END AS [202111]
    , CASE WHEN D.StartDate <= '20211231' AND D.EndDate >= '20211201' THEN 1 ELSE 0 END AS [202112]
    , CASE WHEN D.StartDate <= '20220131' AND D.EndDate >= '20220101' THEN 1 ELSE 0 END AS [202201]
    , CASE WHEN D.StartDate <= '20220228' AND D.EndDate >= '20220201' THEN 1 ELSE 0 END AS [202202]
    , CASE WHEN D.StartDate <= '20220331' AND D.EndDate >= '20220301' THEN 1 ELSE 0 END AS [202203]
    , CASE WHEN D.StartDate <= '20220430' AND D.EndDate >= '20220401' THEN 1 ELSE 0 END AS [202204]
    , CASE WHEN D.StartDate <= '20220531' AND D.EndDate >= '20220501' THEN 1 ELSE 0 END AS [202205]
    , CASE WHEN D.StartDate <= '20220630' AND D.EndDate >= '20220601' THEN 1 ELSE 0 END AS [202206]
    , CASE WHEN D.StartDate <= '20220731' AND D.EndDate >= '20220701' THEN 1 ELSE 0 END AS [202207]
    , CASE WHEN D.StartDate <= '20220831' AND D.EndDate >= '20220801' THEN 1 ELSE 0 END AS [202208]
    , CASE WHEN D.StartDate <= '20220930' AND D.EndDate >= '20220901' THEN 1 ELSE 0 END AS [202209]
    , CASE WHEN D.StartDate <= '20221031' AND D.EndDate >= '20221001' THEN 1 ELSE 0 END AS [202210]
FROM Data D
ORDER BY D.Name
Results:
ID
Name
StartDate
EndDate
202110
202111
202112
202201
202202
202203
202204
202205
202206
202207
202208
202209
202210
1
Aa
2021-10-14
2021-12-22
1
1
1
0
0
0
0
0
0
0
0
0
0
2
Ab
2021-12-02
2022-10-05
0
0
1
1
1
1
1
1
1
1
1
1
1
See this db<>fiddle.
The above uses a standard test for date range overlap of "start1 <= end2 AND start2 <= end1", which assumes all dates are inclusive.

Related

How can separate morning and evening shifts patients with gender wise using SQL stored procedure

I want to separate the morning and evening patient checked-in count gender-wise and also if gender age less then show the Female child and M child on the basis of gender.
SELECT
COUNT(ch.EnteredOn) AS counter,
CONVERT(date, ch.EnteredOn) AS sessionDay,
SUM(CASE WHEN CAST(CONVERT(CHAR(2), ch.EnteredOn, 108) AS INT) < 12 THEN 1 ELSE 0 END) AS 'Morning',
SUM(CASE WHEN CAST(CONVERT(CHAR(2), ch.EnteredOn, 108) AS INT) >= 12 THEN 1 ELSE 0 END) AS 'Evening',
SUM(CASE WHEN p.Gender = 1 THEN 1 ELSE 0 END) AS Male,
SUM(CASE WHEN p.Gender = 2 THEN 1 ELSE 0 END) AS Fmale,
SUM(CASE WHEN DATEDIFF(hour, P.DOB, GETDATE()) / 8766 <= 18
AND P.Gender = 1 THEN 1 ELSE 0 END) AS MChild,
SUM(CASE WHEN DATEDIFF(hour, P.DOB, GETDATE()) / 8766 <= 18
AND P.Gender = 2 THEN 1 ELSE 0 END) AS FChild
FROM
Patient.CheckIn ch
INNER JOIN
Patient.Patients P ON ch.PatientId = p.PatientId
GROUP BY
Ch.EnteredOn
I have only three columns like Gender, Time and DOB. Gender id 1 shows the male and Gender id 2 is using for females.
enter image description here
SELECT convert(date, ch.EnteredOn) AS sessionDay, CAST( CONVERT(CHAR(2), ch.EnteredOn, 108) AS INT) <12 as morning, count(ch.EnteredOn) AS counter
,sum(case when p.Gender=1 Then 1 ELSE 0 end) as Male
,sum(case when p.Gender=2 Then 1 ELSE 0 end) as Fmale
,Sum( case when DATEDIFF(hour,P.DOB,GETDATE())/8766<=18 AND P.Gender=1 Then 1 ELSE 0 end ) as MChiled
,Sum( case when DATEDIFF(hour,P.DOB,GETDATE())/8766<=18 AND P.Gender=2 Then 1 ELSE 0 end ) as FChiled
FROM Patient.CheckIn ch
inner join Patient.Patients P on ch.PatientId=p.PatientId
Group by Ch.EnteredOn
Group BY can be
Group by convert(date, ch.EnteredOn) AS sessionDay, CAST( CONVERT(CHAR(2), ch.EnteredOn, 108) AS INT) <12

previous day's count except on Monday, use Friday's counts

I have the below code. I am comparing counts from today's date to yesterday's date unless date falls on Monday then I want to use Friday's count.
select
cast(getdate() as date)run_dt,
'dx' as db,
'dbo' as [schema],
'tb_cust' as [table],
'completed status' as check_type,
a.curr_cnt,
a.prev_cnt,
abs(cast((a.prev_cnt - a.curr_cnt) / cast(a.curr_cnt as decimal(10,2)) as decimal(5,2))*100) as cal_prcnt_dff,
case
when abs(cast((a.prev_cnt - a.curr_cnt) / cast(a.curr_cnt as decimal(10,2)) * 100 as int) ) >= 100
then 'Y'
else 'N'
end email_trigger,
concat('count on',' ', #curr_dt,' ','[',datename(dw,#curr_dt),']',' ' ,'increased/decreased by 100% from',' ',#prev_dt,' ','[',datename(dw,#prev_dt),']') as 'comment'
from (select
sum(
case
when a.custstatus = 'completed'
and a.custdate = cast(getdate() -1 as date)
then 1
else 0
end) curr_cnt,
sum(
case
when a.custstatus = 'completed'
and a.custtdate = cast(getdate() -2 as date)
--and cast(getdate() as date) in ()
--and datepart(dw,getdate()) in (3,4,5,6)
--or
then 1
else 0
end) prev_cnt,
a.custstatus
from cte_apt a
where 1=1
and a.custstatus = 'completed'
group by
a.custstatus)a
How can I get prev_cnt to count Friday when the day lands on a Monday?
sum(
case
when a.custstatus = 'completed'
and a.custtdate = cast(getdate() -2 as date)
--and cast(getdate() as date) in ()
--and datepart(dw,getdate()) in (3,4,5,6)
--or
then 1
else 0
end) prev_cnt,

Extract the percent days from a specific month

I have a table like this one - changes table:
Table 1:
id start_date end_date s_g r_c s_c
111 1/1/15 25/5/2015 A1 1 0
111 26/05/2015 31/12/9999 Z1 1 2
222 1/1/14 10/2/2015 Q1 1 0
222 11/2/2015 31/12/9999 R1 1 0
And I need to build an montly output for 2015 like this:
id month s_g r_c _s_c percent
111 1 A1 1 0 100%
111 2 A1 1 0 100%
111 3 A1 1 0 100%
111 4 A1 1 0 100%
111 5 A1 1 0 83.33%
111 5 Z1 1 2 16.67%
111 6 Z1 1 2 100%
111 7 Z1 1 2 100%
111 8 Z1 1 2 100%
222 1 Q1 1 0 100%
222 2 Q1 1 0 35.71%
222 2 R1 1 0 64.29%
222 3 R1 1 0 100%
222 4 R1 1 0 100%
222 5 R1 1 0 100%
222 6 R1 1 0 100%
222 7 R1 1 0 100%
222 8 R1 1 0 100%
I need this month only for year 2015.
Any idea how can I build this thing? just assum that those are the only case.
You could try this.
DECLARE #Month_2015 TABLE
(
Date1 Date,
Date2 Date
)
INSERT INTO #Month_2015
select '2015-01-01','2015-01-31'
union all
select '2015-02-01','2015-02-28'
union all
select '2015-03-01','2015-03-31'
union all
select '2015-04-01','2015-04-30'
union all
select '2015-05-01','2015-05-31'
union all
select '2015-06-01','2015-06-30'
...
With CTE as
(
Select ID, date1, s_g, r_c, s_c,
le = case when start_date < Date2 AND End_date > DATE1 THEN Datediff(d,
(case when start_date < date1 THEN date1 else start_date end),
(case when end_date < date2 THEN end_date else date2 end)
Else 0
end,
m = DATEDIFF (d, date1, date2)
from
Table1, #Month_2015
)
Select ID, Month(date1), s_g, r_c, s_c, (le*100.0 / m)
from CTE
Where le > 0
order by ID, Month(date1), s_g, r_c, s_c
Try this. It worked for me based on your table ##
DECLARE #TableRezult TABLE (id INT,month INT,s_g varchar(2),r_c INT,s_c INT, MonthPercentage varchar(25))
DECLARE #month INT=1
WHILE #month<=12
BEGIN
INSERT INTO #TableRezult
SELECT id,#month,s_g,r_c,s_c,
CASE WHEN (end_date >= dateadd(m,1,convert(varchar,#month)+'/1/2015') OR end_date='12/31/2015')
AND ([START_DATE]<=convert(varchar,#month)+'/1/2015') THEN '100%' ELSE
CASE WHEN end_date >= convert(varchar,#month)+'/1/2015' AND end_date < dateadd(m,1,convert(varchar,#month)+'/1/2015') THEN convert(VARCHAR ,Day(end_date)*100.0/DAY(EOMONTH(start_date)))+'%'
WHEN start_date >= convert(varchar,#month)+'/1/2015' AND start_date < dateadd(m,1,convert(varchar,#month)+'/1/2015') THEN convert(varchar,(DAY(EOMONTH(start_date))-Day(start_date))*100.0/DAY(EOMONTH(start_date)))+'%'
ELSE '0%' END
END FROM Table1 WHERE MONTH([START_DATE])<=#month AND MONTH(end_date)>=#month
SET #month=#month+1
END
SELECT * FROM #TableRezult ORDER BY id,[MONTH]
since it is not fully explain,i am posting half solution,after clearing doubts calculation can be corrected.
declare #Table1 table (id int,start_date datetime,end_date datetime
, s_g varchar(20), r_c int, s_c int )
insert into #Table1 values
(111 ,'2015-01-01','2015-5-25','A1', 1, 0 )
,(111 ,'2015-05-26','9999-12-31','Z1', 1, 2 )
,(222 ,'2014-01-01','2015-02-10','Q1', 1, 0 )
,(222 ,'2015-02-11','9999-12-31','R1', 1, 0 )
DECLARE #year INT = 2015
;WITH CTE
AS (
SELECT id
,s_g
,r_c
,s_c
,CASE
WHEN datepart(year, start_date) = #year
THEN start_date
WHEN datepart(year, start_date) < #year
THEN CAST(#year AS CHAR(4)) + '-' + '01' + '-' + '01'
WHEN datepart(year, start_date) > #year
THEN CAST(#year AS CHAR(4)) + '-' + '12' + '-' + '31'
END startdate
,CASE
WHEN datepart(year, end_date) = #year
THEN end_date
WHEN datepart(year, end_date) < #year
THEN CAST(#year AS CHAR(4)) + '-' + '01' + '-' + '01'
WHEN datepart(year, end_date) > #year
THEN CAST(#year AS CHAR(4)) + '-' + '12' + '-' + '31'
END enddate
FROM #table1
)
,CTE1
AS (
SELECT A.id
,A.s_g
,A.r_c
,A.s_c
,A.startdate
,A.enddate
,DATEPART(month, A.startdate) MonthList
FROM CTE A
UNION ALL
SELECT A.id
,A.s_g
,A.r_c
,A.s_c
,dateadd(month, 1, A.startdate)
,A.enddate
,a.MonthList + 1
FROM CTE1 A
WHERE A.MonthList < DATEPART(month, a.enddate)
)
--select * from cte1
SELECT *
,CASE
WHEN datediff(month, startdate, enddate) >= 1
THEN 100
ELSE (datepart(day, enddate) / cast(datepart(day, dateadd(day, - 1, dateadd(month, (datediff(month, 0, enddate) + 1), 0))) AS DECIMAL(4, 2))) * 100
END
FROM cte1
ORDER BY id
,s_g
,monthlist

How to make Query for that

I have a table structure from there i have to make a query in some different way
Table Structure
id unique identifier
code varchar(5) Not Null
Recording_date Datetime Not Null
Max_Temp numeric(5,2) Not Null
Min_Temp numeric(5,2) Not Null
We have some data as well in this table.We have data only for 2013 year and for first 3 months.
But the main thing is that i have to return's data in such a format like
Please help me to create a query for such a logic.
Thanks in advance.
Presuming you have one recording per day then
SELECT
DATEPART(m, Month_Start) + ' ' + DATEPART(yyyy, Month_Start)
, Max_Temp_Days
, CASE
WHEN Denominator = 0 THEN 0
ELSE (Max_Temp_Days / Denominator) * 100
END AS Percent_Max_Temp_Days
, Min_Temp_Days
, CASE
WHEN Denominator = 0 THEN 0
ELSE (Min_Temp_Days / Denominator) * 100
END AS Percent_Max_Temp_Days
FROM (
SELECT
DATEADD(MONTH, DATEDIFF(MONTH, 0, Recording_Date), 0) Month_Start
, Sum(CASE WHEN Max_Temp <= 0 THEN 1 END) Max_Temp_Days
, Sum(CASE WHEN Min_Temp <= 0 THEN 1 END) Min_Temp_Days
, COUNT(*) Denominator
FROM TemperatureRecordings
WHERE Recording_Date BETWEEN '2013-01-01' AND '2013-03-31'
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Recording_Date), 0)
) t
ORDER BY Month_Start
This works for all month data
DECLARE #maxTempratureTable table(monthName varchar(20), [No. of days Max<=0] int,[Percentage Max <=0] float)
INSERT INTO maxTempratureTable
SELECT RIGHT(CONVERT(VARCHAR, Recording_date, 106), 8) ,
COUNT(*) ,
COUNT(*) / DAY(DATEADD(mm,DATEDIFF(mm,-1,Recording_date),-1)) * 100
FROM tablename
WHERE Max_Temp <=0
GROUP BY RIGHT(CONVERT(VARCHAR, Recording_date, 106), 8)
DECLARE #minTempratureTable table(monthName varchar(20), [No. of days Min<=0] int,[Percentage Min<=0] float)
INSERT INTO #minTempratureTable
SELECT RIGHT(CONVERT(VARCHAR, Recording_date, 106), 8) ,
COUNT(*) ,
COUNT(*) / DAY(DATEADD(mm,DATEDIFF(mm,-1,Recording_date),-1)) * 100
FROM tablename
WHERE Min_Temp <=0
GROUP BY RIGHT(CONVERT(VARCHAR, Recording_date, 106), 8)
SELECT * FROM #minTempratureTable min
INNER JOIN #maxTempratureTable max
ON min.monthName = max.monthName
SELECT
MONTH(Recording_date),
SUM(CASE WHEN Max_Temp <= 0 THEN 1 ELSE 0 END),
SUM(CASE WHEN Max_Temp <= 0 THEN 1 ELSE 0 END) / COUNT(*),
SUM( CASE WHEN Min_Temp <= 0 THEN 1 ELSE 0 END ),
SUM( CASE WHEN Min_Temp <= 0 THEN 1 ELSE 0 END ) / COUNT(*)
FROM temperatures
GROUP BY MONTH(Recording_date)

Count of rows for 2 days

My data is in below format:
employee order id date
a 123 01/06/2013
b 124 02/06/2013
a 125 02/06/2013
a 129 02/06/2013
I need the data in below format:
employee day 1 day 2
a 1 2
b 0 1
Try this one -
DECLARE #temp TABLE
(
dtStart DATETIME
, employees CHAR(1)
)
INSERT INTO #temp (employees, dtStart) VALUES('a','01/06/2013')
INSERT INTO #temp (employees, dtStart) VALUES('a','01/06/2013')
INSERT INTO #temp (employees, dtStart) VALUES('b','02/06/2013')
SELECT
employees
, day1 = COUNT(CASE WHEN DAY(dtStart) = 1 THEN 1 END)
, day2 = COUNT(CASE WHEN DAY(dtStart) = 2 THEN 1 END)
FROM #temp
--WHERE dtStart BETWEEN '01/06/2013' AND '30/06/2013'
GROUP BY employees
Something along these lines should work (depending on whether you have a fixed amount of days or not):
select employee,
SUM(CASE WHEN date = '01/06/2013' THEN 1 ELSE 0 END) as day1,
SUM(CASE WHEN date = '02/06/2013' THEN 1 ELSE 0 END) as day2
from table
group by employee
select distinct employees,
SUM(CASE WHEN dtStart = '01/06/2013' THEN 1 ELSE 0 END) as day1,
SUM(CASE WHEN dtStart = '02/06/2013' THEN 1 ELSE 0 END) as day2
from yourTable
group by dtStart,employees
see your demo

Resources