Incremental Week Number in SQL Server - sql

I am trying to increase week number so that the week number of the year will not repeat
For example 18/12/2016 is week number 51, 25/12/2016 is week number 52 and 01/01/2017 is week number 1, 08/01/2017 is week number 2.
What I want is 18/12/2016 week NO 51, 25/12/2016 week No 52, week No 53, 08/01/2017 No week 54 etc so that no week will repeat
See the sample of my dataset and query I have tried
DECLARE #Table1 table (Week_End_Date DATE)
INSERT INTO #Table1
VALUES ('2016-04-03'), ('2016-04-10'), ('2016-04-17'), ('2016-04-24'),
('2016-05-01'), ('2016-05-08'), ('2016-05-15'), ('2016-05-22'),
('2016-05-29'), ('2016-06-05'), ('2016-06-12'), ('2016-06-19'),
('2016-06-26'), ('2016-07-03'), ('2016-07-10'), ('2016-07-17'),
('2016-07-24'), ('2016-07-31'), ('2016-08-07'), ('2016-08-14'),
('2016-08-21'), ('2016-08-28'), ('2016-09-04'), ('2016-09-11'),
('2016-09-18'), ('2016-09-25'), ('2016-10-02'), ('2016-10-09'),
('2016-10-16'), ('2016-10-23'), ('2016-10-30'), ('2016-11-06'),
('2016-11-13'), ('2016-11-20'), ('2016-11-27'), ('2016-12-04'),
('2016-12-11'), ('2016-12-18'), ('2016-12-25'), ('2017-01-01'),
('2017-01-08'), ('2017-01-15'), ('2017-01-22'), ('2017-01-29'),
('2017-02-05'), ('2017-02-12'), ('2017-02-19'), ('2017-02-26'),
('2017-03-05'), ('2017-03-12'), ('2017-03-19'), ('2017-03-26'),
('2017-04-02'), ('2017-04-09'), ('2017-04-16'), ('2017-04-23'),
('2017-04-30'), ('2017-05-07'), ('2017-05-14'), ('2017-05-21'),
('2017-05-28'), ('2017-06-04'), ('2017-06-11'), ('2017-06-18'),
('2017-06-25'), ('2017-07-02'), ('2017-07-09'), ('2017-07-16'),
('2017-07-23'), ('2017-07-30'), ('2017-08-06'), ('2017-08-13'),
('2017-08-20'), ('2017-08-27'), ('2017-09-03'), ('2017-09-10'),
('2017-09-17'), ('2017-09-24'), ('2017-10-01'), ('2017-10-08'),
('2017-10-15'), ('2017-10-22'), ('2017-10-29'), ('2017-11-05'),
('2017-11-12'), ('2017-11-19'), ('2017-11-26'), ('2017-12-03'),
('2017-12-10'), ('2017-12-17'), ('2017-12-24'), ('2017-12-31'),
('2018-01-07'), ('2018-01-14'), ('2018-01-21'), ('2018-01-28'),
('2018-02-04'), ('2018-02-11'), ('2018-02-18'), ('2018-02-25'),
('2018-03-04'), ('2018-03-11'), ('2018-03-18'), ('2018-03-25'),
('2018-04-01')
Query:
SELECT
Week_End_Date,
DATEPART(WEEK,Week_End_Date) AS WeekNumber,
REPLACE(LEFT(Week_End_Date,7),'-','') +
CASE
WHEN CAST(DATEPART(WEEK, Week_End_Date) AS VARCHAR(2)) IN ('1', '2', '3', '4', '5', '6', '7', '8', '9')
THEN '0' + CAST(DATEPART(WEEK, Week_End_Date) AS VARCHAR(2))
ELSE CAST(DATEPART(WEEK, Week_End_Date) AS VARCHAR(2))
END Wk_NO_Norepeat
FROM
#Table1
ORDER BY
Week_End_Date
Desired output
Date Current week number Expected output
04/12/2016 49 49
11/12/2016 50 50
18/12/2016 51 51
25/12/2016 52 52
01/01/2017 1 53
08/01/2017 2 54
15/01/2017 3 55
22/01/2017 4 56
29/01/2017 5 57

You could use ROW_NUMBER(). If i understand it correctly you may use something like:
DECLARE #Table1 table (Week_End_Date DATE)
INSERT INTO #Table1
VALUES ('2016-04-03'), ('2016-04-10'), ('2016-04-17'), ('2016-04-24'),
('2016-05-01'), ('2016-05-08'), ('2016-05-15'), ('2016-05-22'),
('2016-05-29'), ('2016-06-05'), ('2016-06-12'), ('2016-06-19'),
('2016-06-26'), ('2016-07-03'), ('2016-07-10'), ('2016-07-17'),
('2016-07-24'), ('2016-07-31'), ('2016-08-07'), ('2016-08-14'),
('2016-08-21'), ('2016-08-28'), ('2016-09-04'), ('2016-09-11'),
('2016-09-18'), ('2016-09-25'), ('2016-10-02'), ('2016-10-09'),
('2016-10-16'), ('2016-10-23'), ('2016-10-30'), ('2016-11-06'),
('2016-11-13'), ('2016-11-20'), ('2016-11-27'), ('2016-12-04'),
('2016-12-11'), ('2016-12-18'), ('2016-12-25'), ('2017-01-01'),
('2017-01-08'), ('2017-01-15'), ('2017-01-22'), ('2017-01-29'),
('2017-02-05'), ('2017-02-12'), ('2017-02-19'), ('2017-02-26'),
('2017-03-05'), ('2017-03-12'), ('2017-03-19'), ('2017-03-26'),
('2017-04-02'), ('2017-04-09'), ('2017-04-16'), ('2017-04-23'),
('2017-04-30'), ('2017-05-07'), ('2017-05-14'), ('2017-05-21'),
('2017-05-28'), ('2017-06-04'), ('2017-06-11'), ('2017-06-18'),
('2017-06-25'), ('2017-07-02'), ('2017-07-09'), ('2017-07-16'),
('2017-07-23'), ('2017-07-30'), ('2017-08-06'), ('2017-08-13'),
('2017-08-20'), ('2017-08-27'), ('2017-09-03'), ('2017-09-10'),
('2017-09-17'), ('2017-09-24'), ('2017-10-01'), ('2017-10-08'),
('2017-10-15'), ('2017-10-22'), ('2017-10-29'), ('2017-11-05'),
('2017-11-12'), ('2017-11-19'), ('2017-11-26'), ('2017-12-03'),
('2017-12-10'), ('2017-12-17'), ('2017-12-24'), ('2017-12-31'),
('2018-01-07'), ('2018-01-14'), ('2018-01-21'), ('2018-01-28'),
('2018-02-04'), ('2018-02-11'), ('2018-02-18'), ('2018-02-25'),
('2018-03-04'), ('2018-03-11'), ('2018-03-18'), ('2018-03-25'),
('2018-04-01')
DECLARE #FirstWeek INT = (SELECT DATEPART(WEEK,MIN(Week_End_Date))-1 FROM #Table1)
SELECT
Week_End_Date,
ROW_NUMBER() OVER (ORDER BY Week_End_Date)+#FirstWeek AS WeekNumber
FROM
#Table1

Perhaps this does what you want:
select t1.weekenddate, datepart(week, t1.weekenddate) AS WeekNumber,
(first_value(datepart(week, t1.weekenddate)) over (order by t1.weekenddate) +
row_number() over (order by t1.weekenddate) - 1
) as newweeknumber
from #table1 t1;
It calculates the first week number in the data and then just increments the week number by 1 in subsequent rows.

Related

Calculate Date difference between multiple rows SQL

I need to calculate the date difference between multiple rows. The scenario is I have a vehicle that can do inspections throughout the month as well as when the vehicle is assigned to a different project. I want to calculate that how many days that a vehicle is assigned to the project per month or previous month. I have tried multiple ways and I can't get even closer. I am relatively new to stack overflow. Apologies if anything is missing. Please let me know if this can be done. Thank you.
All the columns are in one single table if that helps. Please let me know the query on how to achieve this
I am using SQL server 2017.
Original Data
Expected Output
I am not proud of this solution, but I think it works for you. My approach was to create a table of days and then look at which project the vehicle was assigned to each day. Finally, aggregate by month and year to get the results. I had to do this as a script since you can use aggregate functions in the definitions of recursive CTEs, but you may find a way to do this without needing a recursive CTE.
I created a table variable to import your data so I could write this. Note, I added an extra assignment to test assignments that spanned months.
DECLARE #Vehicles AS TABLE
(
[VehicleID] INT NOT NULL,
[Project] CHAR(2) NOT NULL,
[InspectionDate] DATE NOT NULL
);
INSERT INTO #Vehicles
(
[VehicleID],
[Project],
[InspectionDate]
)
VALUES
(1, 'P1', '2021-08-20'),
(1, 'P1', '2021-09-05'),
(1, 'P2', '2021-09-15'),
(1, 'P3', '2021-09-20'),
(1, 'P2', '2021-10-10'),
(1, 'P1', '2021-10-20'),
(1, 'P3', '2021-10-21'),
(1, 'P2', '2021-10-22'),
(1, 'P4', '2021-11-15'),
(1, 'P4', '2021-11-25'),
(1, 'P4', '2021-11-30'),
(1, 'P1', '2022-02-05');
DECLARE #StartDate AS DATE, #EndDate AS DATE;
SELECT #StartDate = MIN([InspectionDate]), #EndDate = MAX([InspectionDate])
FROM #Vehicles;
;WITH [seq]([n])
AS (SELECT 0 AS [n]
UNION ALL
SELECT [n] + 1
FROM [seq]
WHERE [n] < DATEDIFF(DAY, #StartDate, #EndDate)),
[days]
AS (SELECT DATEADD(DAY, [n], #StartDate) AS [d]
FROM [seq]),
[inspections]
AS (SELECT [VehicleID],
[Project],
[InspectionDate],
LEAD([InspectionDate], 1) OVER (PARTITION BY [VehicleID]
ORDER BY [InspectionDate]
) AS [NextInspectionDate]
FROM #Vehicles),
[assignmentsByDay]
AS (SELECT [d].[d], [i].[VehicleID], [i].[Project]
FROM [days] AS [d]
INNER JOIN [inspections] AS [i]
ON [d].[d] >= [i].[InspectionDate]
AND [d] < [i].[NextInspectionDate])
SELECT [assignmentsByDay].[VehicleID],
[assignmentsByDay].[Project],
MONTH([assignmentsByDay].[d]) AS [month],
YEAR([assignmentsByDay].[d]) AS [year],
COUNT(*) AS [daysAssigned]
FROM [assignmentsByDay]
GROUP BY [assignmentsByDay].[VehicleID],
[assignmentsByDay].[Project],
MONTH([assignmentsByDay].[d]),
YEAR([assignmentsByDay].[d])
ORDER BY [year], [month], [assignmentsByDay].[VehicleID], [assignmentsByDay].[Project]
OPTION(MAXRECURSION 0);
And the output is:
VehicleID
Project
month
year
daysAssigned
1
P1
8
2021
12
1
P1
9
2021
14
1
P2
9
2021
5
1
P3
9
2021
11
1
P1
10
2021
1
1
P2
10
2021
20
1
P3
10
2021
10
1
P2
11
2021
14
1
P4
11
2021
16
1
P4
12
2021
31
1
P4
1
2022
31
1
P4
2
2022
4
I think you are looking for this:
select vehicleId
, Project
, month(inspectiondate) month
, year(inspectiondate) year
, datediff(day , min(inspectiondate), case when max(inspectiondate) = min(inspectiondate) then eomonth(min(inspectiondate)) else max(inspectiondate) end) days
from Vehicles
group by vehicleId, Project , month(inspectiondate), year(inspectiondate)
This query in for each month/year for each specific vehicle in a project in that month/year , you get the max and min inspection date and calculate the difference.
db<>fiddle here

SQL Server - Calculating target vs actual values per week for sales overview

I am new to SQL. I am trying to create an over view using these 2 tables
Actual sale
Week_Year
Unit 34_2020 35_2020 36_2020 37_2020
Unit 1 10 12 15 19
Unit 2 10 12 15 19
Unit 3 10 12 15 19
Target sale
Unit Total to be sold Start Date End Date
Unit 1 50 24-08-20 24-09-20
Unit 2 1000 18-01-20 01-01-21
Unit 3 1000 05-02-20 01-10-20
To combine into this resulting view with Targets and Actuals:
Unit 1 Unit 2 Unit 3
Week Target Actual Week Target Actual Week Target Actual
34_2020 11 10 3_2020 20 10 6_2020 20 10
35_2020 24 12 4_2020 40 12 7_2020 40 12
36_2020 36 15 5_2020 60 15 8_2020 60 15
37_2020 50 19 6_2020 80 19 9_2020 80 19
. 100 . . 100 .
36_2020 120 95 36_2020 700 650
37_2020 140 100 37_2020 800 700
. . 38_2020 .
. . 39_2020 .
. . 40_2020 1000
1_2021 1000
where column Target is 'Total to be sold' spread linearly between the available weeks.
How can I achieve this using SQL Server? Any inputs much appreciated. Thanks.
In order to make the week numbers (34, 35, 36, 37) correspond to system weeks the variable #start_wk_no sets the starting point. The actual sales needs to be unpivoted to join with projected sales. The query uses a tally (or numbers) function to generate the rows.
Data
drop table if exists dbo.test_actuals;
go
create table dbo.test_actuals(
Unit varchar(100) not null,
[34_2020] int not null,
[35_2020] int not null,
[36_2020] int not null,
[37_2020] int not null);
--select * from dbo.test_actuals
insert dbo.test_actuals values
('Unit 1', 10, 12, 15, 19),
('Unit 2', 10, 12, 15, 19),
('Unit 3', 10, 12, 15, 19);
drop table if exists dbo.test_target;
go
create table dbo.test_target(
Unit varchar(100) not null,
TotalToSell int not null,
StartDate date not null,
EndDate date not null)
insert dbo.test_target values
('Unit 1', 50, '08-24-2020', '09-24-2020'),
('Unit 2', 1000, '01-18-2020', '01-01-2021'),
('Unit 3', 1000, '02-05-2020', '10-01-20');
Query
/* based on system weeks, what is the start point */
declare
#start_wk_no int=6250;
;with unpvt_actuals_cte as (
select a.Unit, v.*
from
dbo.test_actuals a
cross apply
(values (34, [34_2020]), (35, [35_2020]), (36, [36_2020]), (36, [36_2020]), (37, [37_2020])) v([Week], act_sales))
select
t.Unit,
wd.wk_proj [Week],
isnull(act.act_sales, 0) [Actual],
TotalToSell/(wk_diff.wk_diff*1.0) [Target],
sum(TotalToSell/(wk_diff.wk_diff*1.0)) over (partition by t.Unit order by wd.wk_proj) Cum_Target
from
dbo.test_target t
cross apply
(select datediff(wk, t.StartDate, t.EndDate) wk_diff) wk_diff
cross apply
dbo.fnTally(0, wk_diff.wk_diff-1) f
cross apply
(select dateadd(wk, f.n, t.StartDate) wk_dt) wk
cross apply
(select datediff(week, 0, wk.wk_dt)-#start_wk_no wk_proj) wd
left join
unpvt_actuals_cte act on t.Unit=act.Unit
and wd.wk_proj=act.[Week];

Group consecutive ranges

I have a datatable with many rows and I would like to conditionally group two columns, namely Begin and End. These columns stand for a certain month in which the associated person was doing something. Here is some sample data (you can use R to read in, or find the pure tables below if you dont use R):
# base:
test <- read.table(
text = "
1 A mnb USA prim 4 12
2 A mnb USA x 13 15
3 A mnb USA un 16 25
4 A mnb USA fdfds 1 2
5 B ghf CAN sdg 3 27
6 B ghf CAN hgh 28 29
7 B ghf CAN y 24 31
8 B ghf CAN ghf 38 42
",header=F)
library(data.table)
setDT(test)
names(test) <- c("row","Person","Name","Country","add info","Begin","End")
out <- read.table(
text = "
1 A mnb USA fdfds 1 2
2 A mnb USA - 4 25
3 B ghf CAN - 3 31
4 B ghf CAN ghf 38 42
",header=F)
setDT(out)
names(out) <- c("row","Person","Name","Country","add info","Begin","End")
The grouping should be done as follows: If person A did hiking from month 4 to month 15 and travelling from month 16 to month 24, I would group the consecutive (i.e. without break) activity from month 4 to month 24. If afterwards person A did surfing from month 25 to month 28, I would also add this, and the whole group activity would last from 4 to 28.
Now problematic are cases were there are overlapping periods, for example person A might also do fishing from 11 to 31, so the whole thing would become 4 to 31. However, if person A did something from 1 to 2, that would be a separate activity (as compared to 1 to 3, which would also have to be added, because 3 is connected to 4). I hope that was clear, if not you can find more examples in the above code.
I am using datatable, because my dataset is quite large. I have started with sqldf so far, but it's problematic if you have so many activities per person (let's say 8 or more).
Can this be done in datatable, or plyr, or sqldf?
Please note: I am also looking for an answer in SQL because I could use that directly in sqldf or try to convert it to another language. sqldf supports (1) the SQLite backend database (by default), (2) the H2 java database, (3) the PostgreSQL database and (4) sqldf 0.4-0 onwards also supports MySQL.
Edit: Here are the 'pure' tables:
In:
Person Name Country add info Begin End
A mnb USA prim 4 12
A mnb USA x 13 15
A mnb USA un 16 25
A mnb USA fdfds 1 2
B ghf CAN sdg 3 27
B ghf CAN hgh 28 29
B ghf CAN y 24 31
B ghf CAN ghf 38 42
Out:
A mnb USA fdfds 1 2
A mnb USA - 4 25
B ghf CAN - 3 31
B ghf CAN ghf 38 42
I did this one which worked in my tests and almost all the main databases out there should normally run it... I underscored my columns... please, change the names before test:
SELECT
r1.person_,
r1.name_,
r1.country_,
CASE
WHEN max(r2.begin_) = max(r1.begin_)
THEN max(r1.info_) ELSE '-'
END info_,
MAX(r2.begin_) begin_,
r1.end_
FROM stack_39626781 r1
INNER JOIN stack_39626781 r2 ON 1=1
AND r2.person_ = r1.person_
AND r2.begin_ <= r1.begin_ -- just optimizing...
LEFT JOIN stack_39626781 r3 ON 1=1
AND r3.person_ = r1.person_
-- matches when another range overlaps this range end
AND r3.end_ >= r1.end_ + 1
AND r3.begin_ <= r1.end_ + 1
LEFT JOIN stack_39626781 r4 ON 1=1
AND r4.person_ = r2.person_
-- matches when another range overlaps this range begin
AND r4.end_ >= r2.begin_ - 1
AND r4.begin_ <= r2.begin_ - 1
WHERE 1=1
-- get rows
-- with no overlaps on end range and
-- with no overlaps on begin range
AND r3.person_ IS NULL
AND r4.person_ IS NULL
GROUP BY
r1.person_,
r1.name_,
r1.country_,
r1.end_
This query is based on the fact that any range from output have no connections/overlaps. Lets say that, for an output of five ranges, five begins and five ends exists with no connections/overlaps. Find and associate them should be easier than generating all connections/overlaps. So, what this query does is:
Find all ranges per person with no overlaps/connections on its end value;
Find all ranges per person with no overlaps/connections on its begin value;
These are the valid ranges, so associate them all to find the correct pair;
For each person and end, the correct begin pair is the maximum one available which value is equal or lesser than this end... it's easy to validate this rule... first, you can't have a begin greater than an end... also, if you have two or more possible begins lesser than end, e. g., begin1 = end - 2 and begin2 = end - 5, selecting the lesser one (begin2) makes the greater one (begin1) an overlap of this range.
Hope it helps.
If you are working with SQL Server 2012 or above, you can use the LAG and LEAD functions to build up your logic to arrive at your final desired dataset. Those functions have also been available in Oracle since Oracle 8i, I believe.
Below is a solution that I created in SQL Server 2012, which should help you. The example values you provided are loaded into a temporary table to represent what you referred to as your first "pure table." Using those two functions, along with OVER clause, I arrived at your final dataset with the following T-SQL code below. I left some of the commented out lines in the code to show how I was able to build up the overall solution piece-by-piece, which accounts for the various scenarios placed into the CASE statement for the GapMarker column that acts as grouping flag.
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL
DROP TABLE #MyTable
CREATE TABLE #MyTable (
Person CHAR(1)
,[Name] VARCHAR(3)
,Country VARCHAR(10)
,add_info VARCHAR(10)
,[Begin] INT
,[End] INT
)
INSERT INTO #MyTable (Person, Name, Country, add_info, [Begin], [End])
VALUES ('A', 'mnb', 'USA', 'prim', 4, 12),
('A', 'mnb', 'USA', 'x', 13, 15),
('A', 'mnb', 'USA', 'un', 16, 25),
('A', 'mnb', 'USA', 'fdfds', 1, 2),
('B', 'ghf', 'CAN', 'sdg', 3, 27),
('B', 'ghf', 'CAN', 'hgh', 28, 29),
('B', 'ghf', 'CAN', 'y', 24, 31),
('B', 'ghf', 'CAN', 'ghf', 38, 42);
WITH CTE
AS
(SELECT
mt.Person
,mt.Name
,mt.Country
,mt.add_info
,mt.[Begin]
,mt.[End]
--,LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End])
--,CASE WHEN [End] + 1 = LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End])
-- --AND LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]) = LEAD([End], 1) OVER (PARTITION BY mt.Person ORDER BY [End])
-- THEN 1
-- ELSE 0
-- END AS Grp
--,MARKER = COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [End]))
,CASE
WHEN mt.[End] + 1 = COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [End])) OR
1 + COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [End])) = mt.[Begin] OR
COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin])) BETWEEN mt.[Begin] AND mt.[End] OR
[End] BETWEEN LAG([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]) AND LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]) THEN 1
ELSE 0
END AS GapMarker
,InBetween = COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]))
,EndInBtw = LAG([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin])
,LagEndInBtw = LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin])
FROM #MyTable mt
--ORDER BY mt.Person, mt.[Begin]
)
SELECT DISTINCT
X.Person
,X.[Name]
,X.Country
,t.add_info
,X.MinBegin
,X.MaxEnd
FROM (SELECT
c.Person
,c.[Name]
,c.Country
,c.add_info
,c.[Begin]
,c.[End]
,c.GapMarker
,c.InBetween
,c.EndInBtw
,c.LagEndInBtw
,MIN(c.[Begin]) OVER (PARTITION BY c.Person, c.GapMarker ORDER BY c.Person) AS MinBegin
,MAX(c.[End]) OVER (PARTITION BY c.Person, c.GapMarker ORDER BY c.Person) AS MaxEnd
--, CASE WHEN c.[End]+1 = c.MARKER
-- OR c.MARKER +1 = c.[Begin]
-- THEN 1
-- ELSE 0
-- END Grp
FROM CTE AS c) X
LEFT JOIN #MyTable AS t
ON t.[Begin] = X.[MinBegin]
AND t.[End] = X.[MaxEnd]
AND t.Person = X.Person
ORDER BY X.Person, X.MinBegin
--ORDER BY Person, [Begin]
And here's a screen shot of the results that match your desired final dataset:

Dates by quarter

I would like to count number of birthdays by quarters in SQL Server
i.e. Between Jan 1 - March 31, April 1 - June 30, July 1 - Sep 30 & Oct 1 - Dec 31.
Please help me with the Date function in Sql Server to do this.
I pass BDate as DATETIME . I want to use this BDate to populate 4 fields of type int with counts for number of birthdays in each quarter.
Thanks.
As long as your "BDate" column is datetime, you can achieve this quite easily using this query:
SELECT DATEPART(QUARTER,BDATE), COUNT(*)
FROM TABLE1
GROUP BY DATEPART(QUARTER,BDATE)
ORDER BY DATEPART(QUARTER,BDATE) ASC
Here's a working fiddle using some random data: http://sqlfiddle.com/#!6/7734b/1
Data set up:
create table test (
d1 varchar(10),
d2 datetime
);
insert into test (d1,d2) values ('2015-01-28','2015-01-28');
insert into test (d1,d2) values ('2015-02-13','2015-02-13');
insert into test (d1,d2) values ('2015-07-19','2015-07-19');
insert into test (d1,d2) values ('2015-11-04','2015-11-04');
If you just want to get counts for each of the quarters present in your data:
select DATEPART(QUARTER, d2), count(*)
from test
group by DATEPART(QUARTER, d2);
You can use d1 or d2 (SQL Server will handle the varchar or datetime properly).
If you want to include all four quarters, even if they're not present in your data:
select qs.q, count(t.d2) as c
from (
SELECT 1 as q
UNION ALL
SELECT 2 as q
UNION ALL
SELECT 3 as q
UNION ALL
SELECT 4 as q) qs
left join test t
on qs.q = DATEPART(QUARTER, t.d2)
group by qs.q;
Again, you can use either d1 or d2, it doesn't matter. The "qs" query just gets the numbers 1 to 4, then that is outer joined to the table with the birth dates.
Please try this:
--replace '19000102' with your int column
select SUM(CASE WHEN DATEPART(MONTH,CONVERT (datetime,convert(char(8),19000102)))
IN (1,2,3)
THEN 1
ELSE 0
END) as 'Q1',
SUM(CASE WHEN DATEPART(MONTH,CONVERT (datetime,convert(char(8),19000102)))
IN (4,5,6)
THEN 1
ELSE 0
END) as 'Q2',
SUM(CASE WHEN DATEPART(MONTH,CONVERT (datetime,convert(char(8),19000102)))
IN (7,8,9)
THEN 1
ELSE 0
END) as 'Q3',
SUM(CASE WHEN DATEPART(MONTH,CONVERT (datetime,convert(char(8),19000102)))
IN (10,11,12)
THEN 1
ELSE 0
END) as 'Q4'

Need to calculate the average of cost based on a per fiscal week basis

I hope someone can help with this issue I have, which is I am trying to work out a weekly average from the following data example:
Practice ID Cost FiscalWeek
1 10.00 1
1 33.00 2
1 55.00 3
1 18.00 4
1 36.00 5
1 24.00 6
13 56.00 1
13 10.00 2
13 24.00 3
13 30.00 4
13 20.00 5
13 18.00 6
What I want is to group by the Practice ID but work out the average for each practice (there are over 500 of these not just those above) and work this out for each week so for example at Week 1 there will be no average, but Week 2 will be the average of Weeks 1 and 2, then Week 3 will be the average of Weeks 1, 2 and 3 and then so on. I need to then show this by Practice ID and for each Fiscal Week.
At the moment I have some code that is not pretty and there has to be an easier way, this code is:
I pass all the data into a table variable then using a CTE I then use case statements to set each individual week like:
CASE WHEN fiscalweek = 1 THEN cost ELSE 0 END AS [1],
CASE WHEN fiscalweek = 2 THEN cost ELSE 0 END AS [2],
CASE WHEN fiscalweek = 3 THEN cost ELSE 0 END AS [3]
This would then bring back the week 1 cost and so on into it's own column e.g. 1, 2, 3 etc. , then I've used a second CTE to sum the columns for each week so for example to work out week 6 I would use this code:
sum([1]) as 'Average Wk 1',
sum([1]+[2])/2 as 'Average Wk 2',
sum([1]+[2]+[3])/3 as 'Average Wk 3',
sum([1]+[2]+[3]+[4])/4 as 'Average Wk 4',
sum([1]+[2]+[3]+[4]+[5])/5 as 'Average Wk 5'
sum([1]+[2]+[3]+[4]+[5]+[6])/6 as 'Average Wk 6'
I've thought about various different ways of working out this average accurately in T-SQL so I can then drop this into SSRS eventually. I've thought about using a While..Loop, Cursor but failing to see an easy way of doing this.
You are looking for the cumulative average of the averages. In databases that support window/analytic functions, you can do:
select fiscalweek, avg(cost) as avgcost,
avg(avg(cost)) over (order by fiscalweek) as cumavg
from practices p
group by fiscalweek
order by 1;
If you don't have window functions, then you need to use some form of correlated subquery or join:
select p1.fiscalweek, avg(p1.avgcost)
from (select fiscalweek avg(cost) as avgcost
from practices p
group by fiscalweek
) p1 join
(select fiscalweek avg(cost) as avgcost
from practices p
group by fiscalweek
) p2
on p12 <= p1
group by p1.fiscalweek
order by 1;
I do want to caution you that you are calculating the "average of averages". This is different from the cumulative average, which could be calculated as:
select fiscalweek,
(sum(sum(cost)) over (order by fiscalweek) /
sum(count(*)) over (order by fiscalweek)
) avgcost
from practices p
group by fiscalweek
order by 1;
One treats every week as one data point in the final average (what you seem to want). The other weights each week by the number of points during the week (the latter solution). These can produce very different results when weeks have different numbers of points.
I dont know If I fully understand the question:But Try Executing this: should help you:
create table #practice(PID int,cost decimal,Fweek int)
insert into #practice values (1,10,1)
insert into #practice values (1,33,2)
insert into #practice values (1,55,3)
insert into #practice values (1,18,4)
insert into #practice values (1,36,5)
insert into #practice values (1,24,6)
insert into #practice values (13,56,1)
insert into #practice values (13,10,2)
insert into #practice values (13,24,3)
insert into #practice values (13,30,4)
insert into #practice values (13,20,5)
insert into #practice values (13,18,6)
select * from #practice
select pid,Cost,
(select AVG(cost) from #practice p2 where p2.Fweek <= p1.Fweek and p1.pid = p2.pid) WeeklyAVG,
Fweek,AVG(COST) over (Partition by PID) as PIDAVG
from #practice p1;
I think this would work:
SELECT t1.pid,
t1.fiscalweek,
(
SELECT SUM(t.cost)/COUNT(t.cost)
FROM tablename AS t
WHERE t.pid = t1.pid
AND t.fiscalweek <= t1.fiscalweek
) AS average
FROM tablename AS t1
GROUP BY t1.pid, t1.fiscalweek
EDIT
To take into account for fiscal weeks without an entry you can simply exchange
SELECT SUM(t.cost)/COUNT(t.cost)
for
SELECT SUM(t.cost)/t1.fiscalweek
to calculate from week 1 or
SELECT SUM(t.cost)/(t1.fiscalweek - MIN(t.fiscalweek) + 1)
to calculate from the first week of this practice.
If all practice averages should start the same week (and not necessarily week no 1) then you'd have to find the minimum of all week numbers.
Also, this won't work if you're calculating across multiple years, but I assume that is not he case.