Calculate number of months between 2 dates SQL - sql

I want to calculate number of full months between 2 dates
declare #startdate date='2021-03-03';
declare #enddate date='2021-05-02';
select datediff(mm,#startdate,#enddate)
This is giving me output as 2 but I want it to be 1 as it should count full months only. So from 03/03/2021 to 03/04/2021 is 1 month and from 03/04/21 to 02/05/21 is still not full month. So, the answer should be 1 in this example. How do I achieve this?

One way is
declare #startdate date='2021-03-03';
declare #enddate date='2021-05-02';
select datediff(mm,#startdate,#enddate) - case when dateadd(mm, datediff(mm,#startdate,#enddate), #startdate) > #enddate then 1 else 0 end

Full month is a tricky thing in some cases.
If I start on Jan 31 and end on Feb 28 is that a full month?
If I start on Feb 28, 2019 and end on Feb 28, 2020 is that 12 full months?
If I start on Feb 29, 2020 and end on Feb 28, 2021 is that 12 full months?
If you did not care about these cases and just wanted a simplistic rule like "it's a full month if the EndDay>=StartDay you could adjust the difference with a "Case When Day(Enddate)<Day(Startdate) Then 1 Else 0 End"

Related

Getting the inventory count of last 30 days after 30 days from today

If today is March 28, 2022, I want to get the inventory count before 30 days from today for this instance it should be Feb 27, 2022 and only 30 days inventory count after Feb 27 is all I need.
like January 29 to February 27 only
You are telling 30 days and again 30 days before 30 days i.e., 60 days
you can apply DATEADD like given below:
DECLARE #today date = '2022-03-28'
DEClARE #startInventoryDate date, #endInventoryDate date
SELECT #startInventoryDate = dateadd(day, -30, #today), #endInventoryDate = dateadd(day, -60, #today)
SELECT #startInventoryDate as startInventoryDate, #endInventoryDate as endInventoryDate
startInventoryDate
endInventoryDate
2022-02-26
2022-01-27

sql query every week start date and end date calculated by its start date of every month

I need to calculate the start date and its end date base on month start
Week
date
1
1-Jan
2
5-Jan
3
12-Jan
4
18-Jan
5
26-Jan
6
31-Jan
if my month 1st week start on 1st jan and its end 2jan than my start date comes 1/jan and end date 2/jan. beause mon comes 3rd jan and my week calc. started on 1st jan sat.
like below example of cliender
Mon
Tue
Wed
Thu
Fri
Sat
Sun
Weeks
1
2
1st Week
3
4
5
6
7
8
9
2nd Week
10
11
12
13
14
15
16
3rd Week
17
18
19
20
21
22
23
4th Week
24
25
26
27
28
29
30
5th Week
31
6th Week
I need 2 column
week start date
week end date
expected data like below
Week
date
week start date
week end date
1
1-Jan
1-Jan
2-Jan
2
5-Jan
3-Jan
9-Jan
3
12-Jan
10-Jan
16-Jan
4
18-Jan
17-Jan
23-Jan
5
26-Jan
24-Jan
30-Jan
6
31-Jan
31-Jan
31-Jan
For MSSql and Sql Server:
If you send your date value as #yourdatevalue parameter to belowed query, it should work fine:
DECLARE #yourdatevalue datetime = GETDATE();
-- strip the time part from your date
DECLARE #date datetime = CONVERT(date, #yourdatevalue);
-- do the day of week math
DECLARE #start datetime = DATEADD(d, 1 - DATEPART(w, #date), #date),
#end datetime = DATEADD(d, 8 - DATEPART(w, #date), #date);
SELECT #yourdatevalue AS [date], #start AS [WeekStart], #end AS [WeekEnd];
Also:
You can set #WeekNum and #YearNum to whatever you want - in this example they are derived from the #datecol variable, which is set to GETDATE() for purposes of illustration. Once you have those values- you can calculate the date range for a week by using the following:
DECLARE #datecol datetime = GETDATE();
DECLARE #WeekNum INT
, #YearNum char(4);
SELECT #WeekNum = DATEPART(WK, #datecol)
, #YearNum = CAST(DATEPART(YY, #datecol) AS CHAR(4));
-- once you have the #WeekNum and #YearNum set, the following calculates the date range.
SELECT DATEADD(wk, DATEDIFF(wk, 6, '1/1/' + #YearNum) + (#WeekNum-1), 6) AS StartOfWeek;
SELECT DATEADD(wk, DATEDIFF(wk, 5, '1/1/' + #YearNum) + (#WeekNum-1), 5) AS EndOfWeek;

Subtraction of inventory from Demand in BigQuery everday and adding new inventory

Here's how my data looks like:
date
sku
inventory_added
demand
22nd Nov 2021
XYZ
70
18
23rd Nov 2021
XYZ
0
18
24th Nov 2021
XYZ
0
50
25th Nov 2021
XYZ
0
15
26th Nov 2021
XYZ
80
30
27th Nov 2021
XYZ
0
20
28th Nov 2021
XYZ
0
15
29th Nov 2021
XYZ
0
20
30th Nov 2021
XYZ
0
10
1st Dec 2021
XYZ
100
40
2nd Dec 2021
XYZ
0
10
I want to create a new column named solution using BigQuery SQL where in the 1st row, i.e. 22nd Nov 2021, I want formula as - inventory_added - demand.
This will give me 1st row's value for solution will be 52.
Now what I am not able to do is from 2nd row:
So, next now, will be 52 (remaining inventory from previous day) + 0 (inventory_added on 23rd Nov 2021) - 18 (demand on 23 Nov 2021). This is equal to 34.
Similarly going to next row, i.e. 24th November:
value in solution will be 34 + 0 - 50 = -16. Now since it is negative, it should be put as 0.
I tried this - MAX(solutions, 0).
The result will look like this:
date
sku
inventory_added
demand
solution
22nd Nov 2021
XYZ
70
18
52
23rd Nov 2021
XYZ
0
18
34
24th Nov 2021
XYZ
0
50
0
25th Nov 2021
XYZ
0
15
0
26th Nov 2021
XYZ
80
30
50
27th Nov 2021
XYZ
0
20
30
28th Nov 2021
XYZ
0
15
15
29th Nov 2021
XYZ
0
20
0
30th Nov 2021
XYZ
0
10
0
1st Dec 2021
XYZ
100
40
60
2nd Dec 2021
XYZ
0
10
50
I am not sure if this can be accomplished by BigQuery, but all suggestions are welcome.
Thanks!
Without the condition "it is negative, it should be put as 0" you may use window (in BigQuery terms - analytic) variant of SUM() function:
SELECT *,
SUM(inventory_added - demand) OVER (PARTITION BY sku ORDER BY date) AS solution
FROM source_table
With this condition the output become iterative, and you must use recursive CTE (if available in BigQuery) or iterative stored procedure.
I see that recursive CTE is not available in BigQuery ... Can you provide a pseudo code may as a starting point for stored procedures? – Shantanu Jain
CREATE PROCEDURE procname()
BEGIN
CREATE temptable;
OPEN CURSOR FOR SELECT * FROM datatable ORDER BY date;
SET #solution = 0;
FETCH CURSOR INTO #date, #sku, #inventory_added, #demand;
LOOP ​
​ SET #solution = GREATEST(#solution + #inventory_added - #demand, 0);
​ INSERT INTO temptable VALUES (#date, #sku, #inventory_added, #demand, #solution);
FETCH CURSOR INTO #date, #sku, #inventory_added, #demand;
UNTIL NO_ROWS_IN_CURSOR END LOOP;
SELECT * FROM temptable;
DROP temptable;
END
AS an option - consider use of recently introduced FOR...IN Loop
declare result int64;
declare prev_sku string;
create temp table results as (select *, 0 as solution from your_table where false);
set (result, prev_sku) = (0, '');
for record in (select *, parse_date('%d %B %Y', regexp_replace(date, r'(\d*)(\w*)( \w{3} \d{4})', r'\1 \3')) dt from your_table order by sku, dt) do
if record.sku != prev_sku then set result = 0; end if;
set result = result + record.inventory_added - record.demand;
if result < 0 then set result = 0; end if;
insert into results values(record.date, record.sku, record.inventory_added, record.demand, result);
set prev_sku = record.sku;
end for;
select * from results
order by sku, parse_date('%d %B %Y', regexp_replace(date, r'(\d*)(\w*)( \w{3} \d{4})', r'\1 \3'));
If applied to sample data in your question - output is
Note: While delivering expected result - obviously this is going to be extremely slow (as any cursor based solution) - so while applicable for learning - I don't think appropriate for real production use

Get end week date function

The start week is Saturday and end week is Friday, I'd like to write a function to return the end week of a date.
e.g.
'2014-6-26' (Thursday) would return '2014-6-27' (Friday)
'2014-6-27' (Friday) would return '2014-6-27' (same day)
'2014-6-28' (Sat) would return '2014-7-04' (next Friday)
Because I cannot SET DATEFIRST in functions, I am struggling to create this function.
This is what I got so far. It works in all scenarios except when I enter a Saturday it returns the previous Friday (which is wrong). e.g. '2014-6-28' returns '2014-6-27'
CREATE FUNCTION [GetEndWeek](
#Date DATETIME
)
RETURNS DATETIME
WITH SCHEMABINDING, RETURNS NULL ON NULL INPUT
AS
BEGIN
RETURN DATEADD(DAY,(13 - (##DATEFIRST + DATEPART(WEEKDAY, #Date )))%7,#Date )
END
Any help appreciated.
I do not want to SET DATEFIRST anywhere else (i.e. in procs) as I'm calling this function a lot.
Thanks!
Assuming that the language is English, you should use datename() rather than datepart(). datepart() is subject to system values. I think this logic does what you want:
return dateadd(day,
(case datename(weekday, #Date)
when 'Saturday' then 6
when 'Sunday' then 5
when 'Monday' then 4
when 'Tuesday' then 3
when 'Wednesday' then 2
when 'Thursday' then 1
when 'Friday' then 0
end),
#Date);
I'm not sure the existing function is correct, it gave me the column "prev_func" below, so for Sat Jun 28 I would expect the following Friday (July 4), like this:
| ADATE | MODULUS_7 | NEW_METHOD | PREV_FUNC |
|-------------------------|-----------|-----------------------|-----------------------|
| Thursday, June 26, 2014 | 1 | Friday, June 27, 2014 | Friday, June 27, 2014 |
| Friday, June 27, 2014 | 0 | Friday, June 27, 2014 | Friday, June 27, 2014 |
| Saturday, June 28, 2014 | -1 | Friday, July 04, 2014 | Friday, June 27, 2014 |
You don't have to use ##datefirst or change datefirst to arrive at predictable day of week calculations. The date zero in sql server, 1900-01-01 is a Monday, so datediff(day,0,[date-here]) % 7 is predictable with zero (no remainder) being Monday, and Friday is 4.
I believe the calculation you need is in the query below, column "new_method" :
select
format(adate,'D') as adate
, 4 - (datediff(day,0,adate) % 7) as modulus_7
, format(
case when 4 - datediff(day,0,adate) % 7 < 0
then dateadd(day,4 - (datediff(day,0,adate) % 7) + 7,adate)
else dateadd(day,4 - datediff(day,0,adate) % 7,adate)
end
,'D') as new_method
, format(dbo.getendweek(adate),'D') as prev_func
from table1
;
see: http://sqlfiddle.com/#!6/0903f/16

I want output of sql Query and i already done some query part but it fails

The query which i have written
SELECT Date_current,
COUNT(*) as'Total'
FROM Call_Register
WHERE (DATEDIFF(dd,'02/1/2014',Date_current) >=0)
AND (DATEDIFF(dd,'02/12/2014',Date_current) <=0)
GROUP BY Date_current
HAVING COUNT(*)>=(convert(int,'02/12/2014')) \
ORDER BY Date_current
But this query gives me error:
Conversion failed when converting the varchar value '02/12/2014' to data type int.
Date Total
---------- -------
Feb 3 2014 2:58PM 10
Feb 4 2014 2:59PM 10
Please Help me
getting Output like
Date Total
---------- -------
Feb 3 2014 2:58PM 1
Feb 3 2014 2:59PM 1
Feb 3 2014 3:00PM 1
Feb 3 2014 3:08PM 1
Feb 3 2014 3:20PM 1
Feb 3 2014 4:05PM 1
Feb 3 2014 4:17PM 1
Feb 3 2014 4:19PM 1
Feb 3 2014 4:21PM 1
Feb 3 2014 4:24PM 1
Feb 4 2014 1:11PM 1
Feb 4 2014 2:35PM 1
Feb 4 2014 2:37PM 1
Feb 4 2014 5:19PM 1
Firstly, you should either use the culture invariant date format yyyyMMdd, or explicitly set the date format using SET DATEFORMAT DMY, or prepare to get inconsistent results.
Secondly, the following is potentially very inefficient:
WHERE (DATEDIFF(dd,'02/1/2014',Date_current) >=0)
AND (DATEDIFF(dd,'02/12/2014',Date_current) <=0)
If you have an index on Date_Current it will not be used because you are performing a function on it. You should instead use:
WHERE Date_Current >= '20140102'
AND Date_Current <= '20141202'
You then have a sargable query. I have had to guess at whether '02/1/2014' meant 1st February 2014, or 2nd January 2014 as it is not clear (hence the importance of my first point).
Finally (this part has already been answered but including it for completeness as I couldn't not point out the first two errors) you cannot convert to int here:
convert(int,'02/12/2014')
You presumably need to convert to date time first:
CONVERT(INT, CONVERT(DATETIME, '20141202'))
Although I suspect this is still not what you want, you are just filtering the days to those that have 41973 records or more, seems like a fairly arbitrary filter....
You need to Cast your String Date after that only you can proceed with Int CAST
CAST('02/12/2014' AS Datetime)
Try this
SELECT Date_current,COUNT(*) AS 'Total'
From Call_Register
WHERE (DATEDIFF(dd,'02/1/2014',Date_current)>=0) AND
(DATEDIFF(dd,'02/12/2014',Date_current)<=0)
Group By Date_current
having COUNT(*)>=(convert(int, CAST('02/12/2014' AS Datetime)) order By Date_current
At last got the as for my question thnx everyone
SELECT cast(Date_current as DATE),COUNT(*) AS 'Total'
From Call_Register
WHERE (DATEDIFF(dd,'02/1/2014',Date_current)>=0) AND
(DATEDIFF(dd,'02/13/2014',Date_current)<=0)
Group By cast(Date_current as DATE)
can i use order by in this cos i want it in descending order pl help for that