Insert creating multiple register from one table to another - sql

I have the following table
Eployee ID Start Month End Month
1 3/4/2013 26/11/2017
2 20/12/2014
3 1/8/2017 30/01/2018
I need to create a new table that will contain one register for each month an employee has worked up to date (if there is no end month it's still working)
I need to get something like this
Month Employee ID
4/2013 1
5/2013 1
...
11/2017 1
12/2014 2
1/2015 2
...
current month 2
8/2017 3
...
1/2018 3

If, you are working with SQL Server, then recursive cte is one option :
with t as (
select EmployeeID, StartMonth, EndMonth
from table
where EndMonth is not null
union all
select EmployeeID, dateadd(mm, 1, StartMonth), EndMonth
from t
where StartMonth < EndMonth
)
select EmployeeID, concat(datepart(mm, StartMonth), '/', datepart(year, StartMonth)) as Month
from t
union all
select EmployeeID, concat(datepart(mm, getdate()), '/', datepart(year, getdate()))
from table
where EndMonth is null
order by EmployeeID;

Related

Split dates into quarters based on start and end date

I want to split quarters based on a given start and end date.
I have the following table:
table1
ID
start_date
end_date
No. of Quarters
1
01-01-2017
01-01-2018
4
2
01-04-2017
01-10-2018
7
So the result table should be have dates split based on number of quarters and end date.
The result table should look like:
table2
ID
Quarterly Start Date
1
01-01-2017
1
01-04-2017
1
01-07-2017
1
01-10-2017
2
01-04-2017
2
01-07-2017
2
01-10-2017
2
01-01-2018
2
01-04-2018
2
01-07-2018
2
01-10-2018
I found a solution on stackoverflow which states
declare #startDate datetime
declare #endDate datetime
select
#startDate= ET.start_date,
#endDate= ET.end_date
from
table1
;With cte
As
( Select #startDate date1
Union All
Select DateAdd(Month,3,date1) From cte where date1 < #endDate
) select cast(cast( Year(date1)*10000 + MONTH(date1)*100 + 1 as
varchar(255)) as date) quarterlyDates From cte
Since I am new to sql, I am having troubles customizing it to my problem.
Could anyone please recommend a way? Thanks!
If I understand correctly, the recursive CTE would look like:
with cte as (
select id, start_date, num_quarters
from t
union all
select id, dateadd(month, 3, start_date), num_quarters - 1
from cte
where num_quarters > 1
)
select *
from cte;
Here is a db<>fiddle.

For each quarter between two dates, add rows quarter by quarter in SQL SERVER

I have a table, with types int, datetime, datetime:
id start date end date
-- ---------- ----------
1 2019-04-02 2020-09-17
2 2019-08-10 2020-08-10
Here is create/insert:
CREATE TABLE dbo.something
(
id int,
[start date] datetime,
[end date] datetime
);
INSERT dbo.something(id,[start date],[end date])
VALUES(1,'20190402','20200917'),(2,'20190810','20200810');
What is a SQL query that can produce these results:
id Year Quarter
-- ---- ----------
1 2019 2
1 2019 3
1 2019 4
1 2020 1
1 2020 2
1 2020 3
2 2019 3
2 2019 4
2 2020 1
2 2020 2
2 2020 3
Just use a recursive CTE. This version switches to counting quarters from year 0:
with cte as (
select id,
year(start_date) * 4 + datepart(quarter, start_date) - 1 as yyyyq,
year(end_date) * 4 + datepart(quarter, end_date) - 1 as end_yyyyq
from t
union all
select id, yyyyq + 1, end_yyyyq
from cte
where yyyyq < end_yyyyq
)
select id, yyyyq / 4 as year, (yyyyq % 4) + 1 as quarter
from cte;
Here is a db<>fiddle.
If you cannot make another reference table/etc, you can use DATEDIFF (and DATEPART) using quarters, and then some simple date arithmetic.
The logic below is simply to find, for each startdate, the first quarter and then the number of additional quarters to get to the maximum. Then do a SELECT where the additional quarters are added to the startdate, to get each quarter.
The hardest part of the query to understand imo is the WITH numberlist section - all this does is generate a series of integers between 0 and the maximum number of quarters difference. If you already have a numbers table, you can use that instead.
Key code part is below, and here's a full DB_Fiddle with some additional test data.
CREATE TABLE #yourtable (id int, startdate date, enddate date)
INSERT INTO #yourtable (id, startdate, enddate) VALUES
(1, '2019-04-02', '2020-09-17'),
(2, '2019-08-10', '2020-08-20')
; WITH number_list AS
-- list of ints from 0 to maximum number of quarters
(SELECT n
FROM (SELECT ones.n + 10*tens.n AS n
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n)
) AS a
WHERE n <= (SELECT MAX(DATEDIFF(quarter,startdate,enddate)) FROM #yourtable)
)
SELECT id,
YEAR(DATEADD(quarter, number_list.n, startdate)) AS [Year],
DATEPART(quarter, DATEADD(quarter, number_list.n, startdate)) AS [Quarter]
FROM (SELECT id, startdate, DATEDIFF(quarter,startdate,enddate) AS num_additional_quarters FROM #yourtable) yt
CROSS JOIN number_list
WHERE number_list.n <= yt.num_additional_quarters
DROP TABLE #yourtable
First create a date dimension table which contains date, corresponding quarter and year. Then use below query to get the result. Tweak column and table name according to your schema.
with q_date as
(
select 1 as id, '2019-04-02' :: date as start_date, '2020-09-17' :: date as end_date
UNION ALL
select 2 as id, '2019-08-10' :: date as start_date, '2020-08-10' :: date as end_date
)
select qd.id, dd.calendar_year, dd.calendar_quarter_number
from dim_date dd, q_date qd
where dd.date_dmk between qd.start_date and qd.end_date
group by qd.id, dd.calendar_year, dd.calendar_quarter_number
order by qd.id, dd.calendar_year, dd.calendar_quarter_number;

How do I calculate amount of time in each week between two dates - SQL Server

[EDITED TO SIMPLIFY]
I have 500+ records. All of which have a reference number, a start date, an end date and a total machining time.
Ref StartDate EndDate MachineTimeHours
123 24/01/2020 30/01/2020 28
321 25/02/2020 27/02/2020 18
Starting at the start date, I need to calculate how many machining hours fall into 1 week and how many fall into the next.
Our working days are Monday to Thursday 8 Hours & Friday 4 Hours.
Ref 321 has a start of 25/2 which is a Tuesday and a finish date of 27/2 which is a Thursday in the same week. This will calculate as all 18 hours being in the same week.
Ref 123 has a start of 24/01. This is a Friday in Week 4 of 2020.
Based on my rules, that would be 4 hours in week 4 and 24 Hours in week 5.
I have a table called 'DatesList' which has all days on it (as well as week number and working hours).
I need my table to list each record for each week and I'll do the grouping on a separate report.
In effect I'd like
Ref StartDate EndDate MachineTimeHours Week Hours
123 24/01/2020 30/01/2020 28 4 4
123 24/01/2020 30/01/2020 28 5 24
321 25/02/2020 27/02/2020 18 9 18
You can start with creating some reference tables.
For the example those are just temporary tables.
Reference data:
--
-- Reference tables
--
CREATE TABLE #ref_calendar
(
CalDate DATE PRIMARY KEY,
DayOfWeek SMALLINT NOT NULL,
WeekNr SMALLINT NOT NULL,
IsHoliday BIT NOT NULL DEFAULT 0
);
DECLARE #year int = 2020;
SET DATEFIRST 1; -- 1: monday start
;WITH RCTE_DATES AS
(
SELECT
DATEFROMPARTS(#year, 1, 1) AS caldate
UNION ALL
SELECT dateadd(day, 1, caldate)
FROM RCTE_DATES
WHERE caldate <= DATEFROMPARTS(#year, 12, 31)
)
INSERT INTO #ref_calendar (CalDate, DayOfWeek, WeekNr)
SELECT
caldate,
DATEPART(weekday, caldate) AS DayOfWeek,
DATEPART(week, caldate) AS WeekNr
FROM rcte_dates c
WHERE NOT EXISTS
(
SELECT 1
FROM #ref_calendar ref
WHERE ref.CalDate = c.caldate
)
OPTION (MAXRECURSION 366);
CREATE TABLE #ref_workhours
(
Id INT IDENTITY(1,1) PRIMARY KEY,
DayOfWeek SMALLINT NOT NULL,
WorkHours DECIMAL(4,2) NOT NULL,
ActiveFrom DATE NOT NULL DEFAULT GetDate(),
ActiveTill DATE
);
INSERT INTO #ref_workhours
(DayOfWeek, WorkHours) VALUES
(1, 8.0), (2, 8.0), (3, 8.0), (4, 8.0), (5, 4.0),
(6, 0), (7, 0);
Some sample data:
--
-- Sample data
--
CREATE TABLE YourDateRangeTable
(
Id INT IDENTITY(1,1) PRIMARY KEY,
JobNumber INT NOT NULL,
PartNumber VARCHAR(8) NOT NULL,
Machine CHAR(3) NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL
);
INSERT INTO YourDateRangeTable
(JobNumber, PartNumber, Machine, StartDate, EndDate) values
(12345, 'XYZ321', 'DL8', '2020-01-24', '2020-01-30');
Then you can run a query that uses the reference tables.
SELECT JobNumber, PartNumber, Machine
, YEAR(cal.CalDate) AS [Year]
, cal.WeekNr AS [Week]
, SUM(w.WorkHours) AS [Hours]
FROM YourDateRangeTable t
JOIN #ref_calendar cal
ON cal.CalDate >= t.StartDate
AND cal.CalDate < t.EndDate
JOIN #ref_workhours w
ON w.DayOfWeek = cal.DayOfWeek
GROUP BY JobNumber, PartNumber, Machine
, YEAR(cal.CalDate), cal.WeekNr;
Returns:
JobNumber PartNumber Machine Year Week Hours
12345 XYZ321 DL8 2020 4 4.00
12345 XYZ321 DL8 2020 5 24.00
A test on db<>fiddle here
You can get all the detes of two given date and also weeknumber and a case statement for the working hour. Based on the result from the inner query write an outer query which will give the sum of total working hour.
Here is the given query.
DECLARE #MinDate DATE = '20200124',
#MaxDate DATE = '20200130'
--Fri Week 4 = 4 hours
--Mon Week 5 = 8 hours
--Tue Week 5 = 8 hours
--Wed Week 5 = 8 hours
Select WeekNo, SUM(WorkingHour) as TotalWorkingHour from(
Select [DATE], DATEPART(WEEK, [DATE]) - DATEPART(WEEK, DATEADD(MM, DATEDIFF(MM,0,[DATE]), 0))+ 1 as WeekNo,
DATENAME(weekday, [DATE]) as WeekDay, Case DATENAME(weekday, [DATE])
when 'Friday' then 4
when 'Monday' then 8
when 'Tuesday' then 8
when 'Wednesday' then 8
else 0
end as WorkingHour from(
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #MinDate)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
)a
)b group by WeekNo
It will give the result as below:
WeekNo TotalWorkingHour
------------------------
4 4
5 24
You can find the demo Here.

Split 2 dates into 2 row isq

I want to split holiday between 2 month into 2 row in sql for example :
EmpId StartDate EndDate TotalDays
1 2017/5/25 2017/6/10 16
I need to split it into 2 row like the following :
EmpId StartDate EndDate TotalDays
1 2017/5/25 2017/5/31 6
1 2017/6/1 2017/6/10 10
Thanks you
Assuming holidays only have one month split (as in your example):
select empid, startdate,
(case when eomonth(startdate) < enddate then eomonth(startdate) else enddate end) as enddate
from t
union all
select empid, dateadd(day, 1, eomonth(startdate)), enddate
from t
where eomonth(startdate) < enddate;
Well, that doesn't give TotalDays, but you can do that using a subquery and datediff().

How can I dynamically create dates between a specific timespan and weeks?

I have the following customer table:
ID | StartDate | WeekCount
1 | 01.12.2015 | 2
2 | 03.12.2015 | 4
3 | 06.06.2014 | 8
The Startdate represents the date the customer gets the first visit, WeekCount is for the next visit (every X Weeks)
I want to query the next visit dates for a timespawn.
Lets say the first visit is 03.12.2015 then I query for March 2016 so the expected date should be 03.03.2015.
So basically StartDate+WeekCount and then the Date between filter.
I think recursive CTE will help you to solve your problem.
DECLARE #to_date DATETIME
SET #to_date = N'2016.03.01'
;WITH test_data AS(
SELECT 1 AS id, CAST(N'2015.12.01' AS DATETIME) AS startDate, 2 AS weekCount
UNION ALL
SELECT 2 AS id, CAST(N'2015.12.03' AS DATETIME) AS startDate, 4 AS weekCount
UNION ALL
SELECT 3 AS id, CAST(N'2014.06.06' AS DATETIME) AS startDate, 8 AS weekCount
),
result_tbl AS(
SELECT id, startDate, weekCount FROM test_data
UNION ALL
SELECT id, DATEADD(ww, R.weekCount, R.startDate), weekCount FROM result_tbl AS R
WHERE R.startDate < #to_date
)
SELECT * FROM result_tbl
ORDER BY id
Provided the datatype is date/datetime
Select columns from your_table
where StartDate>='20160301' and StartDate<'20160401'