Aggregate by week using (Sun - Sat) - sql

SELECT
CONCAT(CAST(WEEK(CAST(date as DATE)) as VARCHAR), ' - WEEK') as week,
COUNT(DISTINCT(field1)) as field1_count,
SUM(amount) as amount
FROM table
WHERE SUBSTR(DATE, 1, 4) = '2020'
GROUP BY 1
ORDER BY 1
Is there a way to ensure that the aggregate here is Sun - Sat? This is currently aggregating at Mon - Sun

Add one day to the date:
SELECT CONCAT(CAST(WEEK(CAST(date + INTERVAL '1' DAY as DATE)) as VARCHAR), ' - WEEK') as week,

Related

Find out last month's data when rolling into a new year

Currently, I am using this code to look at the previous month's data for quicksight in Amazon's Athena (this first part works*):
SELECT month, count(1)
FROM table1
WHERE CAST(EXTRACT(month from now()) - 1 as VARCHAR(2)) = month
GROUP BY month
The challenge is how to ensure that this code will work once we roll over into a new year? I currently have
SELECT month, count(1)
FROM table1
WHERE CASE WHEN( month = '1' THEN month = '13'
ELSE month
END)
CAST(EXTRACT(month from now()) - 1 as VARCHAR(2)) = month
GROUP BY month
To clarify, month was input as a string, hence the CAST as VARCHAR(2) to get "01" through "12".
My thought process behind this was that if month = '01', then it reads it as '13', then extracts '1', equaling '12'. But not sure if that will work
You can use the date_add function to subtract one month from today:
SELECT DATE_ADD('month', -1, NOW())
Alternatively you can subtract an interval of one month to achieve the same results:
SELECT NOW() - INTERVAL '1' MONTH
In both cases you can then use MONTH(…) or EXTRACT(MONTH FROM …) to get the month number.
You seem to want:
where month = extract(month from now()) - 1 or
(extract(month from now()) = 1 and month = 12)

Extract Month in sql and adding 1

If I add 1 to extract(month from date), then does the result become 13 or 1 (January of next year)
I have the below code:
(extract(day from sysdate) >=1 and extract(month from sysdate)=12) and (targstartdate >= to_date(((extract(month from sysdate))|| '-1-' || (extract(year from sysdate)+1)) , 'mm-dd-yyyy') and targstartdate <= to_date(((extract(month from sysdate)+1)|| '-1-' ||(extract(year from sysdate)+1)) , 'mm-dd-yyyy')
You can use MOD
MOD(extract(month from sysdate)+1,12) + 1
If you are trying to get the month number of the next month, flip your logic. Instead of extracting the month number and adding 1, add one month to the date then extract the month. See the difference below. The second adds one month, then extracts the month of 1 (for January).
select extract( month from to_date('12/15/2017','MM/DD/YYYY')) + 1 from dual
union all
select extract (month from ADD_MONTHS(to_date('12/15/2017','MM/DD/YYYY'),1)) from dual;

postgreSQL: How Select the nearest date that is not null

I got a date that I want to find the all records in the past that got the same month and day.
The problem accrues when there is no such date in the same year. For example, the 29th February.
My goal is to get the nearest date from below the date that does not exist.
This is my currently query with the date 2012-02-29:
SELECT date, amount
FROM table_name
WHERE
EXTRACT(MONTH FROM date) = EXTRACT(MONTH FROM DATE('2012-02-29') )
AND EXTRACT(DAY FROM date) = EXTRACT(DAY FROM DATE('2012-02-29') )
AND date < '2012-02-29'
ORDER BY date DESC LIMIT 10;
If I understand correctly, you want one date per year with the property that that day is nearest to the given date.
I would suggest using distinct on:
select distinct on (date_trunc('year', date)) t.*
from table_name t
order by date_trunc('year', date),
abs(date_part('day, (date -
(date '2012-02-29' -
(extract(year from date '2012-02-29') - extract(year from date)) * interval '1 year'
)
)
)
)
);
EDIT:
An example of working code:
select distinct on (date_trunc('year', date)) t.*
from table_name t
order by date_trunc('year', date),
abs(date_part('day', date - (date '2012-02-29' -
((extract(year from date '2012-02-29') - extract(year from date)) * interval '1 year')
)
))

Weeks between two dates in Postgres

I want to calculate the number of weeks that a customer has been a customer. The goal is to compare this to the distinct number of weeks the customer has made a purchase to determine what % of weeks the customer made a purchase.
With timestamp customer.created_at what is the best way to find the number of weeks a customer has been a customer? Said differently, the difference in weeks between the current week and the week in which the customer signed up.
Calendar weeks starting Monday to be apples-to-apples with distinct calendar weeks a customer has made a purchase.
AFAIK this should work in all cases
-Your calendar week starts on Monday
-Both dates start at least from 05.01.1970 (Monday)
Postgres:
SELECT
(
extract(epoch from date_trunc('week',now()::date))
-
extract(epoch from date_trunc('week','2019-12-20'::date))
)
/604800
(where 604800 is the number of seconds in a week)
SELECT (EXTRACT(days FROM (now() - customer.created_at)) / 7)::int;
First find the difference between current time and customer's created_at time stamp.
Then you tell Postgres to give you the difference in days and then you can divide that by 7 and round it to an integer. Note that if you do not want an integer, you can cast appropriately.
Try this
select (CURRENT_DATE - customer.created_at)/7
This is the code I used. Works.
SELECT
(
(
(
CURRENT_DATE::DATE - (EXTRACT('dow' FROM CURRENT_DATE::DATE) * INTERVAL '1 day')
)::DATE -
(
customers.created_at::DATE - (EXTRACT('dow' FROM customers.created_at::DATE) * INTERVAL '1 day')
)::DATE
) / 7
) + 1
I think you can have that to get the calendar weeks
select to_char(i::date, 'IYYYIW') AS mdate from generate_series('2017-01-01', current_date, '1 week'::interval) i;
and to count
select count(to_char(i::date, 'IYYYIW')) from generate_series('2017-01-01', current_date, '1 week'::interval) i;
and you can replace the date with customer date
#zach's answer is (perhaps) valid, however, it is hard to follow.
I found these two procedures that abstract equivalent logic:
CREATE OR REPLACE FUNCTION first_of_week(date) returns date AS $$
SELECT ($1::date-(extract('dow' FROM $1::date)*interval '1 day'))::date;
$$ LANGUAGE SQL STABLE STRICT;
CREATE OR REPLACE FUNCTION weeks_in_range(date,date) returns int AS $$
SELECT ((first_of_week($2)-first_of_week($1))/7)+1
$$ LANGUAGE SQL STABLE STRICT;
Source:
https://gist.github.com/tkellen/2003309#file-gistfile1-sql-L17-L23
Example:
SELECT weeks_in_range('1900-01-01',CURRENT_DATE);
weeks_in_range
----------------
5854
(1 row)
Note, that the above example assumes Sunday to be the start of the week. Alternatively, if you want Monday as a start of the week, then use date_trunc, e.g.
CREATE OR REPLACE FUNCTION weeks_in_range(date,date) returns int AS $$
SELECT (((EXTRACT(DAY FROM (date_trunc('week', $2::date) - date_trunc('week', $1::date))))/7)+1)::int
$$ LANGUAGE SQL STABLE STRICT;
first_of_week is unnecessary in this case.
select ((in_FinishDate - in_StartDate) / 7 +
case
when ((date_part('dow', in_StartDate) = 6) and (date_part('dow', in_FinishDate) != 6)) or
((date_part('dow', in_FinishDate) = 0) and (date_part('dow', in_StartDate) != 0)) then 1
when (date_part('dow', in_StartDate) = 5) and (date_part('dow', in_FinishDate) in (1, 2, 3, 4)) then 1
when (date_part('dow', in_StartDate) = 4) and (date_part('dow', in_FinishDate) in (1, 2, 3)) then 1
when (date_part('dow', in_StartDate) = 3) and (date_part('dow', in_FinishDate) in (1, 2)) then 1
when (date_part('dow', in_StartDate) = 2) and (date_part('dow', in_FinishDate) in (1)) then 1
else 0
end)::int;
This should do the trick.
Rebuttal to #maleckicoa. The following demonstrates why one cannot user a constant of 52 weeks in a year for difference between dates spanning the Dec-to-Jan time frame.
Instead of hard coding Dec 2019 - Jan 2020 (which works just fine) the following generates the annual dates 29-Dec to 4-Jan for the period 2019 through 2030. In most the maleckicoa's algorithum works, but ...
with recursive annual_dates(dec_dt, jan_dt) as
( select make_date(2019,12,29), make_date(2020,01,04)
union all
select (dec_dt+interval '1 year')::date, (jan_dt+interval '1 year')::date
from annual_dates
where extract(isoyear from dec_dt)::integer < 2030
) --select * from annual_dates;
select dec_dt, jan_dt,
extract( isoyear from jan_dt)*52 + extract( week from jan_dt)
-
extract( isoyear from dec_dt)*52 - extract( week from dec_dt) "Weeks Between"
from annual_dates;
with recursive annual_dates(dec_dt, jan_dt) as
( select make_date(2019,12,29), make_date(2020,01,04)
union all
select (dec_dt+interval '1 year')::date, (jan_dt+interval '1 year')::date
from annual_dates
where extract(isoyear from dec_dt)::integer < 2030
) --select * from annual_dates;
select 'Week for ' || dec_dt || ' is ' || extract( week from dec_dt) || ' And '
'Week for ' || jan_dt || ' is ' || extract( week from jan_dt) || ' Giving Difference of '
|| extract( isoyear from jan_dt)*52 + extract( week from jan_dt)
-
extract( isoyear from dec_dt)*52 - extract( week from dec_dt) || ' Weeks Between'
from annual_dates;
This should work in Postgres for most cases (Week starts with Monday, the year has 52 weeks)
Example for year end 2019:
select
extract( isoyear from '2020-01-04'::date)*52 + extract( week from '2020-01-04'::date)
-
extract( isoyear from '2019-12-29'::date)*52 - extract( week from '2019-12-29'::date)

Date split-up based on Fiscal Year

Given a From Date, To Date and a Fiscal Year system, I want to get all the split-up duration within the
given From & To Date based on the Fiscal Year system. Explained below with examples:
Example 1:
Fiscal Year system: Apr to Mar
From Date: Jan-05-2008
To Date: May-15-2008
Based on Fiscal Year system, duration should be splitted into:
Jan-05-2008 to Mar-31-2008
Apr-01-2008 to May-15-2008
Example 2:
Fiscal Year system: Apr to Mar
From Date: Jan-17-2008
To Date: May-20-2009
Based on Fiscal Year system, duration should be splitted into:
Jan-17-2008 to Mar-31-2008
Apr-01-2008 to Mar-31-2009
Apr-01-2009 to May-20-2009
Am looking for approach/algorithm to solve this in PostgreSQL 8.2.
Regards,
Gnanam
I actually favor Andomar's solution (with the addition of a processes that automatically fills the Periods table), but for fun here's a solution that doesn't require it.
CREATE TABLE your_table (start_date date, end_date date);
INSERT INTO your_table VALUES ('Jan-17-2008', 'May-20-2009');
SELECT
GREATEST(start_date, ('04-01-'||series.year)::date) AS year_start,
LEAST(end_date, ('03-31-'||series.year + 1)::date) AS year_end
FROM
(SELECT
start_date,
end_date,
generate_series(
date_part('year', your_table.start_date - INTERVAL '3 months')::int,
date_part('year', your_table.end_date - INTERVAL '3 months')::int)
FROM your_table) AS series(start_date, end_date, year)
ORDER BY
start_date;
You could create a table containing the start and end of all fiscal years, f.e.
Periods (PeriodStartDt, PeriodEndDt)
Then you can join the tables together if they at least partly overlap. Use a case statement to select the end of the period or the end of the row, depending on which is later. For example (not tested):
select case when yt.StartDt < p.PeriodStartDt then p.PeriodStartDt
else yt.StartDt
end as SplitStart
, case when yt.EndDt > p.PeriodEndDt then p.PeriodEndDt
else yt.EndDt
end as SplitEnd
, yt.*
from YourTable yt
inner join Periods p
on yt.StartDt < p.PeriodEndDate
and yt.EndDt >= p.PeriodStartDate
CREATE TABLE your_table (start_date date, end_date date);
INSERT INTO your_table VALUES (CONVERT (date, GETDATE()),CONVERT (date, DATEADD(year, -1, GETDATE())) );
WITH mycte AS
(
SELECT 1 as id
UNION ALL
SELECT id + 1
FROM mycte
WHERE id + 1 < = 12
),
cte_distribution as
(
SELECT *,
DATEPART (month,DATEADD(month, mycte.id - 1, your_table.start_date)) as month_number ,
DATEPART (YEAR,DATEADD(month, mycte.id - 1, your_table.start_date)) as cal_year,
12000/12 as cash
FROM your_table
CROSS JOIN mycte
)
select
*,
(CASE WHEN month_number between 1 and 3 THEN '1st quarter' WHEN month_number between 4 and 6 THEN '2nd quarter' WHEN month_number between 7 and 9 THEN '3rd quarter' WHEN month_number between 9 and 12 THEN '4th quarter' END) as Quarter,
CASE WHEN month_number between 1 and 6 THEN CAST(CAST((cal_year - 1) as CHAR(4)) + '-' + CAST(cal_year as CHAR(4)) AS CHAR(9)) WHEN month_number between 6 and 12 THEN CAST(CAST((cal_year) as CHAR(4)) + '-' + CAST((cal_year + 1) as CHAR(4)) AS CHAR(9)) ELSE NULL END as fin_year
from cte_distribution;