I'm trying to query for dates that come the week before Labor Day. Labor Day is a US federal holiday on the first Monday of every September. I'm trying to select labor day and the full week before labor day. I'm somewhat close with this sort of query
select * from `bigquery-public-data.ghcn_d.ghcnd_1991`
where extract(month from date) = 9
and extract(dayofweek from date) = 2
and extract(week from date) = 36
But it's not always the 36th week, sometimes its the 35th (so the query above is wrong).
I'm guessing I'll have to do a date subtraction in order to get the full week before labor day...but for now I just need help finding how to query the first Monday of every September.
This is one approach:
select t.*
from `bigquery-public-data.ghcn_d.ghcnd_1991` t
join (
select format_date('%Y%m',date) as yr_mo,
date_sub(min(date), interval 1 week) as week_before,
min(date) as first_monday
from `bigquery-public-data.ghcn_d.ghcnd_1991` v
where extract(dayofweek from date) = 2
and extract(month from date) = 9
group by format_date('%Y%m',date)
) v
on t.date between v.week_before and v.first_monday;
This presumes you want all rows of the table where the date is Labor Day or within the week leading to Labor Day.
Because it is the first Monday of September you can perform a MIN on date within a CTE, or a subquery, to get Labor Day and the prior week:
WITH labor_day as (
select MIN(date) date
from `bigquery-public-data.ghcn_d.ghcnd_1991`
where extract(month from date) = 9
and extract(dayofweek from date) = 2
)
SELECT distinct ghcnd.date
FROM `bigquery-public-data.ghcn_d.ghcnd_1991` ghcnd
INNER JOIN labor_day
on ghcnd.date between labor_day.date-7 and labor_day.date
Related
I am trying to Calculate MTD sales on daily sales number but my month starts from 26th of previous month to 25th of next month.
data contains only 3 columns (date, vendor_id, total_sales).
Below Code is working fine for calculating months starting from the 1st. I tried to do this by using the below approaches but it does not works
date(date - interval '25 day') : Not working
Mapping table creation for each day, but will not work for 30/31 days month
Need suggestion on above.
SUM(sales) OVER (
PARTITION BY
vendor_id,
EXTRACT(YEAR FROM date),
EXTRACT(MONTH FROM date)
ORDER BY
date ROWS UNBOUNDED PRECEDING
) AS mtd_total_sales,
So, if the date is >= 26, then "it is the next month".
As a result, something like
CASE
WHEN EXTRACT(DAY FROM date) >= 26 THEN ADD_MONTHS(date, 1)
ELSE date
END
should suffice, since you extract the year and month anyway.
I am trying to find an easy way to exclude partial weeks from the results.
What I have so far:
WITH a AS (SELECT
FORMAT_DATE("%G-%V", created_date) as report_week
, created_date
, FORMAT_DATE('%A', created_date) AS day
, emp_id
, ROUND(SAFE_DIVIDE(SUM(working_time),3600),2) as hours
FROM `table1` a
WHERE created_date >= current_date()-10
GROUP BY 1,2,3,4,5)
SELECT
report_week
, emp_id
, hours
FROM a
WHERE day LIKE '%Monday%'
GROUP BY 1,2,3
ORDER BY report_week ASC
Input:
report_week: conversion of employee's shift date into week
created_date: date of employee's shift
day: conversion of date of employee's shift into day of week (Monday, Tuesday..)
emp_id: the employee's ID
hours: Number of worked hours by the employee
if current_date is 19 April 2022 then current_date()-10 is 9 April 2022.
Output:
The desired output is to return the number of hours worked for each employee during the full week 11 - 17 April only (it would exclude 9th, 10th, 18th and 19th of April from the results).
To obtain this, I tried to filter by having only week starting on a Monday with WHERE day LIKE '%Monday%' but in the example, it would also return the number of hours worked for each employee on 18th and 19th (since the 18th is a Monday). And if I combine this clause with AND (for example WHERE day LIKE '%Monday%' AND day LIKE '%Sunday%', it does not work at all.
Additionally, I see here another potential problem. If a Monday is a day off (like during Easter), then no employees will have hours on that Monday and the rest of the week will then not be returned.
My question: Is there an easy way to get only full weeks (Monday-Sunday) regardless the date range chosen?
Thank you in advance.
Best,
Fabien
You need to use UNNEST and create an array with a range of dates. Also, you need to use DATE_TRUNC to get the week and LAST_DAY to get the last day of the week. You can get the weeks that belong to each day in a range of dates.
You can see this example.
with sample_data as (
SELECT date FROM UNNEST(generate_timestamp_array('2022-04-09 00:00:00', '2022-04-19 00:00:00', INTERVAL 1 DAY)) as date
),
counting as(
select
DATE_ADD(DATE (date), INTERVAL 1 DAY) date
, DATE_TRUNC(DATE(date), WEEK)+1 week_start
, DATE_TRUNC(DATE(date), WEEK) +7 week_end
from sample_data
)
select b.date from (
select week_start,count(*) as ndays
from counting
group by week_start
having ndays=7
) a
join counting b on a.week_start=b.week_start
where timestamp(b.date) between timestamp(b.week_start) and timestamp(b.week_end)
I used the same range of dates like your example.
I work with a hotel client where they have a BigQuery database which has hotel booking data. I've shared the relevant columns in the image below which list the names of each hotel, the arrival date of the guest, the departure date, and the revenue generated from the each booking:
My problem statement is that I have to showcase how many rooms have been booked, and how much revenue has been made for each hotel every month where my final grid would look similar to this:
The important points to remember are:
the depart_dt - arrival_dt are the number of nights that the guest is staying
the Rez_rate_total / (depart_dt - arrival_dt) is the revenue made per night
My problem here is trying to figure out how to change the start date and end date columns into groups of months. The challenge comes when a guest arrives in one month and leaves in the next month. For example, Row 5 in the original data has the guest coming in on 18th July and leaving on 1st Aug - so 13 days of his stay and 13 days of revenue has to be included in July and 1 day has to be included in August.
I haven't used SQL in a while so this is as far as I got:
WITH
temp_table AS (
SELECT
hotel_long_nm,
arrival_dt,
depart_dt,
DATE_DIFF(depart_dt, arrival_dt, day) AS room_nights,
rez_rate_total
FROM
`DATABASE.analytics.bookings` )
SELECT
*
FROM
temp_table
Any help would be greatly appreciated!
Consider the following approach:
with bookings as (
select hotel_long_nm, date(arrival_dt) as arrival_dt, date(depart_dt) as depart_dt, rez_rate_total from project.dataset.bookings
),
tmp as (
-- expose the dates in the reservation (excluding last day of reservation)
select *, generate_date_array(arrival_dt,date_sub(depart_dt, interval 1 day)) as stay_dates from bookings
),
calc as (
-- unnest and calculate the daily rate
select
hotel_long_nm,
stay_dt,
1 as stay_nights,
rez_rate_total/array_length(stay_dates) as rez_rate_daily
from tmp
left join unnest(stay_dates) as stay_dt
),
agg as (
-- aggregate to the year-month level
select
date_trunc(stay_dt, month) as year_month,
hotel_long_nm,
sum(stay_nights) as room_nights,
round(sum(rez_rate_daily),2) as rez_rate_total
from calc
group by 1,2
)
select * from agg
order by hotel_long_nm, year_month
You can consider this approach, following this logic.
Validate if both dates are in the same month
If are not in the same month, i get the final date of the month of
arrival date and subtract both dates
I get the first date of the month of the depart date and subtract
and subtract both dates
In this code you can see an example:
SELECT
/*arrival date*/
CURRENT_DATE() AS the_arival,
/*depart_dt*/
DATE_ADD(CURRENT_DATE(), INTERVAL 30 DAY) AS the_depart,
/*total of night between arrival date and depart date*/
DATE_DIFF(DATE_ADD(CURRENT_DATE(), INTERVAL 30 DAY) , CURRENT_DATE(), DAY) AS total_room_nights,
/* validate if the dates are in the same month or different month if equal 0 same month if >0 another month */
DATE_DIFF(DATE_ADD(CURRENT_DATE(), INTERVAL 30 DAY) , CURRENT_DATE(), MONTH) AS Same_Month,/*1 no and 0 yes/
/*in this case are in different month*/
/*I get the final date of the arrival month and subtract with the arrival date*/
DATE_DIFF(DATE_SUB(DATE_TRUNC(DATE_ADD(DATE_ADD(CURRENT_DATE(), INTERVAL 30 DAY), INTERVAL 1 MONTH), MONTH), INTERVAL 1 DAY),DATE_ADD(CURRENT_DATE(), INTERVAL 30 DAY), DAY) as total_room_nights_first_mont,
/*I get the initial date of the depart month and subtract with the depart date i add +1 because is the night between last day of the mont and first day of the next month*/
DATE_DIFF(DATE_ADD(CURRENT_DATE(), INTERVAL 30 DAY),DATE_TRUNC(DATE_ADD(CURRENT_DATE(), INTERVAL 30 DAY), MONTH), DAY)+1 as total_room_nights_second_month
You can see more information about the date function.Click Here.
Here is the code I am using to display the last date of the month
SELECT * FROM Calendar_table
WHERE date in (SELECT MAX(date) from Calendar_table
ORDER BY YEAR,
DATE);
You can use to_char() to extract the day of the month from the date, and use it for filtering:
select *
from calendar_table
where to_char(dt, 'dd') in ('15', '30')
This assumes that the calendar date is stored in column dt.
You can also use extract():
select *
from calendar_table
where extract(day from dt) in (15, 30)
On the other hand, if you want the 15th of the month and the last day of the month, then:
select *
from calendar_table
where extract(day from dt) = 15 or last_day(dt) = dt
Few days back I have answered a question related to last day handling may be you can use it for the same:
https://stackoverflow.com/a/63134303/10908274
I've been trying for hours now to write a date_trunc statement to be used in a group by where my week starts on a Friday and ends the following Thursday.
So something like
SELECT
DATE_TRUNC(...) sales_week,
SUM(sales) sales
FROM table
GROUP BY 1
ORDER BY 1 DESC
Which would return the results for the last complete week (by those standards) as 09-13-2019.
You can subtract 4 days and then add 4 days:
SELECT DATE_TRUNC(<whatever> - INTERVAL '4 DAY') + INTERVAL '4 DAY' as sales_week,
SUM(sales) as sales
FROM table
GROUP BY 1
ORDER BY 1 DESC
The expression
select current_date - cast(cast(7 - (5 - extract(dow from current_date)) as text) || ' days' as interval);
should always give you the previous Friday's date.
if by any chance you might have gaps in data (maybe more granular breakdowns vs just per week), you can generate a set of custom weeks and left join to that:
drop table if exists sales_weeks;
create table sales_weeks as
with
dates as (
select generate_series('2019-01-01'::date,current_date,interval '1 day')::date as date
)
,week_ids as (
select
date
,sum(case when extract('dow' from date)=5 then 1 else 0 end) over (order by date) as week_id
from dates
)
select
week_id
,min(date) as week_start_date
,max(date) as week_end_date
from week_ids
group by 1
order by 1
;