Select working days from a calendar in a table - sql

I have a table with a calendar which look like this in SQL Server
Date WorkingDay
20200514 1
20200515 1
20200516 0
20200517 0
20200518 1
20200519 1
20200520 1
20200521 0
20200522 1
I am trying to select the third working day from a specific date.
If I start the 20200514, result must be the 20200518.
I try with a query like this but I do not have the date, only a list of result
select top 3 *
from tmp_workingdays
where workingday = 1 and date >= 20200514
order by date asc
How can I select only the date?

Try the following, here is the demo.
select
date
from
(
select
*,
row_number() over (order by date) as rnk
from tmp_workingdays
where workingday=1
and date >= 20200514
) val
where rnk = 3
output:
| Date |
------------
|2020-05-18|

DECLARE #date AS DATE = '2020-05-14' ;
WITH cte_WorkingDates AS
(
SELECT [Date]
FROM tmp_workingdays
WHERE WorkingDay = 1
)
SELECT [Date]
FROM cte_WorkingDates
WHERE [Date] >= #date
ORDER BY [Date] ASC
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY ;

select date
from tmp_workingdays
where workingday = 1
and date >= 20200514
order by date asc
OFFSET 2 ROWS
FETCH NEXT 1 ROWS ONLY

Related

SQL: How to create a daily view based on different time intervals using SQL logic?

Here is an example:
Id|price|Date
1|2|2022-05-21
1|3|2022-06-15
1|2.5|2022-06-19
Needs to look like this:
Id|Date|price
1|2022-05-21|2
1|2022-05-22|2
1|2022-05-23|2
...
1|2022-06-15|3
1|2022-06-16|3
1|2022-06-17|3
1|2022-06-18|3
1|2022-06-19|2.5
1|2022-06-20|2.5
...
Until today
1|2022-08-30|2.5
I tried using the lag(price) over (partition by id order by date)
But i can't get it right.
I'm not familiar with Azure, but it looks like you need to use a calendar table, or generate missing dates using a recursive CTE.
To get started with a recursive CTE, you can generate line numbers for each id (assuming multiple id values) in the source data ordered by date. These rows with row number equal to 1 (with the minimum date value for the corresponding id) will be used as the starting point for the recursion. Then you can use the DATEADD function to generate the row for the next day. To use the price values ​​from the original data, you can use a subquery to get the price for this new date, and if there is no such value (no row for this date), use the previous price value from CTE (use the COALESCE function for this).
For SQL Server query can look like this
WITH cte AS (
SELECT
id,
date,
price
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY date) AS rn
FROM tbl
) t
WHERE rn = 1
UNION ALL
SELECT
cte.id,
DATEADD(d, 1, cte.date),
COALESCE(
(SELECT tbl.price
FROM tbl
WHERE tbl.id = cte.id AND tbl.date = DATEADD(d, 1, cte.date)),
cte.price
)
FROM cte
WHERE DATEADD(d, 1, cte.date) <= GETDATE()
)
SELECT * FROM cte
ORDER BY id, date
OPTION (MAXRECURSION 0)
Note that I added OPTION (MAXRECURSION 0) to make the recursion run through all the steps, since the default value is 100, this is not enough to complete the recursion.
db<>fiddle here
The same approach for MySQL (you need MySQL of version 8.0 to use CTE)
WITH RECURSIVE cte AS (
SELECT
id,
date,
price
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY date) AS rn
FROM tbl
) t
WHERE rn = 1
UNION ALL
SELECT
cte.id,
DATE_ADD(cte.date, interval 1 day),
COALESCE(
(SELECT tbl.price
FROM tbl
WHERE tbl.id = cte.id AND tbl.date = DATE_ADD(cte.date, interval 1 day)),
cte.price
)
FROM cte
WHERE DATE_ADD(cte.date, interval 1 day) <= NOW()
)
SELECT * FROM cte
ORDER BY id, date
db<>fiddle here
Both queries produces the same results, the only difference is the use of the engine's specific date functions.
For MySQL versions below 8.0, you can use a calendar table since you don't have CTE support and can't generate the required date range.
Assuming there is a column in the calendar table to store date values ​​(let's call it date for simplicity) you can use the CROSS JOIN operator to generate date ranges for the id values in your table that will match existing dates. Then you can use a subquery to get the latest price value from the table which is stored for the corresponding date or before it.
So the query would be like this
SELECT
d.id,
d.date,
(SELECT
price
FROM tbl
WHERE tbl.id = d.id AND tbl.date <= d.date
ORDER BY tbl.date DESC
LIMIT 1
) price
FROM (
SELECT
t.id,
c.date
FROM calendar c
CROSS JOIN (SELECT DISTINCT id FROM tbl) t
WHERE c.date BETWEEN (
SELECT
MIN(date) min_date
FROM tbl
WHERE tbl.id = t.id
)
AND NOW()
) d
ORDER BY id, date
Using my pseudo-calendar table with date values ranging from 2022-05-20 to 2022-05-30 and source data in that range, like so
id
price
date
1
2
2022-05-21
1
3
2022-05-25
1
2.5
2022-05-28
2
10
2022-05-25
2
100
2022-05-30
the query produces following results
id
date
price
1
2022-05-21
2
1
2022-05-22
2
1
2022-05-23
2
1
2022-05-24
2
1
2022-05-25
3
1
2022-05-26
3
1
2022-05-27
3
1
2022-05-28
2.5
1
2022-05-29
2.5
1
2022-05-30
2.5
2
2022-05-25
10
2
2022-05-26
10
2
2022-05-27
10
2
2022-05-28
10
2
2022-05-29
10
2
2022-05-30
100
db<>fiddle here

Get latest entry in each week over several years period

I have the following table to store history for entities:
Date Id State
-------------------------------------
2017-10-10 1 0
2017-10-12 1 4
2018-5-30 1 8
2019-4-1 2 0
2018-3-6 2 4
2018-3-7 2 0
I want to get last entry for each Id in one week period e.g.
Date Id State
-------------------------------------
2017-10-12 1 4
2018-5-30 1 8
2019-4-1 2 0
2018-3-7 2 0
I'd try to use Partition by:
select
ID
,Date
,State
,DatePart(week,Date) as weekNumber
from TableA
where Date = (
select max(Date) over (Partition by Id Order by DatePart(week, Date) Desc)
)
order by ID
but it still gives me more than one result per week.
You can use ROW_NUMBER():
SELECT a.*
FROM (SELECT a.*, ROW_NUMBER() OVER (PARTITION BY a.id, DATEPART(WK, a.Date) ORDER BY a.Date DESC) AS Seq
FROM tablea a
) a
WHERE seq = 1
ORDER BY id, Date;

Multiple select of max values in PostgreSQL

I have a table like this:
ID cbk due_16_30 due_31_60
1 2018-06-19 5 200
2 2018-06-19 100 -5
1 2018-06-19 -2 2
2 2018-06-18 20 Null
2 2018-06-18 50 22
1 2018-06-18 30 150
I want to select the max due_16_30 and max due_31_60 from the latest date, where date is between some start date and end date. How can I do that in PostgreSQL?
UPDATE 1:
Thank you for your answers! I have found one more complication - what if I need to do the same, but for different ID? Now what I see - it takes the latest date from between and if there is no row for the specific ID for that date - that ID is missed.
I hope this helps
select cbk, max(due_16_30), max(due_31_60)
from [table]
where cbk = (select max(cbk)
from [table]
where cbk >= start_date and
cbk <= end_date
)
group by cbk;
First get the latest date in subquery, an then get the max due dates :
select max(due_16_30) as max_due_16_30,
max(due_31_60) as max_due_31_60
from tab
where cbk in
(
select max(cbk)
from tab
where cbk between date'2018-06-18' and date'2018-06-19'
);
DB-Fiddle Demo
An alter approach would be LIMTIT clause with subquery :
select t.cbk, max(t.due_16_30), max(t.due_31_60)
from table t
where cbk = (select t1.cbk
from table t1
where t1.cbk >= start_dt and t1.cbk <= end_dt
order by t1.cbk desc
limit 1
)
group by t.cbk;
Try this
select due_16_30,due_31_60
from
(
SELECT *
from your_table
ORDER BY cbk DESC
LIMIT 1
) as T

Filtering a query with subqueries

In order to understand the question I will explain the result expected.
I have a db table where I save some data of the activity of the current day. Then I want to sum some numeric fields and the last register of the text fields, using a filter between two dates.
Example:
•DB TABLE
ID|CALLS|RESULT | DATE
1 | 2 |FAIL |15/09/16
1 | 1 |ERROR |16/09/16
1 | 3 |OK |17/09/16
•SUM BETWEEN 15 and 17
ID|TOTAL CALLS|LAST RESULT
1 | 6 | OK
•SUM BETWEEN 15 and 16
ID|TOTAL CALLS|LAST RESULT
1 | 3 | ERROR
-Would this be the solution?
SELECT DISTINCT ID,
TOTAL_CALLS=SUM(CALLS),
LAST_RESULT= (
SELECT RESULT FROM TABLE T2 where T2.DATE between MIN(T1.DATE) and MAX (T1.DATE) and T1.ID=T2.ID
)
FROM TABLE T1
WHERE
TIME BETWEEN 15/09/16 and 17/09/16
GROUP BY ID
Thank you very much!
Regards
Use the below query.
;WITH cte_1
AS
(SELECT ID,SUM(CALLS)OVER( PARTITION BY ID) [TOTAL CALLS]
,Result [LAST RESULT]
,ROW_NUMBER()OVER( PARTITION BY ID ORDER BY [DATE] desc) RNO
from #YourTable T
WHERE [DATE] between '09/15/2016' AND '09/16/16')
SELECT ID,[TOTAL CALLS],[LAST RESULT]
FROM cte_1
WHERE Rno=1
You can use TOP 1 WITH TIES with ORDERING to get what you need:
DECLARE #dFrom date = '2016-09-15',
#dTo date = '2016-09-16'
SELECT TOP 1 WITH TIES
ID,
SUM(CALLS) OVER (PARTITION BY ID) [TOTAL CALLS],
RESULT [LAST RESULT]
FROM YourTable
WHERE [DATE] between #dFrom and #dTo
ORDER BY ROW_NUMBER() OVER (PARTITION BY ID ORDER BY [DATE]) DESC
You can use max date to get result column value.
DECLARE #FromDate DATETIME= '15 SEP 2016'
DECLARE #ToDate DATETIME= '16 SEP 2016'
SELECT ID , SUM(CALLS) , ( SELECT RESULT
FROM #yourTable
WHERE [DATE] = #ToDate
) RESULT
FROM **#yourTable**
WHERE [DATE] BETWEEN #FromDate AND #ToDate
GROUP BY ID

TSQL Calendar table, count 10 workings days from date

I have a calendar table which stores rows of dates and an indication of wether that date is a holiday or working day.
How can I select the date that is 5 working days into the future from the 2014-12-22 so the selected date will be 2014-12-31
Date_Id Date_Date Date_JDE Is_WorkingDay
20141222 2014-12-22 114356 1
20141223 2014-12-23 114357 1
20141224 2014-12-24 114358 1
20141225 2014-12-25 114359 0
20141226 2014-12-26 114360 0
20141227 2014-12-27 114361 0
20141228 2014-12-28 114362 0
20141229 2014-12-29 114363 1
20141230 2014-12-30 114364 1
20141231 2014-12-31 114365 1
You can use a CTE like this...
;WITH cteWorkingDays AS
(
SELECT Date_Date, ROW_NUMBER() OVER (ORDER BY Date_Date) as 'rowNum'
FROM TableName
WHERE Is_WorkingDay = 1
and Date_Date > '20141222' -- this will be a param I suppose
)
SELECT Date_Date
FROM cteWorkingDays
WHERE rowNum = 5 -- this can be changed to 10 (title value
This is hand typed, but it will be close enough.
EDIT: Based on comment.
Declare #DateToUse TYPE -- unsure if you're using a string or a date type.
SELECT #DateToUse = Date_Date
FROM cteWorkingDays
WHERE rowNum = 5
...;
WITH DatesCTE AS
(
SELECT Date_Id,
Date_Date,
Date_JDE,
Is_WorkingDay,
ROW_NUMBER() OVER(ORDER BY Date_Date) AS rn
FROM DatesTable
WHERE Is_WorkingDay = 1
AND Date_Date > '2014-12-22'
)
SELECT Date_Date
FROM DatesCTE
WHERE rn = 5
SQL Fiddle Demo
with Derived Tables
select * from
(
SELECT Date_Date, ROW_NUMBER() OVER (ORDER BY Date_Date) as 'RowNum'
FROM Table_calendar
WHERE Is_WorkingDay = 1
and CAST(Date_Date as DATE) > '2014-12-22'
)d
where d.RowNum=5
You can Try Like This:
with calender as
(select top 5 date_id,date_date,date_jde from calender
where date_date>='2014-12-22' and is_workingday='1)calender
select top 1 * from calender order by date_date desc