I am trying to find the week number of a particular month given a date, so I want to know which week is that for given month
Example if I enter
2016 Feb 2 ---> Week 1
2016 Feb 9 ---> Week 2
2016 June 2 ---> week 1
2016 Jan 25 ---> week 5
Can I achieve this in a T-SQL query?
I have seen the following option
DATEPART(wk, BookingTimeStamp)
But that gives the week number of the year, not the month
The idea is to build result per week for a given month
The first result using "SQL Server and Weeks in Month" returned this article. It shows two ways using DATEPART along with other date parsing functions. Here is one solution:
DECLARE #MyDate DATETIME =GETDATE()
SELECT DATEDIFF(WEEK, DATEADD(MONTH, DATEDIFF(MONTH, 0, #MyDate), 0), #MyDate) +1
I ran into the same problem when I wanted to get not the number of the week in the year, but the number of the week in relation to the month.
In my solution, you can divide every day of date by 7. Then all the days of week 1 will be between 0 and 1. All days of week 2 are between 1 and 2, and so on.
This is a simple example of a practice query where you can use the case construct to find the number of the week.
Solution 1 (a bit confusing)
SELECT
case
when CAST(strftime('%d', date) as real) / 7 BETWEEN 0 AND 1 then 'week_1'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 1 AND 2 then 'week_2'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 2 AND 3 then 'week_3'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 3 AND 4 then 'week_4'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 4 AND 5 then 'a_bit_of_week_5'
end as week
FROM cost
WHERE strftime('%Y', date) = '2022';
Solution 2 (simple)
SELECT
case
when CAST(strftime('%d', date) as integer) BETWEEN 1 AND 7 then 'week_1'
when CAST(strftime('%d', date) as integer) BETWEEN 7 AND 14 then 'week_2'
when CAST(strftime('%d', date) as integer) BETWEEN 14 AND 21 then 'week_3'
when CAST(strftime('%d', date) as integer) BETWEEN 21 AND 28 then 'week_4'
when CAST(strftime('%d', date) as integer) BETWEEN 28 AND 31 then 'a_bit_of_week_5'
end as week
FROM cost
WHERE strftime('%Y', date) = '2022';
Deduction of solution 1
If you're just looking for a solution to an issue, then you don't need to read any further. I just want to tell you how I arrived at this solution and why you can trust it.
This solution can be visualized using the Python programming language.
from matplotlib import pyplot as plt
days = [x for x in range(1, 32)]
days_on_seven = [x / 7 for x in range(1, 32)] # Divide each day by 7
print(days_on_seven)
## Result is below
[0.14285714285714285,
0.2857142857142857,
0.42857142857142855,
0.5714285714285714,
0.7142857142857143,
0.8571428571428571,
1.0,
1.1428571428571428,
1.2857142857142858,
1.4285714285714286,
1.5714285714285714,
1.7142857142857142,
1.8571428571428572,
2.0,
2.142857142857143,
2.2857142857142856,
2.4285714285714284,
2.5714285714285716,
2.7142857142857144,
2.857142857142857,
3.0,
3.142857142857143,
3.2857142857142856,
3.4285714285714284,
3.5714285714285716,
...
3.857142857142857,
4.0,
4.142857142857143,
4.285714285714286,
4.428571428571429]
Visualisation
As you can see, we get an array of increasing numbers. Let's graph the resulting numbers for each day of the month.
from matplotlib import pyplot as plt
import seaborn as sns
sns.lineplot(x=days, y=days_on_seven)
plt.xlabel("Number of day in month")
plt.ylabel("Result of division by 7")
plt.title("day / 7 plot")
The graph below by link
https://i.stack.imgur.com/SIVfT.png
The graph is very crude, but you can see, as noted earlier, that all the days of week 1 will be between 0 and 1. All days of week 2 are between 1 and 2, and so on.
I hope my decision was helpful and interesting to you.
Related
How I can calculate difference between each week and 2 weeks ago, for a given measure in MDX?
WEEK MEASURE NEW_MEASURE
---- ------- -----------
1 10 NULL
2 5 NULL
3 20 10
4 10 5
5 40 20
Below Members work, but only without CASE statement so I have to calculate it separately:
MEMBER [Measures].[12 Week temp]
AS
([Date].[Week Year].CurrentMember, [Measures].[Total Orders]) -
([Date].[Week Year].lag(13), [Measures].[Total Orders])
MEMBER [Measures].[12 Week]
AS
CASE WHEN [Measures].[12 Week temp] = [Measures].[Total Orders] THEN 0 ELSE [Measures].[12 Week temp] END
I have SSRS report like below with Boolean parameter to show 12h view or 24h view. To fit report into single screen the 24h report need to group by every 2hr.
07:00 08:00 09:00 10:00 11:00 12:00 13:00 14:00 ...
Line 1 25 30 24 26 25 25 30 30 ...
08:00 10:00 12:00 14:00 ...
Line 1 55 50 50 60 ...
The query for the dataset is:
SELECT LineID
,Hour
,HourValue
,Target
FROM vwData
ORDER BY LineID, CASE WHEN [Hour] > 6 THEN - 1 ELSE [Hour] END
How can I achieve this?
This declares your bit variable (which should be true when they want the 24 hour view - false when 12 hour)
DECLARE #24Hour bit = 0
SELECT CASE WHEN #24Hour = 0
THEN Hour
ELSE Hour + (Hour % 2)
END AS [HourGroup]
,SUM(Target) AS [TargetTotal]
FROM vwData
GROUP BY CASE WHEN #24Hour = 0
THEN Hour
ELSE Hour + (Hour % 2)
END
If they want the 24 hour view, we make hour = hour + hour % 2. (7 = 8, 8=8, 9=10, etc., etc.). If you had a more complex query, I would suggest reading up on cross apply, but this is so simple I think this will suffice. The grouping by makes sure to aggregate the REAL 7 and REAL 8 hour records (which will both be returned as "8", if using the 24 hour view). If you don't group your results, you will get two 8 oclock records - one with the REAL 7 hour total and one with the REAL 8 hour total.
EDIT:
Since you didn't include the schema of your DB, I'm guessing that 'Target' is the value being summated, but it could just as easily be 'HourValue'. Furthermore, I have no idea why you would need LineID, so I omitted it from my answer. But you can easily modify that if it's inaccurate. In the future, you should provide some sample data and your database schema so that others aren't forced to make assumptions or guess.
You could add a calculated field with a value given by something like this: `Fields!Hour.Value + Fields!Hour.Value Mod 2' and then group on that field, using a parameter to choose the Group By field in the report (Your new field or the actual hour value).
I have an array that I want to add years and months sequentially to using a SAS program:
Original:
ID
1
2
3
End result:
ID YEAR; MONTH
1 2014 11
1 2014 12
1 2015 1
1 2015 2
1 2015 3
2 2014 11
2 2014 12
2 2015 1
2 2015 2
2 2015 3
3 2014 11
3 2014 12
3 2015 1
3 2015 2
3 2015 3
I also need to set the upper lower limits for the years and months I want to add to the table.
Any help is appreciated. Thanks!
As the comments suggest, I'm taking a bit of a guess on what you're looking for. From what you're asking, I'd recommned using a data step to loop through your original data, outputing multiple rows for each line in the original data.
This uses intnx to advance to the next month (intnx documentation)
*Enter start and end date here;
%Let startdt = '01NOV2014'd;
%Let enddt = '01MAR2015'd;
data want (drop=_date);
set original;
*Create multiple records for each observation in 'original'- one for each month;
_date = &startdt;
DO UNTIL (_date > &enddt);
year = year(_date);
month = month(_date);
output;
*Advance to next month;
_date = intnx('month', _date, 1, 'beginning');
END;
run;
Sample data: (assume year_month_record is the first day of the month and is datetime data type)
location item year_month_record type visits1 visits2
ABC111 11JF445553 2014-01 sales 3 5
ABC111 11JF445553 2014-02 sales 3 6
ABC111 11JF445553 2014-03 sales 2 8
ABC111 11JF445553 2014-04 sales 2 4
ABC111 22WZ777814 2014-02 sales 3 5
ABC111 55RR342013 2014-01 nsales 1 2
For the given sample data, I need to count how many times records with the same location and item appear within specified intervals. In addition, I need to grab the maximum value for specified interval / time frame and sum it up based on location, item_number and type.
The output should look something like this:
location year_month_record length_months type count_unique_visits sum_max_visits1 sum_max_visits2
ABC111 2014-01 3 sales 4 6 13
ABC111 2014-02 3 sales 4 6 12
ABC111 2014-03 3 sales 2 4 12
ABC111 2014-04 3 sales 1 2 4
ABC111 2014-01 3 nsales 1 1 2
notes for calculating visits1 / visits2 above
example output of record 1: max(of item 11JF445553) = 3 + max(item 22WZ77781) = 3. Sum = 6 (item 55RR342013 has a different type). Note 2. All records with max summed up are within "length_months" specified of 3 months. 2014-01 through 2014-03.
new "type" will cause new grouping to start
Additional notes:
count_unique_visits is the count for each record within date range
length_months is defined prior to execution and can be hardcoded
current year_month_record + length_months (i.e. 2014-01 year_month_record with length_months = 3) is 01/2014 through 03/2014
I've tried creating a recursive CTE to select the count and max, but i'm doing something wrong.
Basically, I need to be able to recursively, grab a count and the max visit1/2 for a given interval.
Starting with 01/2014, it would need to look for the max(visits1/2) for the next three months (basically, 01/2014 - 04/2014) and return those. In 02/2014, it would use the range of 02/2014 through 05/2014 and return the max there as well. It would continue this throughout the recordset. The interval would be 3 months, but then I could copy the query and replace with 6 months and so on and so forth.
Closing this topic to ask a more targeted/specific question.
Any help would be appreciated.
You can use a combination of a groupping subquery followed by a cross apply subquery:
DECLARE #len int = 3
SELECT grp.*, SUM(ca.cuv) count_unique_visits, SUM(ca.visits1) sum_max_visits1, SUM(ca.visits2) sum_max_visits2
FROM
(SELECT v.location, v.year_month_record, v.type
FROM Visits v
GROUP BY v.location, v.year_month_record, v.type) grp CROSS APPLY
(SELECT COUNT(*) cuv, MAX(visits1) visits1, MAX(visits2) visits2
FROM Visits ca_v
WHERE ca_v.location = grp.location AND grp.type = ca_v.type AND ca_v.year_month_record >= grp.year_month_record AND
ca_v.year_month_record < DATEADD(month, #len, grp.year_month_record)
GROUP BY ca_v.item
) ca
GROUP BY grp.location, grp.year_month_record, grp.type
ORDER BY grp.type DESC, grp.year_month_record
You can see the results in this SQLFiddle.
NOTE: As I wrote in the comment to the original question, I suspect you have a mistake in the requested output, if not, please explain...
I have calculation which calculates the value from previous year, realized in following way:
([Measures].[SalesAmount LY], Leaves([Time])) =
(
PARALLELPERIOD
(
[Time].[Year - Month - Day].[Year],
1,
[Time].[Year - Month - Day].currentmember
),
[MEASURES].[SalesAmount]
)
I am doing it this way, because using only PARALLELPERIOD results in incorrect total calculations when selecting only some months for example. And it works just fine.
However, now I am facing following issue with leap year:
Month SalesAmount SalesAmount LY
------------- ------------ ---------------
January 2012 1000
February 2012 1100 --Leap year - 29.February
January 2013 1200 1000
February 2013 1300 1050
The missing value (1100 - 1050 = 50 in this case) is exactly equal to the value from 29.2.2012.
Does anyone have an idea how to solve this issue? How to get leap year working correctly, while still maintaining correct totals behavior? Any help would be greately appreciated.
This can help?
PARALLELPERIOD
(
[Time].[Year - Month - Day].[Year],
1,
ISLEAF([Time].[Year - Month - Day].currentmember),
Ancestor([[Time].[Year - Month - Day].currentmember, [Time].[Year - Month - Day].[Month]),
[Time].[Year - Month - Day].currentmember)
),
[MEASURES].[SalesAmount]
if the current member is a Day, it will refer to his month parent, so it doesn't matter if the month has 28 or 29 days.