T-SQL - adding more date values to a table all rows - sql

I have a table that contains many IDs as random numbers and I would like to create another table that contains all the IDs with another column having dates from first day of the year until a specific date.
For example: My actual table looks like:
ID
101
431
566
And I would like to have a table if my specific end date is 2020-01-03 that looks like:
ID Date
101 2020-01-01
101 2020-01-02
101 2020-01-03
431 2020-01-01
431 2020-01-02
431 2020-01-03
566 2020-01-01
566 2020-01-02
566 2020-01-03
Could you help me to solve my issue? Thanks in advance!

You can use a recursive CTE to define the dates and then cross join:
with dates as (
select convert(date, '2020-01-01') as dte
union all
select dateadd(day, 1, dte)
from dates
where dte < #enddate
)
select t.id, dates.date
from t cross join
dates
option (maxrecursion 0);

Related

PostgreSQL: how do build a table with an ever-increasing daily total?

Assuming this data:
ID DATE
44 2019-12-31
45 2020-01-01
46 2020-01-01
47 2020-01-02
48 2020-01-03
48 2020-01-03
48 2020-01-03
How do I make a query that returns something like this?
TOTAL DATE
2 2020-01-01
3 2020-01-02
6 2020-01-03
I want all entries after a certain data, but with a counter that starts with the number of entries on the first day, then for every day, it adds the number of entries on that day. I want to plot them on a chart that shows the speed of the growth.
Is this possible? I'm on PostgreSQL 10.
Thanks!
You can use aggregation and window functions:
select date,
count(*) as count_on_date,
sum(count(*)) over (order by min(date)) as running_count
from t
where date >= '2020-01-01'
group by date
order by date;
If you wanted a count going back in time, then you would use:
select greatest(date, '2020-01-01'::date) as date
count(*) as count_on_date,
sum(count(*)) over (order by min(date)) as running_count
from t
group by greatest(date, '2020-01-01'::date)
order by greatest(date, '2020-01-01'::date);

T-SQL Override special rates and generate final date range

I have transaction table which has date range and basic rate for the range. I have another table for special rate which has date range for special rate and its rate. I would like to split my original transaction in multiple records if special rates falls in transaction date range.
Just for simplicity I have created two tables with limited columns
DECLARE #ClientTrx AS TABLE (ClientId int, StartDate Date, EndDate Date, Rate decimal(10,2))
DECLARE #SpecialRate AS TABLE (ClientId int, StartDate Date, EndDate Date, Rate decimal(10,2))
insert into #ClientTrx select 1, '1/1/2020', '1/15/2020', 10
insert into #ClientTrx select 1, '1/16/2020', '1/31/2020', 10
insert into #ClientTrx select 2, '1/1/2020', '1/15/2020', 20
insert into #ClientTrx select 2, '1/16/2020', '1/31/2020', 20
insert into #ClientTrx select 2, '2/1/2020', '2/13/2020', 20
insert into #SpecialRate select 1, '12/25/2019', '1/3/2020', 13
insert into #SpecialRate select 1, '1/4/2020', '1/6/2020', 15
insert into #SpecialRate select 1, '1/11/2020', '1/18/2020', 12
insert into #SpecialRate select 2, '1/25/2020', '1/31/2020', 23
insert into #SpecialRate select 2, '2/4/2020', '2/8/2020', 25
insert into #SpecialRate select 2, '2/11/2020', '2/29/2020', 22
I need help write a query which produce following results:
ClientId StartDate EndDate Rate
1 2020-01-01 2020-01-03 13.00 special rate
1 2020-01-04 2020-01-06 15.00 special rate
1 2020-01-07 2020-01-10 10.00 regular rate
1 2020-01-11 2020-01-15 12.00 special rate
1 2020-01-16 2020-01-18 12.00 special rate splitting pay period
1 2020-01-19 2020-01-31 10.00 regular rate
2 2020-01-01 2020-01-15 20.00 regular rate
2 2020-01-16 2020-01-24 20.00 regular rate
2 2020-01-25 2020-01-31 23.00 special rate
2 2020-02-01 2020-02-03 20.00 regular rate
2 2020-02-04 2020-02-08 25.00 special rate
2 2020-02-09 2020-02-10 20.00 regular rate
2 2020-02-11 2020-02-13 22.00 special rate
I think using CTE its possible but I can't figure it out. can anyone please help?
Note: I have made some changes in my input and expected output, i think I need one more group level, can you please help?
This is an approach which uses and ad-hoc tally table to expand the datasets and then applies a Gaps-and-Islands for the final summary
Example
;with cte as (
Select A.ClientId
,D
,Rate = coalesce(NewRate,A.Rate)
,Grp = datediff(day,'1900-01-01',D) - row_number() over (partition by ClientID,coalesce(NewRate,A.Rate) Order by D)
From #ClientTrx A
Cross Apply (
Select Top (DateDiff(DAY,StartDate,EndDate)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),StartDate)
From master..spt_values n1,master..spt_values n2
) B
Outer Apply (
Select NewRate=Rate
From #SpecialRate
Where D between StartDate and EndDate
and ClientId=A.ClientID
) C
)
Select ClientID
,StartDate= min(D)
,EndDate = max(D)
,Rate = Rate
From cte
Group By ClientID,Grp,Rate
Order by ClientID,min(D)
Returns
ClientID StartDate EndDate Rate
1 2020-01-01 2020-01-03 13.00
1 2020-01-04 2020-01-06 15.00
1 2020-01-07 2020-01-10 10.00
1 2020-01-11 2020-01-18 12.00
1 2020-01-19 2020-01-31 10.00
2 2020-01-01 2020-01-24 20.00
2 2020-01-25 2020-01-31 23.00
2 2020-02-01 2020-02-03 20.00
2 2020-02-04 2020-02-08 25.00
2 2020-02-09 2020-02-10 20.00
2 2020-02-11 2020-02-15 22.00
Notes:
Cross Apply B generates a record for each date between startDate and endDate in #ClientTrx.
Outer Apply C attempts to find the Exception or NewRate
the CTE generates one record per date and toggles the default or exception rate. It looks like this
Notice how GRP changes. This is a simple technique to "feed" the Gaps-and-Islands
Then is becomes a small matter to group the results from cte by ClientID and Grp

Find missing record between date range

At the end of an enormous stored procedure (in SQL Server), I've created two CTE. One with some date ranges (with 6 month intervals) and one with some records.
Let's assume i have date ranges on table B from 2020-01-01 to 2010-01-01 (with 6 months intervals)
Start End
----------------------
2020-01-01 | 2020-07-01
... ...
other years here
... ...
2010-01-01 | 2010-07-01
and on table A this situation:
Name Date
-----------------
John 2020-01-01
John 2019-01-01
John 2018-07-01
... ...
Rob 2020-01-01
Rob 2019-07-01
Rob 2018-07-01
... ...
I'm trying to generate a recordset like this:
Name MissingDate
-----------------
John 2019-07-01
... ...
John 2010-01-01
Rob 2019-01-01
... ...
Rob 2010-01-01
I've got the flu and I barely know who I am at this moment, I hope it was clear and if anyone could help me with this I would really appreciate it.
If you want missing dates (which appear to be by month), then generate all available dates and take out the ones you have.
with cte as (
select start, end
from dateranges
union all
select dateadd(month, 1, start), end
from cte
where start < end
)
select n.name, cte.start
from cte cross join
(select distinct name from tablea) n left join
tablea a
on a.date = cte.start and a.name = n.name
where a.date is null;

Selecting the most recent date

I have data structured like this:
ID | Enrolment_Date | Appointment1_Date | Appointment2_Date | .... | Appointment150_Date |
112 01/01/2015 01/02/2015 01/03/2018 01/08/2018
113 01/06/2018 01/07/2018 NULL NULL
114 01/04/2018 01/05/2018 01/06/2018 NULL
I need a new variable which counts the number of months between the enrolment_date and the most recent appointment. The challenge is is that all individuals have a different number of appointments.
Update: I agree with the comments that this is poor table design and it needs to be reformatted. Could proposed solutions please include suggested code on how to transform the table?
Since the OP is currently stuck with this bad design, I will point out a temporary solution. As others have suggested, you really must change the structure here. For now, this will suffice:
SELECT '['+ NAME + '],' FROM sys.columns WHERE OBJECT_ID = OBJECT_ID ('TableA') -- find all columns, last one probably max appointment date
SELECT ID,
Enrolment_Date,
CASE WHEN Appointment150_Date IS NOT NULL THEN DATEDIFF (MONTH, Enrolment_Date, Appointment150_Date)
WHEN Appointment149_Date IS NOT NULL THEN DATEDIFF (MONTH, Enrolment_Date, Appointment149_Date)
WHEN Appointment148_Date IS NOT NULL THEN DATEDIFF (MONTH, Enrolment_Date, Appointment148_Date)
WHEN Appointment147_Date IS NOT NULL THEN DATEDIFF (MONTH, Enrolment_Date, Appointment147_Date)
WHEN Appointment146_Date IS NOT NULL THEN DATEDIFF (MONTH, Enrolment_Date, Appointment146_Date)
WHEN Appointment145_Date IS NOT NULL THEN DATEDIFF (MONTH, Enrolment_Date, Appointment145_Date)
WHEN Appointment144_Date IS NOT NULL THEN DATEDIFF (MONTH, Enrolment_Date, Appointment144_Date) -- and so on
END AS NumberOfMonths
FROM TableA
This is a very ugly temporary solution and should be considered as such.
You will need to restructure your data, the given structure is poor database design. Create two separate tables - one called users and one called appointments. The users table contains the user id, enrollment date and any other specific user information. Each row in the appointments table contains the user's unique id and a specific appointment date. Structuring your tables like this will make it easier to write a query to get days/months since last appointment.
For example:
Users Table:
ID, Enrollment_Date
1, 2018-01-01
2, 2018-03-02
3, 2018-05-02
Appointments Table:
ID, Appointment_Date
1, 2018-01-02
1, 2018-02-02
1, 2018-02-10
2, 2018-05-01
You would then be able to write a query to join the two tables together and calculate the difference between the enrollment date and min value of the appointment date.
It is better if you can create two tables.
Enrolment Table (dbo.Enrolments)
ID | EnrolmentDate
1 | 2018-08-30
2 | 2018-08-31
Appointments Table (dbo.Appointments)
ID | EnrolmentID | AppointmentDate
1 | 1 | 2018-09-02
2 | 1 | 2018-09-03
3 | 2 | 2018-09-01
4 | 2 | 2018-09-03
Then you can try something like this.
If you want the count of months from Enrolment Date to the final appointment date then use below query.
SELECT E.ID, E.EnrolmentDate, A.NoOfMonths
FROM dbo.Enrolments E
OUTER APPLY
(
SELECT DATEDIFF(mm, E.EnrolmentDate, MAX(A.AppointmentDate)) AS NoOfMonths
FROM dbo.Appointments A
WHERE A.EnrolmentId = E.ID
) A
And, If you want the count of months from Enrolment Date to the nearest appointment date then use below query.
SELECT E.ID, E.EnrolmentDate, A.NoOfMonths
FROM dbo.Enrolments E
OUTER APPLY
(
SELECT DATEDIFF(mm, E.EnrolmentDate, MIN(A.AppointmentDate)) AS NoOfMonths
FROM dbo.Appointments A
WHERE A.EnrolmentId = E.ID
) A
Try this on sqlfiddle
You have a lousy data structure, as others have noted. You really one a table with one row per appointment. After all, what happens after the 150th appointment?
select t.id, t.Enrolment_Date,
datediff(month, t.Enrolment_Date, m.max_Appointment_Date) as months_diff
from t cross apply
(select max(Appointment_Date) as max_Appointment_Date
from (values (Appointment1_Date),
(Appointment2_Date),
. . .
(Appointment150_Date)
) v(Appointment_Date)
) m;

T-SQL Programming . Common Table expression

I would need a help in the following scneario. I am using T-SQL
Following is my table details. Say the table name is #tempk
Customer Current_Month Contract Amount
201 2015-09-01 3 100
My requirement is to add 12 months from the current month.that is 2016-09-01. Assuming
I am getting the start date of the month. I need the data in the following format
Customer Renewal_Month Contract_months End_Month Amount
201 2015-09-01 3 2016-09-01 100
201 2015-12-01 3 2016-09-01 100
201 2015-03-01 3 2016-09-01 100
201 2015-06-01 3 2016-09-01 100
The contract column can have any values
The consquent records are incremental of contract columns from the previous records.
I am using the following query. I have a date dimension table called Dim_Date that has date,quareter,year,month etc..
WITH GetProrateCTE (Customer_ID,Renewal_Month,Contract_Months,End_Month,MRR) as
(SELECT Customer_ID,Renewal_Month,Contract_Months,DATEADD(month, 12,Renewal_Month) End_Month,MRR
from #tempk),
GetRenewalMonths (Customer_ID,Renewal_Month,Contract_Months,End_Month,MRR) as
(
SELECT A.Customer_ID,B.Month Renewal_Month,A.Contract_Months,A.End_Month,A.MRR
FROM GetProrateCTE A
INNER JOIN (SELECT Month from DW..Dim_Date B GROUP BY MONTH) B
ON B.Month between A.Renewal_Month and A.End_Month
)
SELECT G.Customer_ID,G.Renewal_Month,G.Contract_Months,G.End_Month,G.MRR
FROM GetRenewalMonths G
Could you please help me to achieve the result. Any help would be greatly appreciated.
I want to do this in Common table Expressions. or would it be better if I go cursor.
You can try in this way -
WITH CTE AS
(SELECT Customer,DATEADD(MM,DATEDIFF(MM,0,Current_Month), 0) AS Renewal_Month,Contract,DATEADD(YEAR,1,Current_Month) AS End_Month,Amount,1 AS Level FROM #tempk
UNION ALL
SELECT t.Customer,DATEADD(MONTH,t.Contract,c.Renewal_Month),t.Contract,DATEADD(YEAR,1,t.Current_Month) AS End_Month,t.Amount,Level + 1
FROM #tempk t join CTE c on t.customer = c.customer
WHERE Level < (12/t.Contract))
SELECT Customer,Renewal_Month,Contract AS Contract_months,End_Month,Amount
FROM CTE
Just append your logic of the date dimension table to this.