How to get the week start and end dates? - sql

I have variable called WeekBeginDate and I want only to pull data for that week. For example, if the beginning of the week date is 07/21/2014 which is Monday in this case, then I want only to pull the data from 07/21/2014 to 7/27/2014.
The variable will always contain the date for the beginning of the week only but I don’t have the date for the end of the week.
The week begins on Monday and ends on Sunday. I can’t figure out how to calculate or sum the number of hours if I only have the date for the beginning of week.
SELECT DT, sum (TOT_HOURS)as TOT_HOURS FROM MYTABLE where DT >= #WeekBeginDate and <=#WeekEndDate group by DT
Note, that I only have the variable for the WeekBeginDate.

just modify your table columns in this CTE it may works :
;WITH workhours AS
(
SELECT DATEADD(DAY
, -(DATEPART(dw, DT) -1)
, DT) AS week_start
, DATEADD(DAY
, 7 - (DATEPART(dw, DT))
, DT) AS week_end
FROM MYTABLE
)
SELECT week_start
, week_end
, SUM(TOT_HOURS) total_hrs_per_week
FROM workhours
GROUP BY week_start
, week_end

You may need to add 6 days to the beginning of the week
and group by something else if you need total weekly hours, i'm calling it "id".
not by dt (or don't group at all if it is a total for the whole table):
SELECT id, DT, sum (TOT_HOURS)as TOT_HOURS FROM MYTABLE
where DT BETWEEN #WeekBeginDate and DATEADD(d,6,#WeekBeginDate)
GROUP BY id

This should be of some use to you. I am casting to date so the 24 hrs of day is considered.
DECLARE #WeekBeginDate DATETIME
SET #WeekBeginDate = '2014-07-28 12:08:31.633';
WITH MYTABLE (DT,TOT_HOURS)
AS (
SELECT '2014-06-27 00:08:31.633',5 UNION ALL
SELECT '2014-07-27 00:08:31.633',5 UNION ALL
SELECT '2014-07-28 00:08:31.633',1 UNION ALL
SELECT '2014-07-29 00:08:31.633',1 UNION ALL
SELECT '2014-07-30 00:08:31.633',1 UNION ALL
SELECT '2014-07-31 00:08:31.633',1 UNION ALL
SELECT '2014-08-01 00:08:31.633',1 UNION ALL
SELECT '2014-08-02 00:08:31.633',1 UNION ALL
SELECT '2014-08-03 00:08:31.633',1
)
SELECT CAST(#WeekBeginDate AS DATE) AS StartDate,
DATEADD(d, 6, CAST(#WeekBeginDate AS DATE)) AS EndDate,
SUM (TOT_HOURS)AS TOT_HOURS
FROM MYTABLE
WHERE CAST(DT AS DATE) BETWEEN CAST(#WeekBeginDate AS DATE) AND DATEADD(d, 6, CAST(#WeekBeginDate AS DATE))

Just add 6 (or 7) days...
SELECT DT, sum (TOT_HOURS)as TOT_HOURS FROM MYTABLE
where DT BETWEEN #WeekBeginDate and #WeekBeginDate + 6 group by DT

select #weekBeginDate = DATEADD(wk, DATEDIFF(wk,0,GETDATE()), 0)
select #WeekEndDate = DATEADD(dd, 6, DATEADD(wk, DATEDIFF(wk,0,GETDATE()), 0))
SELECT DT, sum (TOT_HOURS)as TOT_HOURS FROM MYTABLE where DT >= #WeekBeginDate and <=#WeekEndDate group by DT

Here is where having a calendar table would be very useful,
especially if your logic needs to change if Monday is a holiday.
Basically create a table with pre-calculated values for weeks and just join to it.
http://www.made2mentor.com/2011/04/calendar-tables-why-you-need-one

Related

Difference between two dates in business days? Google Bigquery

How do I calculate the difference between two dates in business days in Google Bigquery?
I want to replicate this example below:
I have tried these examples but they do not give the expected results:
DATE_DIFF but only counting business days
I also used this logic,ionand it did not work:
CREATE TEMP FUNCTION BusinessDateDiff(start_date DATE, end_date DATE) AS (
(SELECT COUNTIF(MOD(EXTRACT(DAYOFWEEK FROM date), 7) > 1)
FROM UNNEST(GENERATE_DATE_ARRAY(
start_date, DATE_SUB(end_date, INTERVAL 1 DAY))) AS date)
);
Consider below
create temp function BusinessDateDiff(delivery DATE, eta DATE) AS ((
select if(delivery > eta, 1, -1) * count(*)
from unnest(generate_date_array(
least(delivery, eta), greatest(delivery, eta) - 1
)) day
where not extract(dayofweek from day) in (1, 7)
));
select *,
BusinessDateDiff(DELIVERY_DATE, ORIGINAL_ETA_DATE) as BUSINESS_DAYS
from your_table
if applied to sample data as in your question - output is
getting desired result as follows:
CREATE TEMP FUNCTION BusinessDateDiff(start_date DATE, end_date DATE) AS (
(SELECT -1*COUNTIF(MOD(EXTRACT(DAYOFWEEK FROM date), 7) > 1)
FROM UNNEST(GENERATE_DATE_ARRAY( start_date , DATE_SUB(end_date,INTERVAL 1 DAY))) AS date));
CREATE TEMP FUNCTION BusinessDateDiff1( end_date DATE, start_date DATE) AS (
(SELECT COUNTIF(MOD(EXTRACT(DAYOFWEEK FROM date), 7) > 1)
FROM UNNEST(GENERATE_DATE_ARRAY( end_date , DATE_SUB(start_date,INTERVAL 1 DAY))) AS date));
WITH OrdersTable AS (
SELECT DATE '2022-06-28' AS DELIVERY_DATE,
DATE '2022-08-17' AS ORIGINAL_ETA_DATE
UNION ALL
SELECT DATE '2022-07-01' AS DELIVERY_DATE,
DATE '2022-07-14' AS ORIGINAL_ETA_DATE
UNION ALL
SELECT DATE '2022-06-30' AS DELIVERY_DATE,
DATE '2022-07-08' AS ORIGINAL_ETA_DATE
UNION ALL
SELECT DATE '2022-06-30' AS DELIVERY_DATE,
DATE '2022-07-08' AS ORIGINAL_ETA_DATE
UNION ALL
SELECT DATE '2022-06-29' AS DELIVERY_DATE,
DATE '2022-07-06' AS ORIGINAL_ETA_DATE
UNION ALL
SELECT DATE '2022-06-27' AS DELIVERY_DATE,
DATE '2022-07-01' AS ORIGINAL_ETA_DATE
UNION ALL
SELECT DATE '2022-06-30' AS DELIVERY_DATE,
DATE '2022-07-05' AS ORIGINAL_ETA_DATE
UNION ALL
SELECT DATE '2022-06-30' AS DELIVERY_DATE,
DATE '2022-06-28' AS ORIGINAL_ETA_DATE
)
SELECT
DELIVERY_DATE,
ORIGINAL_ETA_DATE,
case when DELIVERY_DATE < ORIGINAL_ETA_DATE then
BusinessDateDiff(DELIVERY_DATE, ORIGINAL_ETA_DATE)
when DELIVERY_DATE > ORIGINAL_ETA_DATE then
BusinessDateDiff1(ORIGINAL_ETA_DATE, DELIVERY_DATE)
else 0 end AS BUSINESS_DAYS
FROM OrdersTable
[![Desired Result][1]][1]
[1]: https://i.stack.imgur.com/efmw3.png

Perform a monthly distinct beneficiary count between 1/1/2018 -12/31/2020 based on two date fields

Perform a monthly distinct beneficiary count between 1/1/2018 -12/31/2020 using Microsoft SQL Server.
Below is my code but I have to change it for every month, is there any way to group by each month from 2018 to 2020 with 2 different date fields?
SELECT COUNT(distinct BEN_ID)
FROM LDS_2017andbeyond
WHERE
[DTE_FIRST_SVC] between '2018-01-01' and '2018-01-31'
AND
[DTE_LAST_SVC] between '2018-01-01' and '2018-01-31'
One simple way to group dates on the same month is the EOMONTH function. It'll return the last day of the month for a date.
SELECT
FORMAT(EOMONTH([DTE_FIRST_SVC]), 'yyyy-MM') AS MONTH_FIRST_SVC
, FORMAT(EOMONTH([DTE_LAST_SVC]), 'yyyy-MM') AS MONTH_LAST_SVC
, COUNT(DISTINCT BEN_ID) AS TOTAL_UNIQUE_BEN_ID
FROM LDS_2017andbeyond
WHERE [DTE_FIRST_SVC] BETWEEN '2018-01-01' AND '2020-12-31'
AND [DTE_LAST_SVC] BETWEEN '2018-01-01' AND '2020-12-31'
GROUP BY EOMONTH([DTE_FIRST_SVC]), EOMONTH([DTE_LAST_SVC])
ORDER BY MONTH_FIRST_SVC DESC, MONTH_LAST_SVC DESC
One solution would be to use a recursive CTE. You start with an anchor query and union it to itself with a DATEADD function in the recursive portion. This solution will give you every month month, even if the count is 0, as opposed to just grouping on the data, which will omit any months that arn't present.
Something like:
WITH CTE_Date AS (
SELECT CAST('01/01/2018' AS DATE) AS GroupMonth -- Start Date. Set as far back as necessary. Can use a DATEADD() to make dynamic.
UNION ALL
SELECT DATEADD(month, 1, GroupMonth) AS GroupMonth
FROM CTE_Date
WHERE DATEADD(month, 1, GroupMonth) < '12/31/2020' -- End Date. Remove the where to go to current.
)
SELECT
COUNT(distinct BEN_ID),
CAST(MONTH(d.GroupMonth) AS VARCHAR(2)) + '-' + CAST(YEAR(d.GroupMonth) AS VARCHAR(4)) AS Dt
FROM
LDS_2017andbeyond lds
LEFT OUTER JOIN CTE_Date d ON
MONTH(lds.[DTE_FIRST_SVC]) = MONTH(d.GroupMonth)
AND
YEAR(lds.[DTE_LAST_SVC]) = YEAR(d.GroupMonth)
GROUP BY
CAST(MONTH(d.GroupMonth) AS VARCHAR(2)) + '-' + CAST(YEAR(d.GroupMonth) AS VARCHAR(4))

Get total sales of last 12 months even if values are null

I have following code in which it presently gives month wise Total Sales for current year, I need to get total sales from last month of previous year to current month of this year.
My query is as follows:
;WITH mcte AS (
SELECT DATEADD(year, -1, getdate()) as MONTH_NAME
UNION ALL
SELECT DATEADD(MONTH,1,MONTH_NAME)
FROM mcte
WHERE DATEPART(MONTH,MONTH_NAME) < 12),octe AS(
SELECT (DATENAME (MONTH, DATEADD ( MONTH, DATEPART(MONTH, OI.CreatedDate), -1) )) AS MONTH_NAME,
SUM (OI.ItemQty * OI.TotalPrice) AS TOTAL_SALES
FROM Order_Item OI
GROUP BY MONTH(OI.CreatedDate))
SELECT DATENAME(MONTH,m.MONTH_NAME) + '' + DATENAME(YEAR,m.MONTH_NAME) as
MONTH_NAME, o.TOTAL_SALES FROM mcte m LEFT JOIN octe o ON o.MONTH_NAME = DATENAME(MONTH,m.MONTH_NAME)
and I am getting records
MONTH_NAME TOTAL_SALES
July2019 54023.45
August2019 NULL
December2019 NULL
September2019 NULL
October2019 NULL
November2019 NULL
Here I am only getting data for previous year only, not getting data for current year.Can anyone please guide me on this.
Thank you
You are only generating months up to 12. Try replacing the first CTE with:
WITH mcte AS (
SELECT DATEADD(year, -1, getdate()) as MONTH_NAME
UNION ALL
SELECT DATEADD(MONTH,1,MONTH_NAME)
FROM mcte
WHERE month_name < GETDATE()
),
Note the difference is the WHERE clause.
The entire query should look like this:
WITH months AS (
SELECT DATEFROMPARTS(YEAR(getdate()) - 1, MONTH(getdate()), 1) as month
UNION ALL
SELECT DATEADD(MONTH, 1, month)
FROM months
WHERE EOMONTH(month) < GETDATE()
)
SELECT m.month, SUM(OI.ItemQty * OI.TotalPrice) AS TOTAL_SALES
FROM months m LEFT JOIN
Order_Item OI oi
ON oi.CreatedDate >= m.month AND
oi.CreatedDate < DATEAADD(month, 1, m.month)
GROUP BY m.month
Try doing this:
DECLARE #CurDate DATE = GET_DATE()
DECLARE #OneYearPrior DATE = DATEADD(YEAR, -1, #CurDate)
WITH relevant_months(start_date, month_of_sale, year_of_sale) AS (
SELECT
#CurDate AS start_date,
MONTH(#CurDate) as month_of_sale,
YEAR(#CurDate) as year_of_sale
UNION ALL
SELECT DATEADD(MONTH, -1, start_date) AS start_date,
MONTH(DATEADD(MONTH, -1, start_date)) as month_of_sale,
YEAR(DATEADD(MONTH, -1, start_date)) AS year_of_sale
FROM relevant_months
WHERE DATEADD(MONTH, -1, start_date) >= #OneYearPrior
),
relevant_data AS (
SELECT OI.CreatedDate,
OI.ItemQty,
OI.TotalPrice,
MONTH(OI.CreatedDate), AS month_of_sale,
YEAR(OI.CreatedDate) AS year_of_sale
FROM Order_Item OI
WHERE OI.CreatedDate >= DATEADD(YEAR, -1, GETDATE())
)
SELECT rm.month_of_sale as month, rm.year_of_sale as year,
SUM(rd.ItemQty*rd.TotalPrice) as total_sales
FROM relevant_months rm
LEFT JOIN relevant_data rd
ON rm.month_of_sale = rd.month_of_sale
AND rm.year_of_sale = rd.year_of_sale
GROUP BY rm.month_of_sale, rm.year_of_sale
ORDER BY rm.year_of_sale asc, rm.month_of_sale asc

SQL - How to find missing activity days using start_date and end_date

I have a few fields in a database that look like this:
trip_id
start_date
end_date
start_station_name
end_station_name
I need to write a query that shows all the stations with no activity on a particular day in the year 2015. I wrote the following query but it's not giving the right output:
select
start_station_name,
extract(date from start_date) as dt,
count(*)
from
trips_table
where
(
start_date >= timestamp('2015-01-01')
and
start_date < timestamp('2016-01-01')
)
group by
start_station_name,
dt
order by
count(*)
Can someone help come up with the right query? Thanks in advance!
Below is for BigQuery Standard SQL
It assumes start_date and end_date are of DATE type
It also assumes that all days in between start_date and end_date are "dedicated" to station in start_station_name field, which most likely not what is expected but question is missing details here thus such an assumption
#standardSQL
WITH days AS (
SELECT day
FROM UNNEST(GENERATE_DATE_ARRAY('2015-01-01', '2015-12-31')) AS day
),
stations AS (
SELECT DISTINCT start_station_name AS station
FROM `trips_table`
)
SELECT s.*
FROM (SELECT * FROM stations CROSS JOIN days) AS s
LEFT JOIN (SELECT * FROM `trips_table`,
UNNEST(GENERATE_DATE_ARRAY(start_date, end_date)) AS day) AS a
ON s.day = a.day AND s.station = a.start_station_name
WHERE a.day IS NULL
You can test/play it with below simple/dummy data
#standardSQL
WITH `trips_table` AS (
SELECT 1 AS trip_id, DATE '2015-01-01' AS start_date, DATE '2015-12-01' AS end_date, '111' AS start_station_name UNION ALL
SELECT 2, DATE '2015-12-10', DATE '2015-12-31', '111'
),
days AS (
SELECT day
FROM UNNEST(GENERATE_DATE_ARRAY('2015-01-01', '2015-12-31')) AS day
),
stations AS (
SELECT DISTINCT start_station_name AS station
FROM `trips_table`
)
SELECT s.*
FROM (SELECT * FROM stations CROSS JOIN days) AS s
LEFT JOIN (SELECT * FROM `trips_table`,
UNNEST(GENERATE_DATE_ARRAY(start_date, end_date)) AS day) AS a
ON s.day = a.day AND s.station = a.start_station_name
WHERE a.day IS NULL
ORDER BY station, day
the output is like below
station day
111 2015-12-02
111 2015-12-03
111 2015-12-04
111 2015-12-05
111 2015-12-06
111 2015-12-07
111 2015-12-08
111 2015-12-09
Use recursion for this purpose: try this SQL SERVER
WITH sample AS (
SELECT CAST('2015-01-01' AS DATETIME) AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM sample s
WHERE DATEADD(dd, 1, dt) < CAST('2016-01-01' AS DATETIME)
)
SELECT * FROM sample
Where CAST(sample.dt as date) NOT IN (
SELECT CAST(start_date as date)
FROM tablename
WHERE start_date >= '2015-01-01 00:00:00'
AND start_date < '2016-01-01 00:00:00'
)
Option(maxrecursion 0)
If you want the station data with it then you can use left join as :
WITH sample AS (
SELECT CAST('2015-01-01' AS DATETIME) AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM sample s
WHERE DATEADD(dd, 1, dt) < CAST('2016-01-01' AS DATETIME)
)
SELECT * FROM sample
left join tablename
on CAST(sample.dt as date) = CAST(tablename.start_date as date)
where sample.dt>= '2015-01-01 00:00:00' and sample.dt< '2016-01-01 00:00:00' )
Option(maxrecursion 0)
For mysql, see this fiddle. I think this would help you....
SQL Fiddle Demo

How can I select the 1st of every month for the last 5 years in sql?

I am trying to get a list of the 1st of the Month for the last 5 years. How can i do that ?? I have a select statement:
select convert(varchar(10), dateadd(mm,Datediff(mm,0,getdate()),0),111) as StartDate
but i am not sure how to get a list for every month.
with dates
as (
select dateadd(month, datediff(month, 0, getdate()), 0) as date
union all
select dateadd(month, - 1, date)
from dates
)
select top 60 *
from dates
with cte as (
select DATEFROMPARTS ( datepart(yyyy,getdate()), datepart(mm,getdate()), 1 ) as startdate
union all
select dateadd(month,-1,startdate) from dates
where datediff(year,startdate,getdate()) <> 5 )
select CONVERT ( varchar(12), startdate , 107 ) from cte;