I had created a table long time ago using SQL that had Day, Month, Year, Weekday, Date, and Period (example: April 2016).
This is what my current table looks like:
| Period | Day | Month | Year | Weekday | Date |
|:-----------|-----|-------|------|---------|----------:|
| April 2016 | 21 | April |2016 |Thursday |2016-04-21 |
Now I am needing to add Week (it is week 1, 2,... of that current month).
This select statement gives the correct result:
SELECT datediff(week, dateadd(week, datediff(week, 0, dateadd(month, datediff(month, 0, GETDATE()), 0)), 0), GETDATE() - 1) + 1
This query returns
4
How do I insert a new column called Week into this existing table and have it find the current week number?
I believe that the existing table is using GETDATE() to calculate its values. Unfortunately I do not have my CREATE query anymore.
Any help is much appreciated!
First add a column for week using ALTER TABLE
ALTER TABLE TableName
ADD Week int
Then UPDATE the column with the week number:
UPDATE TableName
SET Week = DATEPART(day, DATEDIFF(day, 0, [Date])/7 * 7)/7 + 1
Note: This week number is based on the day number alone, not the days of the week (Monday, Tuesday) etc.
Related
From iso week and year, I would like to get a date.
The date should be first day of the week.
First day of the week is Monday.
For example iso week 10 and iso year should convert to 2019-03-04.
I am using Snowflake
The date expression to do this is a little complex, but not impossible:
SELECT
DATEADD( /* Calculate start of ISOWeek as offset from Jan 1st */
DAY,
WEEK * 7 - CASE WHEN DAYOFWEEKISO(DATE_FROM_PARTS(YEAR, 1, 1)) < 5 THEN 7 ELSE 0 END
+ 1 - DAYOFWEEKISO(DATE_FROM_PARTS(YEAR, 1, 1)),
DATE_FROM_PARTS(YEAR, 1, 1)
)
FROM (VALUES (2000, 1), (2000, 2), (2001, 1), (2002, 1), (2003, 1)) v(YEAR, WEEK);
Unfortunately, Snowflake doesn't support this functionality natively.
While it's possible to compute manually the date from ISO week and year, it's very complex. So like others suggested, generating a Date Dimension table for this is much easier.
Example of a query that can generate it for the lookups (note - this is not a full Date Dimension table - that is typically one row per day, this is one row per week).
create or replace table iso_week_lookup as
select
date_part(yearofweek_iso, d) year_iso,
date_part(week_iso, d) week_iso,
min(d) first_day
from (
select dateadd(day, row_number() over (order by 1) - 1, '2000-01-03'::date) AS d
from table(generator(rowCount=>10000))
)
group by 1, 2 order by 1,2;
select * from iso_week_lookup limit 2;
----------+----------+------------+
YEAR_ISO | WEEK_ISO | FIRST_DAY |
----------+----------+------------+
2000 | 1 | 2000-01-03 |
2000 | 2 | 2000-01-10 |
----------+----------+------------+
select min(first_day), max(first_day) from iso_week_lookup;
----------------+----------------+
MIN(FIRST_DAY) | MAX(FIRST_DAY) |
----------------+----------------+
2000-01-03 | 2027-05-17 |
----------------+----------------+
select * from iso_week_lookup where year_iso = 2019 and week_iso = 10;
----------+----------+------------+
YEAR_ISO | WEEK_ISO | FIRST_DAY |
----------+----------+------------+
2019 | 10 | 2019-03-04 |
----------+----------+------------+
Note, you can play with the constants in create table to create a table of the range you want. Just remember to use Monday as the starting day, otherwise you'll get a wrong value for the first week in the table :)
If you do not have Date Dimension table and/or utilities, as mentioned in the comments, you should parsing it from a textual form. But it would be DBMS implementation dependent:
In MySQL: STR_TO_DATE(CONCAT(year, ' ', week), '%x %v')
In PostgreSQL: TO_DATE(year || ' ' || week, 'IYYY IW')
(also Oracle DB would be something similar)
I'm tasked with pulling the data for the four recent quarters. If I was dealing with dates this would be easy, but I'm not sure how to do so when I have a quarters table that looks like this:
| quarter | year |
+---------+------+
| 1 | 2016 |
| 2 | 2016 |
| 3 | 2016 |
...
I know that I can get the current quarter by doing something like this:
SELECT *
FROM quarters
WHERE quarter = (EXTRACT(QUARTER FROM CURRENT_DATE))
AND year = (EXTRACT(YEAR FROM CURRENT_DATE));
However, I'm not sure the best way to get the four most recent quarters. I thought about getting this quarter from last year, and selecting everything since then, but I don't know how to do that with tuples like this. My expected results would be:
| quarter | year |
+---------+------+
| 1 | 2017 |
| 2 | 2017 |
| 3 | 2017 |
| 4 | 2017 |
Keep in mind they won't always be the same year - in Q12018 this will change.
I've built a SQLFiddle that can be used to tinker with this - http://sqlfiddle.com/#!17/0561a/1
Here is one method:
select quarter, year
from quarters
order by year desc, quarter desc
fetch first 4 rows only;
This assumes that the quarters table only has quarters with data in it (as your sample data suggests). If the table has future quarters as well, then you need to compare the values to the current date:
select quarter, year
from quarters
where year < extract(year from current_date) or
(year = extract(year from current_date) and
quarter <= extract(quarter from current_date)
)
order by year desc, quarter desc
fetch first 4 rows only;
For the case that there can be gaps, like 2/2017 missing, and one would then want to return only three quarters instead of four, one can turn years and quarters into consecutive numbers by multiplying the year by four and adding the quarters.
select *
from quarters
where year * 4 + quarter
between extract(year from current_date) * 4 + extract(quarter from current_date) - 3
and extract(year from current_date) * 4 + extract(quarter from current_date)
order by year desc, quarter desc;
My requirement is to populate week number against calendar date.The catch is week number will start from October 1 and end at December 7.
So week commencing October 1 will be treated as week 1 , 7th October as week 2 and so on last week number will populate against December 7. Rest will have week number column as NULL. How to do it in hive ?
with t as (select date '2014-10-23' as dt)
select case
when dt between cast(concat(date_format(dt,'yyyy'),'-10-01') as date)
and cast(concat(date_format(dt,'yyyy'),'-12-07') as date)
then datediff (dt,cast(concat(date_format(dt,'yyyy'),'-10-01') as date)) div 7 + 1
end as week_number
from t
+-------------+
| week_number |
+-------------+
| 4 |
+-------------+
I've written a stored procedure to get the week from a date, it also returns the date at the start of the week as well as the week number and year.
I'm aware of the 'WEEK' function, however this doesn't give me the date at the start of the week and I'm not aware of a function that does this given the week and year.
Question is:
How can I get the 'date' at the start of the week given the week number? Where the start of the week is passed in as a day index, 0 = Sunday, 1 = Monday etc.
My current function doesn't always work and if the first day of the week is Monday, then Sunday falls into the next week, not the end of the same week as I would like it to be.
I was digging around this for a bit too. But I stumbled on some mysql code that also worked. It basically subtracts days based on the day of the week. i.e. If the date is a Wed (4), you know the date was 1-4=-3 days ago.
How about this:
# with Sunday being the start of the week:
select convert(date_add(now(), interval(1-dayofweek(now())) day), date) as WeekStartDate
select convert(date_add(now(), interval(7-dayofweek(now())) day), date) as WeekEndDate
# with Monday being the start of the week:
select convert(date_add(now(), interval(2-dayofweek(now())) day), date) as WeekStartDate
select convert(date_add(now(), interval(8-dayofweek(now())) day), date) as WeekEndDate
Credit:
How do I get the first day of the week of a date in mysql?
Use Sequence engine. You can adapt the following example as needed:
MariaDB [_]> SHOW ENGINES\G
.
.
.
*************************** 3. row ***************************
Engine: SEQUENCE
Support: YES
Comment: Generated tables filled with sequential values
Transactions: YES
XA: NO
Savepoints: YES
.
.
.
MariaDB [_]> SET #`year` := 2016,
-> #`mode` := 1,
-> #`week` := 23;
Query OK, 0 rows affected (0.00 sec)
MariaDB [_]> SELECT
-> `der`.`date`,
-> `der`.`week`,
-> `der`.`year`
-> FROM (
-> SELECT
-> `der`.`date`,
-> WEEK(`der`.`date`, #`mode`) `week`,
-> YEAR(`der`.`date`) `year`
-> FROM (
-> SELECT
-> DATE_ADD(CONCAT(#`year`, '-01-01'), INTERVAL `s`.`seq` DAY) `date`
-> FROM
-> seq_0_to_365 `s`
-> ) `der`
-> ) `der`
-> WHERE
-> `der`.`week` = #`week` AND
-> `der`.`year` = #`year`;
+------------+------+------+
| date | week | year |
+------------+------+------+
| 2016-06-06 | 23 | 2016 |
| 2016-06-07 | 23 | 2016 |
| 2016-06-08 | 23 | 2016 |
| 2016-06-09 | 23 | 2016 |
| 2016-06-10 | 23 | 2016 |
| 2016-06-11 | 23 | 2016 |
| 2016-06-12 | 23 | 2016 |
+------------+------+------+
7 rows in set (0.01 sec)
Solved, I re-wrote the stored procedure:
exitProc:BEGIN
#--
# Procedure:
# weekFromDate
#
# Parameters:
# vcCompKey, the key associated with the company
# dtDate, the date to translate
# dtOutSOW, returned start of week date
# siOutWeek, returned week number
# siOutYear, returned year
#--
DECLARE siDIY SMALLINT; #Day in year
DECLARE siFDOW SMALLINT; #First day of week
DECLARE siGoBack SMALLINT; #Flag used to check for last year
DECLARE siRmonth SMALLINT; #Reference Month
DECLARE siRyear SMALLINT; #Reference Year
DECLARE dtSOY DATE; #Date of start of year
DECLARE vcFMDOY VARCHAR(12);#First month and day of year
DECLARE vcFDOW VARCHAR(12);#First day of the week
DECLARE vcDYSOW VARCHAR(80);#Days of week
#Get the first day of the week for the specified company
SET vcFDOW = vcGetParamValue(vcCompKey, 'Var:First day of week');
IF (vcFDOW IS NULL) THEN
#No entry found, abort!
LEAVE exitProc;
END IF;
#Get the first month and day of the year for the specified company
SET vcFMDOY = vcGetParamValue(vcCompKey, 'Var:First day of year');
IF (vcFMDOY IS NULL) THEN
#No entry found, abort!
LEAVE exitProc;
END IF;
#Set-up days of week
SET vcDYSOW = 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday';
#Get the first day of the week index base 1
SET siFDOW = FIND_IN_SET(LOWER(vcFDOW), LOWER(vcDYSOW)) - 1;
#Get the reference month and year
SET siRmonth = MONTH(dtDate);
SET siRyear = YEAR(dtDate);
SET dtSOY = DATE(CONCAT(siRyear, '/', vcFMDOY));
#Calculate the start of week date
SET dtOutSOW = DATE_SUB(dtDate, INTERVAL (DAYOFWEEK(dtDate) - siFDOW) DAY) + 1;
#Calculate the day in year
SET siDIY = DATEDIFF(dtOutSOW, dtSOY);
#Do we need to go back to the end of the previous year?
SET siGoBack = YEAR(dtDate) - YEAR(dtOutSOW);
IF siGoBack < 0 Or siDIY < 0 Or dtDate < dtOutSOW THEN
#Yes
IF YEAR(dtOutSOW) = YEAR(dtDate) THEN
SET dtOutSOW = DATE_SUB(dtOutSOW, INTERVAL 7 DAY);
END IF;
SET dtSOY = DATE(CONCAT(YEAR(dtOutSOW), '/', vcFMDOY));
SET siDIY = DATEDIFF(dtOutSOW, dtSOY);
END IF;
#Calculate the week no. and year
SET siOutWeek = (siDIY / 7) + 1;
SET siOutYear = YEAR(dtOutSOW);
END
This routine does make use of other tables in my database and allows for companies to have different start of years.
As a test, I will find the start of the current week, first note:
mysql> SELECT NOW(), WEEK(NOW());
+---------------------+-------------+
| NOW() | WEEK(NOW()) |
+---------------------+-------------+
| 2016-06-18 12:10:58 | 24 |
+---------------------+-------------+
Then this is the meat of the function:
mysql> SELECT '2016-01-01'
+ INTERVAL 7*24
- DAYOFWEEK('2016-01-01')
+ 1 DAY;
+----------------------------------------------------------------+
| '2016-01-01' + INTERVAL 7*24 - DAYOFWEEK('2016-01-01') + 1 DAY |
+----------------------------------------------------------------+
| 2016-06-12 |
+----------------------------------------------------------------+
'2016-01-01' is the beginning of the year in question.
24 is the WEEK() number.
+ 1 DAY is to compensate for start of week.
Something else needs to be done for handling your option of what day the week starts week.
Some correction for user1014010's answer. When week starts with Monday you'll receive date of next week for Sundays. There's my correction:
SELECT DATE(DATE_ADD(NOW(), INTERVAL -((5 + DAYOFWEEK(NOW())) % 7) DAY)) AS WeekStartDate
I have an SQL Query which calculates a quote of two sums. This quote shall be calculated on a weekly basis. Therefore is used Datepart('ww', Date, 2, 2) to get the calendar week.
This query worked fine during 2014, but now I am facing a problem.
The users choses with date pickers in a form from and to date which is then used in the where clause to only select relevant records.
If they chose a from date from past year (e.g. 2014) and a to date from this year my query shows irrelvant data, because it does not cosinder the year just the calendar week.
So, how group by calendar week AND only chose records from the correct year.
SELECT DatePart('ww',Date,2,2) AS WEEK,
Year(Workload_Date) AS [YEAR],
(SUM(Value1)+SUM(Value2))AS [Total1],
(SUM(Value3)+SUM(Value4)) AS [Total2],
((SUM(Value1)+SUM(Value2))/(SUM(Value3)+SUM(Value4))) AS [Quote]
FROM tbl
WHERE DatePart('ww',Date,2,2) Between DatePart('ww',FromDatePickerField,2,2) And DatePart('ww',ToDatePickerField,2,2)
GROUP BY DatePart('ww',Date,2,2), Year(Date)
ORDER BY Year(Date), DatePart('ww',Date,2,2);
The table contains one record per day.
Date | Value1 | Value2 | Value3 | Value4 |
01.01.2014 | 4 | 3 | 2 | 3 |
02.01.2014 | 4 | 3 | 9 | 3 |
03.01.2014 | 4 | 3 | 4 | 1 |
04.01.2014 | 4 | 3 | 1 | 3 |
...
01.01.2015 | 4 | 3 | 6 | 3 |
02.01.2015 | 4 | 3 | 3 | 7 |
You could base your logic on Week_Ending_date instead of the week number and year, that way you could aggregate all you data on a weekly basis and let SQL handle the week/year detection logic.
Incase, you have a date range that spans 2 years, even then the calculations will be based on the week_ending_date and should work out correctly.
Something like...
SELECT DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE) AS WEEK_ENDING_DATE
,Year(DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE)) AS [YEAR]
,(SUM(Value1) + SUM(Value2)) AS [Total1]
,(SUM(Value3) + SUM(Value4)) AS [Total2]
,((SUM(Value1) + SUM(Value2)) / (SUM(Value3) + SUM(Value4))) AS [Quote]
FROM tbl
WHERE DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE) BETWEEN DATEADD(dd, 7-(DATEPART(dw, FromDatePickerField)), FromDatePickerField)
AND DATEADD(dd, 7-(DATEPART(dw, ToDatePickerField)), ToDatePickerField)
and date >= FromDatePickerField
and date <= ToDatePickerField
GROUP BY DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE)
ORDER BY DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE)
I see 2 possible solutions.
1) You stop the user while using the datepicker on the form whenever the years of the two datepickers are different. Maybe a MsgBox or something,
2) You change the to-DatePicker to the from-Year.
DECLARE #MinDate DATE = '01.11.2014'
DECLARE #tmpDate DATE = '12.02.2015'
DECLARE #MaxDate DATE = (CASE WHEN Year(#MinDate) != Year(#tmpDate) THEN Convert(DATE, '31.12.' + Convert(VARCHAR(4), Year(#MinDate))) ELSE #tmpDate END)
SELECT #MinDate, #tmpDate, #MaxDate
Result :
minDate tmpDate maxDate
01.11.2014 12.02.2015 31.12.2014