I need help creating a SQL update for the following example (with sample data):
Table 1: DETAILTRAN
Structure: VENDOR CHAR(10), EMPLOYEE CHAR(10), WEEK INT
VENDOR EMPLOYEE WEEK
VEN01 EMP01 1
VEN01 EMP01 1
VEN02 EMP03 1
VEN03 EMP02 1
VEN01 EMP01 2
VEN01 EMP01 2
VEN01 EMP03 2
VEN03 EMP02 2
VEN02 EMP01 3
VEN02 EMP01 3
VEN02 EMP03 3
VEN03 EMP03 3
Table 2: SUMMARTRAN (that needs to be updated)
Structure: WEEK01 INT, WEEK02 INT, WEEK03 INT, VENDOR CHAR(10), EMPLOYEE CHAR(10)
The results of SQL Update this table (SUMMARTRAN) should look like this:
WEEK01 WEEK02 WEEK03 VENDOR EMPLOYEE
2 3 0 VEN01
1 0 3 VEN02
1 1 1 VEN03
2 2 2 EMP01
1 1 0 EMP02
1 1 2 EMP03
You can approach this using grouping sets and conditional aggregation:
select vendor, employee,
sum(case when week = 1 then 1 else 0 end) as week01,
sum(case when week = 2 then 1 else 0 end) as week02,
sum(case when week = 3 then 1 else 0 end) as week03
from DETAILTRAN
group by grouping sets ((vendor), (employee));
You can incorporate this into an insert, statement for summartran:
insert into summertran(vendor, employee, week01, week02, week03)
select vendor, employee,
sum(case when week = 1 then 1 else 0 end) as week01,
sum(case when week = 2 then 1 else 0 end) as week02,
sum(case when week = 3 then 1 else 0 end) as week03
from DETAILTRAN
group by grouping sets ((vendor), (employee));
Try this
with VENDOR as
(
select isnull(count(case when WEEK = 1 then WEEK end),0) WEEK01,
isnull(count(case when WEEK = 2 then WEEK end),0) WEEK02 ,
isnull(count(case when WEEK = 3 then WEEK end),0) WEEK03 ,
VENDOR
from DETAILTRAN
group by VENDOR
),EMPLOYEE as
(
select isnull(count(case when WEEK = 1 then WEEK end),0) WEEK01,
isnull(count(case when WEEK = 2 then WEEK end),0) WEEK02 ,
isnull(count(case when WEEK = 3 then WEEK end),0) WEEK03 ,
EMPLOYEE
from DETAILTRAN
group by EMPLOYEE
)
select WEEK01, WEEK02, WEEK03, VENDOR, EMPLOYEE = '' from VENDOR
union all
select WEEK01, WEEK02, WEEK03, VENDOR='', EMPLOYEE from EMPLOYEE
SQL FIDDLE DEMO
To update the result to SUMMARTRAN table use this
with VENDOR as
(
select isnull(count(case when WEEK = 1 then WEEK end),0) WEEK01,
isnull(count(case when WEEK = 2 then WEEK end),0) WEEK02 ,
isnull(count(case when WEEK = 3 then WEEK end),0) WEEK03 ,
VENDOR
from DETAILTRAN
group by VENDOR
),EMPLOYEE as
(
select isnull(count(case when WEEK = 1 then WEEK end),0) WEEK01,
isnull(count(case when WEEK = 2 then WEEK end),0) WEEK02 ,
isnull(count(case when WEEK = 3 then WEEK end),0) WEEK03 ,
EMPLOYEE
from DETAILTRAN
group by EMPLOYEE
)
insert into SUMMARTRAN (WEEK01, WEEK02, WEEK03, VENDOR, EMPLOYEE)
select WEEK01, WEEK02, WEEK03, VENDOR, EMPLOYEE = '' from VENDOR
union all
select WEEK01, WEEK02, WEEK03, VENDOR='', EMPLOYEE from EMPLOYEE
Related
We have a table that contains a snapshot of every employees data at the end of each month until the month they leave the company. This table also has the snapshot of each employee for the current day which is replaced each day until the end of the month.
What we're trying to do is select weekly statistics for Hires, Rehires, and Terms for each department. However since we only capture data by month and not by week, I'm having trouble breaking this down by week without getting duplicates.
I'm able to pull monthly statistics similar to this. Is there a method to group by each week in a month if there is only an entry for a month?
select
Max(AsOfDate) as AsOfDate,
Sector,
Department,
sum(case
when DatePart(Year, TermDate) = DatePart(Year, AsOfDate) and DatePart(Month, TermDate) = DatePart(Month, AsOfDate) then 1
else 0
end) as Terms,
sum(case
when DatePart(Year, HireDate) = DatePart(Year, AsOfDate) and DatePart(Month, HireDate) = DatePart(Month, AsOfDate) then 1
else 0
end) as Hires,
sum(case
when DatePart(Year, RehireDate) = DatePart(Year, AsOfDate) and DatePart(Month, RehireDate) = DatePart(Month, AsOfDate) then 1
else 0
end) as Rehires
from Employee_History
group by Year(AsOfDate), datepart(Month, AsOfDate), Department
Example data if today was 2022-03-17
AsOfDate
EmployeeID
Department
Title
HireDate
RehireDate
TermDate
2022-01-31
EMP22
HR
Admin
2021-01-12
null
2022-01-17
2022-01-31
EMP45
IT
Programmer
2022-01-10
null
null
2022-02-28
EMP45
IT
Programmer
2022-01-10
null
null
2022-03-17
EMP45
IT
Programmer
2022-01-10
null
null
2022-01-31
EMP03
IT
Manager
2018-08-17
2022-01-24
null
2022-02-28
EMP03
IT
Manager
2018-08-17
2022-01-24
null
2022-03-17
EMP03
IT
Manager
2018-08-17
2022-01-24
null
Desired output for January 2022 for example
AsOfDate
Department
Hires
Rehires
Terms
2022-01-01
HR
0
0
0
2022-01-08
HR
0
0
0
2022-01-15
HR
0
0
0
2022-01-22
HR
0
0
1
2022-01-29
HR
0
0
0
2022-01-01
IT
0
0
0
2022-01-08
IT
0
0
0
2022-01-15
IT
1
0
0
2022-01-22
IT
0
0
0
2022-01-29
IT
0
1
0
What you need is a mapping table for week <-> end of the Month thing containing:
create table weekmap(asOfDate DATE PRIMARY KEY, weekDayStart DATE, weekDayEnd DATE)
One problem is that your snapshot table contains "current date" if month isn't finished. I would advice to change that so it always has end of month to simplify stuff. Alternatively, create new column for that.
Populate it with whatever logic your weeks should be, some use ISO WEEK, some use day from start of new year etc.
Then you join your snapshot against this table (and you need to handle case where asOfDate isn't end of the month):
select w.asOfDate, w.weekDayStart, t.Department
, SUM(case when HireDate between weekdaystart and weekdayend then 1 else 0 end) AS hires
, SUM(case when ReHireDate between weekdaystart and weekdayend then 1 else 0 end) AS rehires
, SUM(case when TermDate between weekdaystart and weekdayend then 1 else 0 end) AS term
from snapshottable t
inner join weekmap w
ON w.asOfDate = t.asOfDateFixedEndOfMonth
group by w.asOfDate, w.weekDayStart, t.Department
There will be some loss of data if a guy is hired and fired twice in one month, but then you probably have a bigger problem
This is table structure;
ID Score Valid CreatedDate
1 A 1 2018-02-19 23:33:10.297
2 C 0 2018-02-19 23:32:40.700
3 B 1 2018-02-19 23:32:30.247
4 A 1 2018-02-19 23:31:37.153
5 B 0 2018-02-19 23:25:08.667
...
I need to find total number of each score and valid in each month
I mean final result should be like
Month A B C D E Valid(1) NotValid(0)
January 123 343 1021 98 12 1287 480
February 516 421 321 441 421 987 672
...
This is what I tried;
SELECT DATEPART(year, CreatedDate) as Ay,
(select count(*) from TableResults where Score='A') as 'A',
(select count(*) from TableResults where Score='B') as 'B',
...
FROM TableResults
group by DATEPART(MONTH, CreatedDate)
but couldn't figure how to calculate all occurrence of scores on each month.
Use conditional aggregation.
SELECT DATEPART(year, CreatedDate) as YR
, DATEPART(month, CreatedDate) MO
, sum(Case when score = 'A' then 1 else 0 end) as A
, sum(Case when score = 'B' then 1 else 0 end) as B
, sum(Case when score = 'C' then 1 else 0 end) as C
, sum(Case when score = 'D' then 1 else 0 end) as D
, sum(Case when score = 'E' then 1 else 0 end) as E
, sum(case when valid = 1 then 1 else 0 end) as Valid
, sum(case when valid = 0 then 1 else 0 end) as NotValid
FROM TableResults
GROUP BY DATEPART(MONTH, CreatedDate), DATEPART(year, CreatedDate)
I'm not a big fan of queries in the select; I find they tend to cause performance problems in the long run. Since we're aggregating here I just applied the conditional logic to all the columns.
I have a table which contains data of std attendance of one year
AttID Present absent. leave sick month StdRegNo
1. 23 1 0 0 JAN. 1
2. 25 0 0 0 JAN. 2
3. 23 0 0 0 MAR. 1
4. 21 3 0 1 MAR. 2
SO ON.......
I want result in such a view as bellow:
StdReq month P A L S month P A L S
1. Jan. 23 1 0 0 Mar 23 0 0 0
2. Jan. 25 0 0 0 Mar 21 3 0 1
I need this view for 12 months how can I do this? please help me
You can use a query like this:
select StdRegNo
-- January info
,max(case when [month] = 'JAN' then Present end) JAN_P
,max(case when [month] = 'JAN' then [absent] end) JAN_A
,max(case when [month] = 'JAN' then leave end) JAN_L
,max(case when [month] = 'JAN' then sick end) JAN_S
-- March info
,max(case when [month] = 'MAR' then Present end) MAR_P
,max(case when [month] = 'MAR' then [absent] end) MAR_A
,max(case when [month] = 'MAR' then leave end) MAR_L
,max(case when [month] = 'MAR' then sick end) MAR_S
-- And so on ...
from yourTable
group by StdRegNo;
Background Info
I have a large table 400M+ rows that changes daily (one days data drops out an a new days data drops in) The table is partitioned on a 'day' field so there are 31 paritions.
Each row in the table has data similar to this:
ID, Postcode, DeliveryPoint, Quantity, Day, Month
1 SN1 1BG A1 6 29 1
2 SN1 1BG A1 1 28 1
3 SN1 1BG A2 2 27 1
4 SN1 1BG A1 3 28 1
5 SN2 1AQ B1 1 29 12
6 SN1 1BG A1 2 26 12
I need to pull out 7 days of data in the format:
Postcode, Deliverypoint, 7dayAverage, Day1,day2,Day3,Day4,Day5,Day6,Day7
SN1 1BG A1 2 0 1 2 1 3 4 0
I can easily extract the data for the 7 day period but need to create a columnar version as shown above.
I have something like this:
select postcode,deliverypoint,
sum (case day when 23 then quantity else 0 end) as day1,
sum (case day when 24 then quantity else 0 end) as day2,
sum(case day when 25 then quantity else 0 end) as day3,
sum(case day when 26 then quantity else 0 end) as day4,
sum(case day when 27 then quantity else 0 end) as day5,
sum(case day when 28 then quantity else 0 end) as day6,
sum(case day when 29 then quantity else 0 end) as day7,
sum(quantity)*1.0/#daysinweek as wkavg
into #allweekdp
from maintable dp with (nolock)
where day in (select day from #days)
group by postcode,deliverypoint
where #days has the day numbers in the 7 day period.
But as you can see, I've hard-coded the day numbers into the query, I want to get them out of my temporary table #days but can't see a way of doing it (an array would be perfect here)
Or a I going about this in completely the wrong way ?
Kind Regards
Steve
If I understand correctly, what I would do is:
Convert the day and month columns into datetime values,
Get the first day of the week and day of the weekday (1-7) for each date, and
Pivot the data and group by the first day of the week
see here: sqlfiddle
As utexaspunk suggested, Pivot might be the way to go. I've never been comfortable with pivot and have preferred to pivot it manually so I control how everything looks, so I'm using a similar solution to how you did your script to solve the issue. No idea how the performance between my way and utexaspunk's will compare.
Declare #Min_Day Integer = Select MIN(day) as Min_Day From #days;
With Day_Coding_CTE as (
Select Distinct day
, day - #Min_Day + 1 as Day_Label
From #days
)
, Non_Columnar_CTE as (
Select dp.postcode
, dp.deliverypoint
, d.day
, c.Day_Label
, SUM(quantity) as Quantity
From maintable dp with (nolock)
Left Outer Join #days d
on dp.day = d.day --It also seems like you'll need more criteria here, but you'll have to figure out what those should be
Left Outer Join Day_Coding_CTE c
on d.day = c.day
)
Select postcode
, deliverypoint
, SUM(Case
When Day_Label = 1
Then Quantity
Else 0
End) as Day1
, SUM(Case
When Day_Label = 2
Then Quantity
Else 0
End) as Day2
, SUM(Case
When Day_Label = 3
Then Quantity
Else 0
End) as Day3
, SUM(Case
When Day_Label = 4
Then Quantity
Else 0
End) as Day4
, SUM(Case
When Day_Label = 5
Then Quantity
Else 0
End) as Day5
, SUM(Case
When Day_Label = 6
Then Quantity
Else 0
End) as Day6
, SUM(Case
When Day_Label = 7
Then Quantity
Else 0
End) as Day7
, SUM(Quantity)/#daysinweek as wkavg
From Non_Columnar_CTE
Group by postcode
deliverypoint
I have a table which looks like this :
Item Month Year Sales
Name1 1 2013 333
Name2 2 2013 454
Name3 1 2013 434
I need to write a stored procedure which looks like this :
Item Sales_On_Month(1) Sales_On_Month(2) Sales_On_Month(2)-Sales_On_Month(1) Sales_On_Month(3) Sales_On_Month(3)-Sales_On_Month(2)
Name1 333 334 1 335 1
Name2 454 454 0 654 200
I tried the following query :
I see a lot of nulls in the middle.If you could let me know the modifications to the query or another approach
it would be great :
select (case when [MONTH] = 1 then Sales END) AS Sales_On_Month(1),
(case when [MONTH] = 2 then Sales END) AS Sales_On_Month(2),
(case when [MONTH] = 2 then Sales END) - (case when [MONTH] = 1 then Sales END) AS Sales_On_Month(2)-Sales_On_Month(1) ...............
from ABC;
Use an aggregate, SUM(), MAX(), whatever, andGROUP BY`:
SELECT Item
,SUM(CASE WHEN [MONTH] = 1 THEN Sales END) AS Sales_1
,SUM(CASE WHEN [MONTH] = 2 THEN Sales END) AS Sales_2
,SUM(CASE WHEN [MONTH] = 2 THEN Sales END) - SUM(CASE WHEN [MONTH] = 1 THEN Sales END) AS Sales_On_Month(2)-Sales_On_Month(1)
FROM ABC
GROUP BY Item