OrderBy date but containing recors from "half" the day before - sql

SELECT cast(Ord.regdate AS date) AS Date,
COUNT(Ord.Ordernumber) AS [Backlog of line],
(SELECT CASE
WHEN CAST(SUM(Ord.Qty) AS INT) IS NULL THEN 0
ELSE CAST(SUM(Ord.Qty) AS INT)
END) AS [Backlog of Qty]
FROM Orders [Ord]
WHERE Ord.regdate < CAST(CAST(CAST(DATEADD(DAY,-1,GETDATE()) AS date) AS varchar(10)) + ' 12:00' AS datetime)
GROUP BY cast(Ord.regdate AS date)
ORDER BY cast(Ord.regdate AS date) DESC
Date : Backlog of line : Backlog of Qty
2015-09-20 : 10 : 50
2015-09-21 : 5 : 25
The problem here is that when we get to next day... the 21 will get more records if the are orders that are done after 12:00 the 2015-09-21...
When we run this it just takes the records that are older then 12:00 the day before and groups them by date.
My Question is..
I want the date 2015-09-22 to show and have the records after 12:00 from 2015-09-21 until 12:00 the 2015-09-22... and so an

This should works. It shifts regdate by 12 hours first.
SELECT Ord.regdate AS Date,
COUNT(Ord.Ordernumber) AS [Backlog of line],
(SELECT CASE
WHEN CAST(SUM(Ord.Qty) AS INT) IS NULL THEN 0
ELSE CAST(SUM(Ord.Qty) AS INT)
END) AS [Backlog of Qty]
FROM (
Select Ordernumber, Qty, CAST(DATEADD(HOUR, 12, regdate) as date) as regdate
From Orders
WHERE regdate < CAST(CAST(CAST(DATEADD(DAY,-1,GETDATE()) AS date) AS varchar(10)) + ' 12:00' AS datetime)
) [Ord]
GROUP BY Ord.regdate
ORDER BY Ord.regdate DESC

Related

Grouping several years' data by week

I am using Covid world data, which tracks number of new_cases every day. I have data from 2020-01-01 to present day. This is my current query:
SELECT MIN(date) as week, datepart(iso_week, date) week_num, sum(new_cases) as [Total Cases]
FROM covid_data_cleaned
GROUP BY DATEPART(iso_week, date), DATEPART(year, date)
ORDER BY MIN(date) DESC
Which gives me pretty much what I want except for when the year changes:
week week_num Total Cases
2022-01-10 2 20803473
2022-01-03 1 17217248
**2022-01-01 52 2115780**
**2021-12-27 52 7971560**
2021-12-20 51 5561762
I want to figure out the workaround to combining the '52' values together. Alternatively, my dataset has 112 weeks; can I assign the values 1-112 to the whole dataset rather than 1-52 for each year?
You could aggregate on a correction via a CASE WHEN for the iso year.
SELECT
min(weekstart) as [week]
, [iso_week] as weeknum
, sum(new_cases) as [Total Cases]
FROM
(
SELECT
min([date]) as weekstart
, datepart(iso_week, [date]) as [iso_week]
, case
when month(min([date])) = 1
and datepart(iso_week, [date]) >= 52
then year([date]) - 1
when month(min([date])) = 12
and datepart(iso_week, [date]) = 1
then year([date]) + 1
else year([date])
end as iso_year
, sum(new_cases) as new_cases
FROM covid_data_cleaned
GROUP BY year([date]), datepart(iso_week, [date])
) q
GROUP BY iso_year, [iso_week]
ORDER BY [week] DESC;
Instead of iso week, you can use week.
DECLARE #table table(datev date, new_Cases int)
INSERT INTO #table values
('2022-01-01',123),('2022-01-09',432),('2022-01-03',123),('2021-12-27',234);
SELECT MIN(datev) as week, datepart(week, datev) week_num, sum(new_cases) as [Total Cases]
FROM #table
GROUP BY DATEPART(week, datev), DATEPART(year, datev)
ORDER BY MIN(datev) DESC
week
week_num
Total Cases
2022-01-09
3
432
2022-01-03
2
123
2022-01-01
1
123
2021-12-27
53
234

Keep last n business days records from today date in SQL Server

How can we keep last n business days records from today date in this table:
Suppose n = 7
Sample Data:
Table1:
Date
----------
2021-11-29
2021-11-30
2021-12-01
2021-12-02
2021-12-03
2021-12-04
2021-12-05
2021-12-06
2021-12-07
2021-12-08
2021-12-09
2021-12-10
2021-12-11
2021-12-12
2021-12-13
Based on this table data we want output like below. It should delete all the rows before the 03-Dec or data for last 7 business days.
Date
-------
2021-12-03
2021-12-06
2021-12-07
2021-12-08
2021-12-09
2021-12-10
2021-12-13
Note: It's fine if we keep data for Saturday, Sunday in between business days.
I tried this query
DECLARE #n INT = 7
SELECT * FROM Table1
WHERE [date] < Dateadd(day, -((#n + (#n / 5) * 2)), Getdate())
but Saturday, Sunday logic doesn't fit here with my logic. Please suggest better approach.
You can get the 7th working day from today as
select top(1) cast(dateadd(d, -n + 1, getdate()) as date) d
from (
select n
, sum (case when datename(dw, dateadd(d, -n + 1, getdate())) not in ('Sunday', 'Saturday') then 1 end) over(order by n) wdn
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11)
)t0(n)
) t
where wdn = 7
order by n;
Generally using on-the-fly tally for a #n -th day
declare #n int = 24;
with t0(n) as (
select n
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
) t(n)
), tally as (
select top(#n + (#n/5 +1)*2) row_number() over(order by t1.n) n
from t0 t1, t0 t2, t0 t3
)
select top(1) cast(dateadd(d, -n + 1, getdate()) as date) d
from (
select n
, sum (case when datename(dw, dateadd(d, -n + 1, getdate())) not in ('Sunday', 'Saturday') then 1 end) over(order by n) wdn
from tally
) t
where wdn = #n
order by n;
You can use CTE to mark target dates and then delete all the others from the table as follows:
; With CTE As (
Select [Date], Row_number() Over (Order by [Date] Desc) As Num
From tbl
Where DATEPART(weekday, [Date]) Not In (6,7)
)
Delete From tbl
Where [Date] Not In (Select [Date] From CTE Where Num<=7)
If the number of business days in the table may be less than 7 and you need to bring the total number of days to 7 by adding days off, try this:
Declare #n Int = 7
; With CTE As (
Select [Date], IIF(DATEPART(weekday, [Date]) In (6,7), 0, 1) As IsBusinessDay
From tbl
)
Delete From tbl
Where [Date] Not In (Select Top(#n) [Date] From CTE Order By IsBusinessDay Desc, [Date] Desc)
If there is only one date for each day, you can simply do this:
SELECT TOP 7 [Date] FROM Table1
WHERE
[Date] < GETDATE() AND DATENAME(weekday, [DATE]) NOT IN ('Saturday', 'Sunday')
ORDER BY
[DATE] DESC

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.

How to Calculate Employment Experience with Special Case

I am trying to calculate the years of experience
Let's say someone has multiple employments as follow:
startdate enddate
2007-08-27 2008-05-09
2007-08-27 2008-05-09
2012-01-01 2018-07-31
2013-01-06 2019-03-25
What would be the correct sql syntax to just select startdate, enddate which will be later pluged in a function to calculate the number of days for each employment?
Here's my expected result:
1st row: startdate 8/27/2007, enddate 5/9/2008
2nd row: startdate 1/1/2012, enddate 3/25/2019
The second employment took place during the same time for the 1st employment so, it will not be counted. The fourth employment started before the third employment ended so we should use the startdate for the third employment and enddate for the fourth employment
Use Distinct to remove dplicated records. Then you can use this query:
Select totaldays / 365 from
(Select Sum(
DATEDIFF(day, srartdate, enddate )
) As totaldays
)
This query returns the dates without overlapping:
select
v.startdate startdate,
min(vv.enddate) enddate
from view_appemployment v
inner join view_appemployment vv
on v.startdate <= vv.enddate
and not exists (
select * from view_appemployment vvv
where vv.enddate >= vvv.startdate and vv.enddate < vvv.enddate
)
where not exists (
select * from view_appemployment vvvv
where v.startdate > vvvv.startdate and v.startdate <= vvvv.enddate
)
group by v.startdate
See the demo
Results:
startdate | enddate
------------------ | ------------------
27/08/2007 00:00:00 | 09/05/2008 00:00:00
01/01/2012 00:00:00 | 25/03/2019 00:00:00
This is a Gaps & Islands in Sequences problem, the following query should do what you want:
CREATE TABLE #emp (empid int, startdate date,enddate date)
INSERT INTO #emp VALUES
(1,'2007-08-27','2008-05-09'),
(1,'2007-08-27','2008-05-09'),
(1,'2012-01-01','2018-07-31'),
(1,'2013-01-06','2019-03-25')
;WITH starts AS (
SELECT em.*,
(CASE WHEN EXISTS (SELECT 1
FROM #emp em2
WHERE em2.EmpID = em.EmpID AND
em2.StartDate < em.StartDate AND
em2.EndDate >= em.StartDate
) THEN 0 ELSE 1 END) AS [IsStart]
FROM #emp em )
SELECT EmpID
,MIN(StartDate) AS [StartDate]
,MAX(EndDate) AS [EndDate]
FROM (SELECT s.*, SUM(isstart) OVER (PARTITION BY EmpID ORDER BY StartDate) AS [grp]
FROM starts s
) s
GROUP BY EmpID, grp
ORDER BY EmpID
Please try this:
SELECT concat(id," row: start date ",date_format(start_date,'%d/%m/%y')," end date ",date_format(end_date,'%d/%m/%y'))as dateinfo FROM `dates`