how to calculate WeekOfMonth in SQL - date

Trying to calculate holidays in a given year for loading DimDate table. There is an option in SQL to calculate the WeekOfYear Number but couldnt find a function to calculate the WeekNumberOfMonth.
How to find week number of a given month to calculate holidays in a year to fill DIMDate table

Logic
CurrentDateWeek - CurrentDateMonthBeginWeek
the difference between the week Number for a given date and the first week number for the given date provides week of a month
Simple One Line SQL Statement
Declare #CurrentDate as datetime
set #CurrentDate = '5/31/2014'
Select (Datepart(week,#CurrentDate) - (Datepart(week,cast(month(#CurrentDate) as varchar) + '/1/' + cast(Year(#CurrentDate) as varchar))) + 1) as WeekOfMonth
Stored Procedure
Declare #CurrentDate as datetime
Declare #BeginWeek as int
Declare #EndWeek as Int
set #CurrentDate = '5/31/2014'
SET #EndWeek = DATEPART(week,eomonth(#CurrentDate))
SET #BeginWeek = Datepart(week,cast(month(#CurrentDate) as varchar) + '/1/' + cast(Year(#CurrentDate) as varchar))
Select Datepart(week,#CurrentDate) - #BeginWeek + 1
Select #BeginWeek as beginweek
select #EndWeek as endweek
Select Datepart(week,#CurrentDate)
Using the above logic we could fill the DIMDATE table with holiday days

Related

PostgreSQL to get the xth business day for the given month

Get xth Business day of a calendar month. For ex. if Nov'21 then 3rd business day is 3rd November, but if Oct'21 3rd business day is 5th Oct. We need to build a query or function to get this dynamically. We need to exclude the weekends (0,6) and any public holidays (from a table with public holidays)..
I believe we dont have a direct calendar function in postgres, may be we can try getting the input as month and integer for (xth business day) we need to get the output as date..
if input : Nov/11 (Month) and 3 (xth Business Day) it will be output: '2021-11-03' as output
create or replace function nth_bizday(y integer, m integer, bizday integer)
returns date language sql as
$$
select max(d) from
(
select d
from generate_series
(
make_date(y, m, 1),
make_date(y, m, 1) + interval '1 month - 1 day',
interval '1 day'
) t(d)
where extract(isodow from d) < 6
-- and not exists (select from nb_days where nb_day = d)
limit bizday
) t;
$$;
select nth_bizday(2021, 11, 11);
-- 2021-11-15
If you want to skip other non-business days except weekends then the where clause should be extended as #SQLPro suggests, something like this (supposing that you have the non-business days listed in a table, nb_days):
where extract(isodow from d) < 6
and not exists (select from nb_days where nb_day = d)
Business days are generally specific to organization... You must create a CALENDAR table with date and entries from the begining to the end, with a boolean column that indicates if a day is on or off...
Then a view can compute the nth "on" days for every month...

Convert Excel formula (using Date and subtraction) into T-SQL

I am trying to write this Excel formula into T-SQL (to write a function).
Expected output is 0.71944444, but currently my output (using T-SQL) is 24.0000.
I am not sure why we have to add a day to same date and subtract the same date.
Bottom is a screenshot from Excel:
This is what I have so far in T-SQL:
CREATE FUNCTION [dbo].[fn_0921] (
#Punch_Start nvarchar(max)
)
RETURNS decimal(36, 8) AS
BEGIN
DECLARE #return_value nvarchar(max);
SET #return_value =
DATEDIFF(
MINUTE, CAST(#Punch_Start AS datetime2),
(
dateadd(
day, 1, CAST(#Punch_Start AS datetime2)
)
)
)
/ (60.0)
RETURN #return_value
END;
Thanks for help.
The Excel formula is returning the difference between the datetime in cell K4 & the start of the next day (i.e. 7/26/2021 00:00) as a fraction of a whole day. The following is the equivalent in T-SQL:
DECLARE #Punch_Start datetime2 = '7/25/2021 06:44';
SELECT DATEDIFF(
MINUTE,
#Punch_Start,
CAST(
CAST(
DATEADD(DAY, 1, #Punch_Start)
AS date) -- Add 1 day to #Punch_Start & cast as date to remove the time component - this is the start of the next day
AS datetime2) -- Cast back to datetime2 to get the difference in minutes
) / 1440.; -- Divide the difference in minutes by the number of minutes in a day (60 minutes per hour, 24 hours per day) to get the difference as a fraction of a day
This can probably help you:
DECLARE #date DATETIME2 = '2021-07-25 06:44'
DECLARE #seconds INT = DATEDIFF(second, CAST(#date AS date), #date)
DECLARE #secondsFromEnd FLOAT = 86400 - #seconds
SELECT #secondsFromEnd / 86400

How to write the query to get the first and last date of a January and other month's in postgresql

How to get the first and last date of the particular month i.e if i pass the particular month name say March it should return output as 01/03/2019 and 31/03/2019.( For current year)
If you want to pass value March you would have to modify the code to understand every month. I'm not sure it's worth the trouble. Anyways, here's a code to return two values (start and end of month) based on current_date. Should you wish to change the day, you could put for example '2019-04-13' in that place.
SELECT
date_trunc('month', current_date) as month_start
, (date_trunc('month', current_date) + interval '1 month' - interval '1 day')::date as month_end
DATE_TRUNC function truncates the date to the precision specified in first argument, thus making the date as of first day of given month (taken from current_date in above example).
For end of month you need a bit more computation. I've always used this in production and what it does is it first truncates your date to first day of month, then adds one month and goes back one day, so that you have your end of month date (whether it's 30, 31, or special case for February during leap years).
for any month, the first day must be 1st,
so it is:
make_date(2019, 3, 1)
and for any month, the last day is 1 day before the first day of next month,
so it is:
make_date(2019, 4, 1) - integer '1'
sorry, I don't have a PostgreSQL environment to test if it is correct,
so please test it yourself.
and, BTW,
you can find more details about date/time operators and functions here:
https://www.postgresql.org/docs/current/functions-datetime.html
One straightforward approach, which would also work on most other databases, would be to truncate the incoming date by month to obtain the first day of that month. Then, truncate the date with one month added to it, and subtract one day, to obtain the last day of the month.
SELECT
DATE_TRUNC('month', '2019-03-15'::date) AS date_start,
DATE_TRUNC('month', '2019-03-15'::date + INTERVAL '1 MONTH')
- INTERVAL '1 DAY' AS date_end;
Demo
From here Date LastDay
SELECT date_trunc('MONTH', dtCol)::DATE;
CREATE OR REPLACE FUNCTION last_day(DATE)
RETURNS DATE AS
$$
SELECT (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day')::DATE;
$$ LANGUAGE 'sql' IMMUTABLE STRICT;
The conversion from month name parameter is actually rather simple. Create an array with the month names and find the position in the array of the parameter, that result becomes the month value into the make_date function with year extracted from current date and day 1. The below contains an overloaded function providing for either date or month name with optional year.
create type first_last_date as ( first_of date, last_of date);
create or replace function first_last_of_month(date_in date)
returns first_last_date
language sql immutable strict leakproof
as $$
select (date_trunc('month', date_in))::date, (date_trunc('month', date_in) + interval '1 month' - interval '1 day')::date ;
$$;
create or replace function first_last_of_month( month_name_in text
, year_in integer default null
)
returns first_last_date
language sql immutable leakproof
as $$
select first_last_of_month ( make_date ( coalesce (year_in, extract ('year' from now())::integer)
, array_position(ARRAY['jan','feb','mar','apr','may','jun','jul','aug','sep','nov','dec']
, lower(substring(month_name_in,1,3)))
,1 ) );
$$;
-- test
Select first_last_of_month('March');
Select first_last_of_month('February') y2019
, first_last_of_month('February', 2020) y2020;
Select first_last_of_month(now()::date);

DB2 How to convert the following "with temp as" into a normal select statement

I'm trying to do an insert with this "with" statement but it seems like it supports select statement only, therefore I want to convert it into select statement. I'm just amazed on how this is working. Got a similar example on stack and changed it to fit my needs.
with temp (startdate, enddate, maxdate) as (
select min(salesdate) startdate, min(salesdate)+3 months enddate, max(salesdate) maxdate
from SALES
union all
select startdate + 3 months + 1 days, enddate + 3 months + 1 days, maxdate from temp
where enddate <= maxdate
)
select startdate, min(enddate, maxdate) from temp;
Thanks in advance.
Edit: It seems my query is misunderstood. Here is the pseudo code of what the query is supposed to be doing. The query is returning the expected result which is pretty amazing to me. I don't know how the recursive doesn't overlap after I added 1 day. After writing the pseudo code, I see that the select startdate + 3 months + 1 days should have been written as select enddate + 1 days which logically says what it's supposed to do instead of magically work:
rows = []
startdate = min(salesdate)
enddate = startdate + 3 months
maxdate = max(salesdate)
i = 0;
do {
rows[i++] = [startdate, min(enddate, maxdate)] // min for final iteration where enddate > maxdate.
startdate = enddate + 1 days
enddate = enddate + 1 days + 3 months // aka: startdate + 3 months
} while (enddate <= maxdate)
return rows
Hence, I've broken a huge date range into smaller chunks of 3 months ranges. Whether it is exactly 90 days or 91 days is not important, as long as I get every single date without gap and without overlap.
I'm curious about your decision, that a query with a recursive common table expression (RCTE) is "not normal". IBM calls it as 'select-statement' and considers it as normal. If it's some educational question, and you don't want to use RCTE due to some reason, then consider the the following example.
select s + (3*(x.i-1)) month start, s + (3*x.i) month - 1 day end
from table(values (date('2011-01-01'), date('2012-01-01'))) d(s, e)
, xmltable('for $id in (1 to $e) return <i>{number($id)}</i>'
passing ((year(e)-year(s))*12 + (month(e)-month(s)))/3 as "e"
columns i int path '.'
) x;
START END
---------- ----------
2011-01-01 2011-03-31
2011-04-01 2011-06-30
2011-07-01 2011-09-30
2011-10-01 2011-12-31
;
It's a little bit complicated, since you must pass desired number of rows to return to the xmltable table function, which returns a single column with values 1 to N. In other words you must compute desired number of 3-months intervals and pass it to the function.
(R)CTE can't be used in the UPDATE/DELETE statements, where you are able to use so called fullselect statements only (they don't allow CTEs). If you really need to use CTE for UPDATE/DELETE as in this case, you can do one of the following:
If you ARE ABLE to compute a temporary result set for whole delete/update statement, you can do something like this (I don't use here RCTE for simplicity, but a simple CTE only):
with a (id) as (values 1)
select count(1)
from old table(
delete from test t
where exists (select 1 from a where a.id=t.id)
);
If you ARE NOT ABLE to compute a temporary result set for whole delete/update statement, you can create a table/scalar function with the corresponding parameters, where you are able to use your RCTE. This function can be used in the outer statement afterwards.
I was able to insert it by moving the insert statement in front of the "with" statement. Copying answer here so I know next time. Although, I'm still interested in seeing how to convert it to a pure select statement. Will select that answer as the correct answer.
insert into my_temp_table
with temp (startdate, enddate, maxdate) as (
select min(salesdate) startdate, min(salesdate)+3 months enddate, max(salesdate) maxdate
from SALES
union all
select startdate + 3 months + 1 days, enddate + 3 months + 1 days, maxdate from temp
where enddate <= maxdate
)
select startdate, min(enddate, maxdate) from temp;

How do I convert a date ( YYYY-MM-DD ) into a month number in postgresql?

I got a table:
CREATE TABLE TRANSACTION (
transaction_date date,
id_transaction int,
PRIMARY KEY (id_transaction)
);
and I want to compare the month of 'transaction_date' field with a number of month.
SELECT *
FROM TRANSACTION T
WHERE month = transaction_date;
but I don't know how to make this conversion.
You can use EXTRACT(MONTH FROM transaction_date)
SELECT *
FROM transaction
WHERE EXTRACT(MONTH FROM transaction_date) = 1;
sqlfiddle demo
As per the documentation:
EXTRACT (field FROM source)
The extract function retrieves subfields such as year or hour from
date/time values. source must be a value expression of type timestamp,
time, or interval.
SELECT *
FROM TRANSACTION T
WHERE EXTRACT(MONTH FROM TIMESTAMP transaction_date) = month;
month should be an integer between 1 (January) and 12 (December).