I have a partitioned table by a date, in the field LOAD_DATE.
I need to extract the last 6 partitions for the first day of each month.
i.e.
2022-01-01
2021-12-01
2021-11-01
2021-10-01
2021-09-01
2021-08-01
So far, I have something like this, which is pretty ugly, because I have to copy-paste a line to allow more months:
SELECT *
FROM partitionedTable
where FECHA_CARGA IN (
Select
DATE_TRUNC(CURRENT_DATE, MONTH)
UNION ALL
Select
DATE_TRUNC(DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH), MONTH)
UNION ALL
Select
DATE_TRUNC(DATE_SUB(CURRENT_DATE, INTERVAL 2 MONTH), MONTH),
UNION ALL
Select
DATE_TRUNC(DATE_SUB(CURRENT_DATE, INTERVAL 3 MONTH), MONTH),
UNION ALL
Select
DATE_TRUNC(DATE_SUB(CURRENT_DATE, INTERVAL 4 MONTH), MONTH),
UNION ALL
Select
DATE_TRUNC(DATE_SUB(CURRENT_DATE, INTERVAL 5 MONTH), MONTH)
)
How can I improve this code?
You should be able to change your where clause to
fecha_carga in UNNEST(generate_date_array( date_trunc(date_sub(current_date(), INTERVAL 5 MONTH), MONTH), current_date() , INTERVAL 1 MONTH))
Related
I am trying to work on a query where there is date selection in the where clause i.e. if sysdate is Monday I have to get the dates from Monday to Saturday and Hours Between Morning 08:00:00 AM to Next Day Morning 07:00:00 AM. I am hardcoding the dates and Hours in the where clause, When I run the query data does not show.
Query:
SELECT TO_CHAR(sysdate, 'HH24:MI:SS'), REPLACE(TO_CHAR(sysdate, 'DAY'), ' ')
FROM dual
WHERE TO_CHAR(sysdate, 'HH24:MI:SS') BETWEEN '08:01:00' AND '08:00:00'
AND TO_CHAR(sysdate, 'DAY') >= 'MONDAY'
AND TO_CHAR(sysdate, 'DAY') <= 'SATURDAY';
You need to filter the wider range first (from 8 AM at Monday till the end of a Saturday, if I understood correctly) and then exclude time from 7 AM till 8 AM.
In the below code iw format element stands for ISO week, that starts on the Monday.
with a as (
select
date '2022-04-17'
+ interval '01:30:00' hour to second
+ interval '2' hour * level
as dt
from dual
connect by level < 90
)
select
to_char(dt, 'yyyymmdd') as day_
, listagg(to_char(dt, 'hh24:mi'), ',')
within group (order by dt asc) as hours
from a
where 1 = 1
/*From Mon 08 AM*/
and dt > trunc(dt, 'iw') +
interval '8' hour
/*Till Sat end of the day*/
and dt < trunc(dt, 'iw') + 6
/*and except minutes between 7 and 8 AM*/
and not (
to_char(dt, 'hh24mi') < '0800'
and to_char(dt, 'hh24mi') > '0700'
)
group by to_char(dt, 'yyyymmdd')
DAY_ | HOURS
:------- | :----------------------------------------------------------------
20220418 | 09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220419 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220420 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220421 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220422 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
20220423 | 01:30,03:30,05:30,09:30,11:30,13:30,15:30,17:30,19:30,21:30,23:30
db<>fiddle here
(And what if sysdate isn't Monday?)
Therefore, could you explain a little bit better what is the input (dates? One date? SYSDATE?) and what is desired output (related to that input).
Basically, I don't understand what you want. Meanwhile, errors you made (if it'll help).
Format model is wrong; this is what you did:
SQL> select to_char(sysdate, 'DAY') day, length(to_char(sysdate, 'DAY')) len from dual;
DAY LEN
--------- ----------
FRIDAY 9
"FRIDAY" doesn't have 9 characters; it has 6 of them --> use the fm format modifier (it'll truncate trailing spaces):
SQL> select to_char(sysdate, 'fmDAY') day, length(to_char(sysdate, 'fmDAY')) len from dual;
DAY LEN
--------- ----------
FRIDAY 6
SQL>
Today (22.04.2022) is Friday. Your query searches for data whose day is between "MONDAY" and "SATURDAY". As you're comparing strings and alphabet goes as [A, B, ..., F, G, ..., M, N, ..., S, T], "F(riday)" is NEVER between M(onday) and S(aturday) so there's zero chance that it'll work.
As of hours: which time exactly is between 08:01 and 08:00? Time doesn't go backwards (unless you meant "08:01 today and 08:00 tomorrow").
if sysdate is Monday I have to get the dates from Monday to Saturday and Hours Between Morning 08:00:00 AM to Next Day Morning 07:00:00 AM.
You can find whether SYSDATE is Monday by comparing the day to the start of the ISO week (which will always be midnight on Monday):
SELECT *
FROM DUAL
WHERE SYSDATE - TRUNC(SYSDATE, 'IW') < 1
You can find out whether the hours are between 08:00 and 07:00 the next day by subtracting 8 hours and finding out whether the time is between 00:00 and 23:00:
SELECT *
FROM DUAL
WHERE (SYSDATE - INTERVAL '8' HOUR) - TRUNC(SYSDATE - INTERVAL '8' HOUR)) DAY TO SECOND
<= INTERVAL '23' HOUR;
You can combine the two to find out if the day is between Monday and Saturday and the time is between 08:00 and 07:00 on the next day (so for Saturday, it would include 7 hours of Sunday) using:
SELECT *
FROM DUAL
WHERE (SYSDATE - INTERVAL '8' HOUR) - TRUNC(SYSDATE - INTERVAL '8' HOUR), 'IW') < 6
AND (SYSDATE - INTERVAL '8' HOUR) - TRUNC(SYSDATE - INTERVAL '8' HOUR)) DAY TO SECOND
<= INTERVAL '23' HOUR;
Note: This does not use TO_CHAR so it is unaffected by any changes to the NLS_TERRITORY or NLS_DATE_LANGUAGE session parameters so it will always give the same answer (independent of the settings of the user who runs the query).
You can use such a combination
SELECT TO_CHAR(dt,'HH24:MI:SS','NLS_DATE_LANGUAGE=English') AS Hour,
TO_CHAR(dt,'Day','NLS_DATE_LANGUAGE=English') AS day
FROM t
WHERE TO_CHAR(dt,'Dy','NLS_DATE_LANGUAGE=English') IN ('Tue','Wed','Thu','Fri')
AND TO_CHAR(dt, 'HH24:MI:SS') NOT BETWEEN '07:00:01' AND '08:00:00'
OR TO_CHAR(dt,'Dy','NLS_DATE_LANGUAGE=English') = 'Mon'
AND TO_CHAR(dt, 'HH24:MI:SS')>= '07:00:00'
OR TO_CHAR(dt,'Dy','NLS_DATE_LANGUAGE=English') = 'Sat'
AND TO_CHAR(dt, 'HH24:MI:SS')<= '08:00:00'
where needs to consider restricting the periods for the bound dates individually
Demo
Does anyone have a easy solution to make a numbering from sunday to saturday and generate the dates in PostgreSQL(version 11).I have the below solution but it is limited to only 5 weeks and i need something that is flexible.
I have dates as a column in my source table, i want those dates to be numbered from saturday to sunday like below.
Current Query
WITH CTE AS
(
SELECT 1 as rno,generate_series( date_trunc('week', current_date)::date - 1
, date_trunc('week', current_date)::date + 5
, interval '1 day') current_week
)
,CTE_1 AS
(
SELECT rno,current_week FROM CTE
UNION
select 2,dt::date d from generate_series( (SELECT MIN(current_week)::DATE FROM CTE)- interval '7 days', (SELECT MIN(current_week)::DATE FROM CTE)- interval '1 days', interval '1 days') dt
)
,CTE_2 AS
(
SELECT rno,current_week FROM CTE_1
UNION
select 3,dt::date d from generate_series( (SELECT MIN(current_week)::DATE FROM CTE_1)- interval '7 days', (SELECT MIN(current_week)::DATE FROM CTE_1)- interval '1 days', interval '1 days') dt
)
,CTE_3 AS
(
SELECT rno,current_week FROM CTE_2
UNION
select 4,dt::date d from generate_series( (SELECT MIN(current_week)::DATE FROM CTE_2)- interval '7 days', (SELECT MIN(current_week)::DATE FROM CTE_2)- interval '1 days', interval '1 days') dt
)
,last_5_weeks as
(
SELECT rno,current_week FROM CTE_3
UNION
select 5,dt::date d from generate_series( (SELECT MIN(current_week)::DATE FROM CTE_3)- interval '7 days', (SELECT MIN(current_week)::DATE FROM CTE_3)- interval '1 days', interval '1 days') dt
)
SELECT rno,current_week::DATE as selected_date FROM last_5_weeks order by selected_date DESC
Current output
rno Date
1 "2020-10-24"
1 "2020-10-23"
1 "2020-10-22"
1 "2020-10-21"
1 "2020-10-20"
1 "2020-10-19"
1 "2020-10-18"
2 "2020-10-17"
2 "2020-10-16"
2 "2020-10-15"
2 "2020-10-14"
2 "2020-10-13"
2 "2020-10-12"
2 "2020-10-11"
3 "2020-10-10"
3 "2020-10-09"
3 "2020-10-08"
3 "2020-10-07"
3 "2020-10-06"
3 "2020-10-05"
3 "2020-10-04"
4 "2020-10-03"
4 "2020-10-02"
4 "2020-10-01"
4 "2020-09-30"
4 "2020-09-29"
4 "2020-09-28"
4 "2020-09-27"
5 "2020-09-26"
5 "2020-09-25"
5 "2020-09-24"
5 "2020-09-23"
5 "2020-09-22"
5 "2020-09-21"
5 "2020-09-20"
How about using arithmetics?
select 1 + (row_number() over(order by dt desc) - 1) / 7 rn, dt::date dt
from generate_series(
date_trunc('week', current_date)::date + 5 - interval '5 week -1 day',
date_trunc('week', current_date)::date + 5,
'1 day'
) s(dt)
order by dt desc
generate_series() produces all dates at once. You control the number of weeks that are generated with the value given to week in the literal interval. Then, in the outer query, we use row_number() to enumerate the week numbers.
Demo on DB Fiddle
One sequence for weeks and another one for days. It is flexible, 5 is a parameter. date_trunc('week',now()+'P1W'::interval)::date-2 is this week's saturday.
select
w rno,
date (date_trunc('week',now()+'P1W'::interval)::date-2 + make_interval(weeks => 1-w, days => 1-d)) "Date"
from generate_series(1, 5, 1) w
cross join generate_series(1, 7, 1) d;
I am trying to get the last 31st August every year dynamically.
E.g if current date is today I would like to get 31st August 2019
next year, and I want this to be dynamic and get 31st August 2020?
I have tried Date_Sub and Date_Trunc and they are not working. Any ideas would be really helpful?
SELECT DATE_SUB(current_date(), INTERVAL 5 DAY) as five_days_ago
Below will always return last /latest August 31st
#standardSQL
SELECT IF(CURRENT_DATE() < last_august_31, DATE_SUB(last_august_31, INTERVAL 1 YEAR), last_august_31) AS last_august_31
FROM UNNEST([DATE(EXTRACT(YEAR FROM CURRENT_DATE()), 8, 31)]) last_august_31
In case if you need to use this within the query with date field - consider below example
#standardSQL
WITH `project.dataset.table` AS (
SELECT DATE '2019-01-01'dt UNION ALL
SELECT '2019-12-31' UNION ALL
SELECT CURRENT_DATE()
)
SELECT dt, IF(dt < last_august_31, DATE_SUB(last_august_31, INTERVAL 1 YEAR), last_august_31) AS last_august_31
FROM `project.dataset.table`,
UNNEST([DATE(EXTRACT(YEAR FROM dt), 8, 31)]) last_august_31
-- ORDER BY dt
with result
Row dt last_august_31
1 2019-01-01 2018-08-31
2 2019-12-31 2019-08-31
3 2020-02-25 2019-08-31
with dates as (
select cast('2019-01-01' as date) as my_date union all select '2019-12-31' union all select current_date()
)
select
my_date,
date(extract(year from my_date) - case when extract(month from my_date) < 9 then 1 else 0 end, 8, 31) as prev_aug_31,
date(extract(year from my_date) + case when extract(month from my_date) >= 9 then 1 else 0 end, 8, 31) as next_aug_31
from dates
I need to select all dates, from current date, with a 30 days jump between each other in the past.
In example, today is October 18th, so I would need to get September 18th , August 19th, July 20th.
I cannot simply write the function current_date - 30, current_date - 60, current_date - 90 because the beginning of the data is far.
If you are using Oracle you can use below code -
WITH DATES(CUR_DATE, MON) AS (SELECT CURRENT_DATE CUR_DATE, 1 MON FROM DUAL
UNION ALL
SELECT D.CUR_DATE - 30, D.MON + 1
FROM DATES D
WHERE TO_CHAR(D.CUR_DATE, 'MM') > 1
AND TO_CHAR(D.CUR_DATE, 'YY') = TO_CHAR(SYSDATE, 'YY'))
SELECT * FROM DATES
Here is the fiddle. For other DBs please post your DBMS product.
If you are using Postgres this is quite easy using generate_series()
select d::date as "date"
from generate_series(current_date,
current_date - interval '1 year',
interval '-1 month') as t(d)
That returns the "last 12 months" starting from today.
If today is 2019-10-18, this returns:
date
----------
2019-10-18
2019-09-18
2019-08-18
2019-07-18
2019-06-18
2019-05-18
2019-04-18
2019-03-18
2019-02-18
2019-01-18
2018-12-18
2018-11-18
2018-10-18
If you want 30 days intervals (rather than 1 month), use:
select d::date as date
from generate_series(current_date,
current_date - interval '1 year',
interval '-30 day') as t(d)
For SQL Server, you can use the code below:
;WITH cte_Date30Days(n, d, dte)
AS (
SELECT 0, 0, current_timestamp as dte
UNION ALL
SELECT n+1, d - 30, dateadd(day, -30, dte)
FROM
cte_Date30Days
WHERE n < 7-1
)
SELECT dte FROM cte_Date30Days;
Only 7 rows are returned but change that value to suit your needs.
I am trying to change the start day of the week, but not able to achieve
currently its Monday to sunday, it should be made sunday- saturday
I tried using this query on the below dataset invoice date and invoice_week
SELECT invoice_date,
weekofyear(invoice_date) as invoice_week,
datesub(invoice_date,1),
weekofyear(datesub(invoice_date,1)) as invoice_week1
from table
I think instead of subtract the date by 1, you should add the date by 1:
select
invoice_date,
weekofyear(invoice_date) as invoice_week,
adddate(invoice_date,1),
weekofyear(adddate(invoice_date,1)) as invoice_week1
from(
select cast('2018-01-07 16:00:00' as timestamp) invoice_date
)stg
Found a solution which works. Given a date, here's the Date for the start of the week and end of the week if you want a Sunday - Saturday week, not a Monday - Sunday week as is default.
SET var:date=2019-01-06;
select to_date(IF( dayofweek(cast('${var:date}' as timestamp)) = 1, cast('${var:date}' as timestamp), trunc(cast('${var:date}' as timestamp), 'd') - interval 1 day)) as startOfWeek, to_date(IF( dayofweek(cast('${var:date}' as timestamp)) = 1, (trunc(cast('${var:date}' as timestamp) + interval 1 day, 'd') + interval 5 day), (trunc(cast('${var:date}' as timestamp), 'd') + interval 5 day) )) as endOfWeek;
+-------------+------------+
| startofweek | endofweek |
+-------------+------------+
| 2019-01-06 | 2019-01-12 |
+-------------+------------+