Can we minimize this big case statement in simplified one.
case
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 1 MONTH)) then 12
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 2 MONTH)) then 11
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 3 MONTH)) then 10
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 4 MONTH)) then 9
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 5 MONTH)) then 8
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 6 MONTH)) then 7
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 7 MONTH)) then 6
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 8 MONTH)) then 5
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 9 MONTH)) then 4
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 10 MONTH)) then 3
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 11 MONTH)) then 2
when EXTRACT(MONTH from GIVEN_DATE) = EXTRACT(MONTH from DATE_SUB(CURRENT_DATE(), interval 12 MONTH)) then 1
end as MONTH_NUMBER
Consider below approach
select given_date, 1 + mod(extract(month from given_date) + 5, 12) as MONTH_NUMBER
If to apply to some dummy dates - output is
Are you looking for something like this?
select 1 + date_diff(current_date(), given_date, month)
This makes a lot of assumptions, such as given_date being within a year. If not, then modulo arithmetic probably does what you want:
select 1 + (date_diff(current_date(), given_date, month) % 12)
Related
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))
enter image description hereI have a dates column that I am trying to group in to specific groups. Some dates have passed other dates are in the future. I want to have group them by 1-30, 31- 59, 60 - 89, +90, so there would be 8 groups total for both those that have passed and those that are on the horizon. Below is what I have wrote so far, but I feel like I am over complicating and the more tweaks I make the more incorrect it becomes. Any insight is appreciated!
CASE WHEN DATEDIFF(CURRENT_DATE(),`date`) >= 90 THEN '90 days past'
WHEN DATEDIFF(CURRENT_DATE(),`date`) >= 60 THEN '60 days past'
WHEN DATEDIFF(CURRENT_DATE(),`date`) >= 30 THEN '30 days past'
WHEN DATEDIFF(`date`,CURRENT_DATE()) >= 90 THEN '90 days future'
WHEN DATEDIFF(`date`,CURRENT_DATE()) >= 60 THEN '60 days future'
WHEN DATEDIFF(`date`,CURRENT_DATE()) >= 30 THEN 'Next 30 days'
ELSE 'Not Late'
END
I think it's okay.
I would just use the same datediff for each.
create table test (`date` date);
insert into test values
( date_add(current_date, interval -90 day) ),
( date_add(current_date, interval -89 day) ),
( date_add(current_date, interval -60 day) ),
( date_add(current_date, interval -59 day) ),
( date_add(current_date, interval -30 day) ),
( date_add(current_date, interval -29 day) ),
( date_add(current_date, interval 29 day) ),
( date_add(current_date, interval 30 day) ),
( date_add(current_date, interval 59 day) ),
( date_add(current_date, interval 60 day) ),
( date_add(current_date, interval 89 day) ),
( date_add(current_date, interval 90 day) )
select `date`,
CASE
WHEN DATEDIFF(`date`, CURRENT_DATE) <= -90 THEN '90 days past'
WHEN DATEDIFF(`date`, CURRENT_DATE) <= -60 THEN '60 days past'
WHEN DATEDIFF(`date`, CURRENT_DATE) <= -30 THEN '30 days past'
WHEN DATEDIFF(`date`, CURRENT_DATE) >= 90 THEN '90 days future'
WHEN DATEDIFF(`date`, CURRENT_DATE) >= 60 THEN '60 days future'
WHEN DATEDIFF(`date`, CURRENT_DATE) >= 30 THEN 'Next 30 days'
ELSE 'Not Late'
END as status
from test
date | status
:--------- | :-------------
2021-10-12 | 90 days past
2021-10-13 | 60 days past
2021-11-11 | 60 days past
2021-11-12 | 30 days past
2021-12-11 | 30 days past
2021-12-12 | Not Late
2022-02-08 | Not Late
2022-02-09 | Next 30 days
2022-03-10 | Next 30 days
2022-03-11 | 60 days future
2022-04-09 | 60 days future
2022-04-10 | 90 days future
db<>fiddle here
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 write a query in PostgreSQL that should return "month" wise results when two dates are given.
SELECT count(overall_sl) as total_sales, count(CASE WHEN overall_sl < value_1 THEN 1 END) failed_sales
FROM (
SELECT overall_sl, value_1
FROM salecombined c where c.date_updated between '2018-01-01' and '2018-12-31'
GROUP BY dept_name, date_updated, date, overall_sl, no_addons,
value_1, category_id, subcategory_id, branch_name
) sales;
I expect results to be like this (The above query doesn't do that)
start_dt end_dt total_sales failed_sales
-------- ------ ----------- -------------
2018-01-01 2018-01-31 0 0
2018-02-01 2018-02-28 589 154
2018-03-01 2018-03-31 107 14
2018-04-01 2018-04-30 375 114
-- and so on
I wrote below query but it is taking longer to execute; how can I optimise this?
SELECT date_trunc('month', dd):: date start_dt,
(date_trunc('month', dd::DATE) + interval '1 month' - interval '1 day')::date as end_dt,
(select count(overall_sl) from (
SELECT overall_sl
FROM salecombined c
WHERE c.date_updated between date_trunc('month', dd):: date and (date_trunc('month', dd::DATE) + interval '1 month' - interval '1 day')::date
GROUP BY dept_name, date_updated, date, overall_sl, no_addons,
category_id, subcategory_id, branch_name
) jobs
) total_sales,
(select count(CASE WHEN overall_sl < value_1 THEN 1 END) from (
SELECT overall_sl, value_1
FROM salecombined c
WHERE c.date_updated between date_trunc('month', dd):: date and (date_trunc('month', dd::DATE) + interval '1 month' - interval '1 day')::date
GROUP BY dept_name, date_updated, date, overall_sl, no_addons,
value_1, category_id, subcategory_id, branch_name
) jobs
) failed_sales
FROM generate_series
( '2018-01-01'::timestamp
, '2018-12-11'::timestamp
, '1 month'::interval) dd
I am not expert in SQL or PostgreSQL.
I am not quiet sure but maybe you are searching for GROUP BY date_trunc():
demo:db<>fiddle
SELECT
date_trunc('month', sdate)::date as month_begin,
(date_trunc('month', sdate) + interval '1 month -1 day')::date as month_end,
SUM(value)
FROM sales
WHERE sdate BETWEEN <start> and <end>
GROUP BY date_trunc('month', sdate)
date_trunc('month', date) converts the date to the first of the month. So you can group all dates within one month (because all dates are the first of a month in that group)
date_trunc('month', sdate) + interval '1 month -1 day' calculates the last day of a month (date_trunc to go to first, add one month to go the first of the next month, subtract one day to get to the day before the next first = last day of current month.)
Here is how the date is formatted:
XXXXXX001221
This date is for Dec 21
If I want to get yesterdays entries using the CURDATE() function how would I do this?
If you're just doing month-day (indices are based on the string format of CURDATE(), which is YYYY-MM-DD):
SELECT * FROM table_name
WHERE date_field LIKE
CONCAT('%', SUBSTR(DATE_SUB(CURDATE(), INTERVAL 1 DAY) FROM 6 FOR 2),
SUBSTR(DATE_SUB(CURDATE(), INTERVAL 1 DAY) FROM 9 FOR 2));
Or an alternative way using EXTRACT():
SELECT * FROM table_name
WHERE date_field LIKE
CONCAT('%', EXTRACT(MONTH FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)),
EXTRACT(DAY FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)));
For year as well:
SELECT * FROM table_name
WHERE date_field LIKE
CONCAT('%', SUBSTR(DATE_SUB(CURDATE(), INTERVAL 1 DAY) FROM 3 FOR 2),
SUBSTR(DATE_SUB(CURDATE(), INTERVAL 1 DAY) FROM 6 FOR 2),
SUBSTR(DATE_SUB(CURDATE(), INTERVAL 1 DAY) FROM 9 FOR 2));
SELECT * FROM table_name
WHERE date_field LIKE
CONCAT('%', EXTRACT(YEAR FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)),
EXTRACT(MONTH FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)),
EXTRACT(DAY FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)));
EDIT: Instead of WHERE date_field LIKE ... you can use WHERE RIGHT(date_field, 6) = ... as mentioned in codethis' answer:
SELECT * FROM table_name
WHERE RIGHT(date_field, 4) =
CONCAT(EXTRACT(MONTH FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)),
EXTRACT(DAY FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)));
SELECT * FROM table_name
WHERE RIGHT(date_field, 6) =
CONCAT(EXTRACT(YEAR FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)),
EXTRACT(MONTH FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)),
EXTRACT(DAY FROM DATE_SUB(CURDATE(), INTERVAL 1 DAY)));
NOTE: You can also use a variable to save calculating DATE_SUB(CURDATE(), INTERVAL 1 DAY) all the time.
SET #yesterday = DATE_SUB(CURDATE(), INTERVAL 1 DAY);
SELECT ...
Try something like:
SELECT * FROM table WHERE RIGHT(date_col, 6) = (SELECT CONVERT(VARCHAR(6), GETDATE()-1, 12) AS [YYMMDD])