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;
Related
I have the following schema:
CREATE TABLE phone_clicks (
id integer NOT NULL,
start_date date NOT NULL,
industry_id integer NOT NULL,
clicks integer DEFAULT 0 NOT NULL
);
insert into phone_clicks(id, start_date, industry_id)
values
(1, '2021-10-03', 1),
(2, '2021-10-03', 2),
(3, '2021-10-02', 3),
(4, '2021-10-01', 1),
(5, '2021-11-01', 3),
(6, '2021-11-01', 4),
(7, '2021-11-02', 1),
(8, '2021-11-02', 2);
and i want to get a return object that has the industry id along with the number of items but based on the calendar month for the previous two months, not including the current. So i want the number of items in October and the number in November.
What i have, that is not working
SELECT industry_id,
sum(
CASE
WHEN start_date >= (date_trunc('month', start_date) - interval '1 month')::date AND start_date < (date_trunc('month', start_date) - interval '2 month')::date THEN 1
ELSE 0
END) AS last_month,
sum(
CASE
WHEN start_date >= (date_trunc('month', start_date) - interval '2 month')::date AND start_date <= (date_trunc('month', start_date) - interval '3 month')::date THEN 1
ELSE 0
END) AS prev_month
FROM phone_clicks
Group by industry_id
What i am currently getting from the above :
industry_id last_month prev_month
4 0 0
1 0 0
3 0 0
2 0 0
What i am expecting:
industry_id last_month prev_month
4 1 0
1 1 2
3 1 1
2 1 1
So industry 1, it has two items with start_date in October and 1 in November.
date_trunc('month', ...) always returns the first of the month, so you don't really need the >= .. <= in your CASE expression.
It's enough to compare the month start of "start_date" with the month start of last month, which is date_trunc('month', current_date - interval '1 month')
The query can also be simplified a bit by using the filter operator.
SELECT industry_id,
count(*) filter (where date_trunc('month', start_date) = date_trunc('month', current_date - interval '1 month')) as last_month,
count(*) filter (where date_trunc('month', start_date) = date_trunc('month', current_date - interval '2 month')) as prev_month
FROM phone_clicks
group by industry_id
order by industry_id
How can I count the days of each week for a specific month that the user will give to my SQL query? For example, if the user gives April 2021, the result will be:
If the user gives May 2021 the result will be:
Here is a direct computation of the same. The input is given as a string, such as May 2021; you can use a bind variable in its place. Just keep in mind the possibility that the user may be in a non-English-speaking locale; as long as they use their local language in passing the month to the query, everything should work fine.
with
inputs (mth) as (select 'May 2021' from dual)
, first_day (dt) as (select to_date(mth, 'fmMonth yyyy') from inputs)
, mondays (dt, ord, lst) as (
select trunc(dt, 'iw') + 7 * (level - 1), level, max(level) over ()
from first_day
connect by level <= 1 + (trunc(add_months(dt, 1), 'iw') - trunc(dt, 'iw')) / 7
)
select to_number(to_char(dt, 'iw')) as week_number,
case ord when 1 then dt + 7 - trunc(dt + 7, 'mm')
when lst then last_day(dt) + 1 - dt
else 7 end as week_days
from mondays
order by week_number
;
WEEK_NUMBER WEEK_DAYS
----------- ----------
17 2
18 7
19 7
20 7
21 7
22 1
I think this is the query you are looking for:
SELECT WEEK AS WEEK_NUMBER, COUNT(*) AS WEEK_DAYS
FROM (SELECT TO_CHAR(FIRST_DAY + (LEVEL-1), 'IW') AS WEEK
FROM (SELECT supplied_date AS FIRST_DAY, LAST_DAY(supplied_date) - SUPPLIED_DATE+1 AS DAYS
FROM (SELECT TO_DATE('05/2021', 'MM/YYYY') AS SUPPLIED_DATE
FROM DUAL))
CONNECT BY LEVEL <= DAYS)
GROUP BY WEEK
ORDER BY WEEK;
Just replace the inner TO_DATE with your date.
Edit 1: Additional Column (See Comments)
SELECT MONTH_NAME, WEEK AS WEEK_NUMBER, COUNT(*) AS WEEK_DAYS
FROM (SELECT TO_CHAR(FIRST_DAY, 'MONTH') AS MONTH_NAME, TO_CHAR(FIRST_DAY + (LEVEL-1), 'IW') AS WEEK
FROM (SELECT supplied_date AS FIRST_DAY, TO_CHAR(LAST_DAY(supplied_date), 'DD') AS DAYS
FROM (SELECT TO_DATE('05/2021', 'MM/YYYY') AS SUPPLIED_DATE
FROM DUAL))
CONNECT BY LEVEL <= DAYS)
GROUP BY MONTH_NAME, WEEK
ORDER BY WEEK;
Note: I also changed the way I calculate the number of days in the month.
You can generate the weeks (without needing to use any aggregation) using:
WITH input ( month ) AS (
SELECT DATE '2021-04-01' FROM DUAL
),
weeks_of_month ( month_start, week_start, end_day ) AS (
SELECT TRUNC( month, 'MM' ),
TRUNC( month, 'IW' ),
LAST_DAY( TRUNC( month ) )
FROM input
UNION ALL
SELECT month_start,
week_start + INTERVAL '7' DAY,
end_day
FROM weeks_of_month
WHERE week_start + INTERVAL '7' DAY <= end_day
)
SELECT TO_CHAR( week_start, 'IW' ) AS iso_week,
GREATEST( month_start, week_start ) AS first_day_of_week,
LEAST( end_day, week_start + INTERVAL '6' DAY ) AS last_day_of_week,
LEAST( end_day + 1, week_start + INTERVAL '7' DAY )
- GREATEST( month_start, week_start ) AS days
FROM weeks_of_month;
Which outputs (with the NLS_DATE_FORMAT set to YYYY-MM-DD (DY)):
ISO_WEEK
FIRST_DAY_OF_WEEK
LAST_DAY_OF_WEEK
DAYS
13
2021-04-01 (THU)
2021-04-04 (SUN)
4
14
2021-04-05 (MON)
2021-04-11 (SUN)
7
15
2021-04-12 (MON)
2021-04-18 (SUN)
7
16
2021-04-19 (MON)
2021-04-25 (SUN)
7
17
2021-04-26 (MON)
2021-04-30 (FRI)
5
db<>fiddle here
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 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])