Which includes steps in the process and status of each step.
For processions completed the "Done" step is last and its duration is 0
A process that is without a "Done" stage-it still continues to run the query
I need a query to add another column in the table that calculates the minutes of each step in the process
I would appreciate your help
Which Syntax is effective
added a Syntax of table creation and data:
Create table T_Step (
employee_ID INT
, Process_ID int
, Step_ID int
, Start_Date Datetime
, Step_Status varchar(30)
);
Insert into T_Step values
('1','1','1','2018-01-01 8:00' ,'Pending')
, ('1','1','2','2018-01-01 9:30' ,'InService')
, ('1','1','3','2018-01-01 9:45' ,'Done')
, ('2','2','1','2018-01-02 11:32','Pending')
, ('2','2','2','2018-01-02 11:40','InService')
, ('2','2','3','2018-01-02 12:20','Done')
;
Thanks
Use LEFT JOIN and then count time difference between two-step for the particular process.
This query work in MYSQL
select t1.employee_ID,t1.Process_ID,t1.Step_ID,t1.Start_Date,t1.Step_Status,
IFNULL(TIMESTAMPDIFF(MINUTE,t1.Start_Date,t2.Start_Date),0) As TimeInMinute
from T_Step t1
LEFT JOIN T_Step t2
ON t1.Process_ID=t2.Process_ID AND t1.Step_ID!=t2.Step_ID AND (t2.Step_ID-t1.Step_ID)=1
ORDER BY t1.Process_ID,t1.Step_ID;
OUTPUT
| employee_ID | Process_ID | Step_ID | Start_Date | Step_Status | TimeInMinute |
| ----------- | ---------- | ------- | ------------------- | ----------- | ------------ |
| 1 | 1 | 1 | 2018-01-01 08:00:00 | Pending | 90 |
| 1 | 1 | 2 | 2018-01-01 09:30:00 | InService | 15 |
| 1 | 1 | 3 | 2018-01-01 09:45:00 | Done | 0 |
| 2 | 2 | 1 | 2018-01-02 11:32:00 | Pending | 8 |
| 2 | 2 | 2 | 2018-01-02 11:40:00 | InService | 40 |
| 2 | 2 | 3 | 2018-01-02 12:20:00 | Done | 0 |
DEMO
declare #T_Step table (
employee_ID INT
, Process_ID int
, Step_ID int
, Start_Date Datetime
, Step_Status varchar(30)
);
Insert into #T_Step values
('1','1','1','2018-01-01 8:00' ,'Pending')
, ('1','1','2','2018-01-01 9:30' ,'InService')
, ('1','1','3','2018-01-01 9:45' ,'Done')
, ('2','2','1','2018-01-02 11:32','Pending')
, ('2','2','2','2018-01-02 11:40','InService')
, ('2','2','3','2018-01-02 12:20','Done')
;
with cte as (Select *, R=ROW_NUMBER()
over(partition by employee_ID order by employee_ID)
from #T_Step)
Select T1.employee_ID,T1.Process_ID,T1.Step_Status,t1.Start_Date,t2.Start_Date, DATEDIFF(SECOND,t2.Start_Date,t1.Start_Date) TimeTaken
from cte T1
left join cte T2 on T1.R = T2.R+1 and T1.employee_ID = T2.employee_ID
order by T1.employee_ID
Related
From a stock transaction table I've created following sorted view table to be ready for FIFO:
rowN
date_
stockCode
sign_
amount
unitPrice
1
'2022-01-20'
ABC
in
5
29.20
2
'2022-01-22'
ABC
in
3
32.50
3
'2022-01-23'
ABC
out
7
40.00
4
'2022-01-23'
ABC
out
1
42.00
5
'2022-01-01'
XYZ
in
3
20.50
6
'2022-01-03'
XYZ
out
3
25.00
and I want to create a select query which looks like the previous table with only "out" rows and added cost_of_sales columns which is FIFO cost of that sale. But my knowledge of SQL is limited by just joins and sum over partitions.
The resulting table in my mind should look like this:
rowN
date_
stockCode
sign_
amount
unitPrice
cost_of_sales_uP
cost_of_sales
3
'2022-01-23'
ABC
out
7
40.00
30.1428
211.00
4
'2022-01-23'
ABC
out
1
42.00
32.50
32.50
6
'2022-01-03'
XYZ
out
3
25.00
20.50
61.50
I have no idea how to achieve this. Any help and guidance is appreciated. Result table doesn't have to be exactly like that but the main idea is there.
Thanks!
The following method might not be the fastest.
But it gets the job done.
First the incoming are unfolded into a temp table.
Then, by looping over the outgoing, each incoming unit is assigned to an outgoing on a First-In-First-Out basis.
The final query then uses the results of the temp table to calculate the total & average of the incoming.
IF OBJECT_ID('tempdb..#tmpStock') IS NOT NULL DROP TABLE #tmpStock;
CREATE TABLE #tmpStock (
id int identity primary key,
code varchar(30),
date_in date,
rowN int,
unitPrice decimal(10,2),
rowN_out int
);
--
-- Using a recursive CTE to unfold the incoming for the temp table
--
with RCTE as (
select stockCode, date_, rowN, amount, unitPrice
, 1 as lvl
from stock_transactions
where sign_ = 'in'
union all
select stockCode, date_, rowN, amount, unitPrice
, lvl + 1
from RCTE
where lvl < amount
)
insert into #tmpStock (code, date_in, rowN, unitPrice)
select stockCode, date_, rowN, unitPrice
from RCTE
order by stockCode, date_, rowN
option (maxrecursion 0);
DECLARE #IdOut INT = 1;
DECLARE #RowsOut INT = 0;
DECLARE #code VARCHAR(30);
DECLARE #amount SMALLINT;
DECLARE #date DATE;
DECLARE #rowN INT;
DECLARE #StockOut TABLE (
id int identity primary key,
code varchar(30),
date_out date,
rowN int,
amount smallint
);
insert into #StockOut (code, date_out, rowN, amount)
select stockCode, date_, rowN, amount
from stock_transactions
where sign_ = 'out'
order by stockCode, date_, rowN;
SELECT #RowsOut = COUNT(*) FROM #StockOut;
WHILE #IdOut <= #RowsOut
BEGIN
SELECT
#code = code
, #amount = amount
, #date = date_out
, #rowN = rowN
FROM #StockOut
WHERE id = #IdOut;
;WITH cte_in as (
select *
, rn = row_number() over (order by date_in, rowN)
from #tmpStock
where code = #code
and date_in <= #date
and rowN_out is null
)
UPDATE cte_in
SET rowN_out = #rowN
WHERE rn <= #amount;
SET #IdOut = #IdOut + 1;
END;
select * from #tmpStock
id | code | date_in | rowN | unitPrice | rowN_out
-: | :--- | :--------- | ---: | --------: | -------:
1 | ABC | 2022-01-20 | 1 | 29.20 | 3
2 | ABC | 2022-01-20 | 1 | 29.20 | 3
3 | ABC | 2022-01-20 | 1 | 29.20 | 3
4 | ABC | 2022-01-20 | 1 | 29.20 | 3
5 | ABC | 2022-01-20 | 1 | 29.20 | 3
6 | ABC | 2022-01-22 | 2 | 32.50 | 3
7 | ABC | 2022-01-22 | 2 | 32.50 | 3
8 | ABC | 2022-01-22 | 2 | 32.50 | 4
9 | XYZ | 2022-01-01 | 5 | 20.50 | 6
10 | XYZ | 2022-01-01 | 5 | 20.50 | 6
11 | XYZ | 2022-01-01 | 5 | 20.50 | 6
SELECT o.*
, CAST(i.AveragePriceIn AS DECIMAL(10,2)) AS cost_of_sales_uP
, i.TotalPriceIn AS cost_of_sales
FROM stock_transactions o
LEFT JOIN (
SELECT rowN_out
, AVG(unitPrice) as AveragePriceIn
, SUM(unitPrice) as TotalPriceIn
FROM #tmpStock
GROUP BY rowN_out
) i on i.rowN_out = o.rowN
WHERE o.sign_ = 'out'
ORDER BY o.rowN;
rowN | date_ | stockCode | sign_ | amount | unitPrice | cost_of_sales_uP | cost_of_sales
---: | :--------- | :-------- | :---- | -----: | --------: | ---------------: | ------------:
3 | 2022-01-23 | ABC | out | 7 | 40.00 | 30.14 | 211.00
4 | 2022-01-23 | ABC | out | 1 | 42.00 | 32.50 | 32.50
6 | 2022-01-03 | XYZ | out | 3 | 25.00 | 20.50 | 61.50
Demo on db<>fiddle here
I got a table data as follows:
ID | TYPE_ID | CREATED_DT | ROW_NUM
=====================================
123 | 485 | 2019-08-31 | 1
123 | 485 | 2019-05-31 | 2
123 | 485 | 2019-02-28 | 3
123 | 485 | 2018-11-30 | 4
123 | 485 | 2018-08-31 | 5
123 | 485 | 2018-05-31 | 6
123 | 487 | 2019-05-31 | 1
123 | 487 | 2018-05-31 | 2
I would like to select 6 ROW_NUMs for each TYPE_ID, if there is missing data I need to return NULL value for CREATED_DT and the final result set should look like:
ID | TYPE_ID | CREATED_DT | ROW_NUM
=====================================
123 | 485 | 2019-08-31 | 1
123 | 485 | 2019-05-31 | 2
123 | 485 | 2019-02-28 | 3
123 | 485 | 2018-11-30 | 4
123 | 485 | 2018-08-31 | 5
123 | 485 | 2018-05-31 | 6
123 | 487 | 2019-05-31 | 1
123 | 487 | 2018-05-31 | 2
123 | 487 | NULL | 3
123 | 487 | NULL | 4
123 | 487 | NULL | 5
123 | 487 | NULL | 6
Query:
SELECT
A.*
FROM TBL AS A
WHERE A.ROW_NUM <= 6
UNION ALL
SELECT
B.*
FROM TBL AS B
WHERE B.ROW_NUM NOT IN (SELECT ROW_NUM FROM TBL)
AND B.ROW_NUM <= 6
I tried using UNION ALL and ISNULL to backfill data that is not available but it is still giving me the existing data but not the expected result. I think this can be done in a easy way by using CTE but not sure how to get this working. Can any help me in this regard.
Assuming Row_Num has at least record has at least all 6 rows... 1,2,3,4,5,6 in tbl and no fractions or 0 or negative numbers...
we get a list of all the distinct type ID's and IDs. (Alias A)
Then we get a distinct list of row numbers less than 7 (giving us 6 records)
we cross join these to ensure each ID & Type_ID has all 6 rows.
we then left join back in the base set (tbl) to get all the needed dates; where such dates exist. As we're using left join the rows w/o a date will still persist.
.
SELECT A.ID, A.Type_ID, C.Created_DT, B.Row_Num
FROM (SELECT DISTINCT ID, Type_ID FROM tbl) A
CROSS JOIN (SELECT distinct row_num from tbl where Row_num < 7) B
LEFT JOIN tbl C
on C.ID = A.ID
and C.Type_ID = A.Type_ID
and C.Row_num = B.Row_num
Giving us:
+----+-----+---------+------------+---------+
| | ID | Type_ID | Created_DT | Row_Num |
+----+-----+---------+------------+---------+
| 1 | 123 | 485 | 2019-08-31 | 1 |
| 2 | 123 | 485 | 2019-05-31 | 2 |
| 3 | 123 | 485 | 2019-02-28 | 3 |
| 4 | 123 | 485 | 2018-11-30 | 4 |
| 5 | 123 | 485 | 2018-08-31 | 5 |
| 6 | 123 | 485 | 2018-05-31 | 6 |
| 7 | 123 | 487 | 2019-05-31 | 1 |
| 8 | 123 | 487 | 2018-05-31 | 2 |
| 9 | 123 | 487 | NULL | 3 |
| 10 | 123 | 487 | NULL | 4 |
| 11 | 123 | 487 | NULL | 5 |
| 12 | 123 | 487 | NULL | 6 |
+----+-----+---------+------------+---------+
Rex Tester: Example
This also assumes that you'd want 1-6 for each combination of type_id and ID. If ID's irrelevant, then simply exclude it from the join criteria. I included it as it's an ID and seems like it's part of a key.
Please reference the other answer for how you can do this using a CROSS JOIN - which is pretty neat. Alternatively, we can utilize the programming logic available in MS-SQL to achieve the desired results. The following approach stores distinct ID and TYPE_ID combinations inside a SQL cursor. Then it iterates through the cursor entries to ensure the appropriate amount of data is stored into a temp table. Finally, the SELECT is performed on the temp table and the cursor is closed. Here is a proof of concept that I validated on https://rextester.com/l/sql_server_online_compiler.
-- Create schema for testing
CREATE TABLE Test (
ID INT,
TYPE_ID INT,
CREATED_DT DATE
)
-- Populate data
INSERT INTO Test(ID, TYPE_ID, CREATED_DT)
VALUES
(123,485,'2019-08-31')
,(123,485,'2019-05-31')
,(123,485,'2019-02-28')
,(123,485,'2018-11-30')
,(123,485,'2018-08-31')
,(123,485,'2018-05-31')
,(123,487,'2019-05-31')
,(123,487,'2018-05-31');
-- Create TempTable for output
CREATE TABLE #OutputTable (
ID INT,
TYPE_ID INT,
CREATED_DT DATE,
ROW_NUM INT
)
-- Declare local variables
DECLARE #tempID INT, #tempType INT;
-- Create cursor to iterate ID and TYPE_ID
DECLARE mycursor CURSOR FOR (
SELECT DISTINCT ID, TYPE_ID FROM Test
);
OPEN mycursor
-- Populate cursor
FETCH NEXT FROM mycursor
INTO #tempID, #tempType;
-- Loop
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #count INT = (SELECT COUNT(*) FROM Test WHERE ID = #tempID AND TYPE_ID = #tempType);
INSERT INTO #OutputTable (ID, TYPE_ID, CREATED_DT, ROW_NUM)
SELECT ID, TYPE_ID, CREATED_DT, ROW_NUMBER() OVER(ORDER BY ID ASC)
FROM Test
WHERE ID = #tempID AND TYPE_ID = #tempType;
WHILE #count < 6
BEGIN
SET #count = #count + 1
INSERT INTO #OutputTable
VALUES (#tempID, #tempType, NULL, #count);
END
FETCH NEXT FROM mycursor
INTO #tempID, #tempType;
END
-- Close cursor
CLOSE mycursor;
-- View results
SELECT * FROM #OutputTable;
Note, if you have an instance where a unique combination of ID and TYPE_ID are grouped more than 6 times, the additional groupings will be included in your final result. If you must only show exactly 6 groupings, you can change that part of the query to SELECT TOP 6 ....
create a cte with a series and cross apply it
CREATE TABLE Test (
ID INT,
TYPE_ID INT,
CREATED_DT DATE
)
INSERT INTO Test(ID, TYPE_ID, CREATED_DT)
VALUES
(123,485,'2019-08-31')
,(123,485,'2019-05-31')
,(123,485,'2019-02-28')
,(123,485,'2018-11-30')
,(123,485,'2018-08-31')
,(123,485,'2018-05-31')
,(123,487,'2019-05-31')
,(123,487,'2018-05-31')
;
WITH n(n) AS
(
SELECT 1
UNION ALL
SELECT n+1 FROM n WHERE n < 6
)
,id_n as (
SELECT
DISTINCT
ID
,TYPE_ID
,n
FROM
Test
cross apply n
)
SELECT
id_n.ID
,id_n.TYPE_ID
,test.CREATED_DT
,id_n.n row_num
FROM
id_n
left join
(
select
ID
,TYPE_ID
,CREATED_DT
,ROW_NUMBER() over(partition by id, type_id order by created_dt) rn
from
Test
) Test on Test.ID = id_n.ID and Test.TYPE_ID = id_n.TYPE_ID and id_n.n = test.rn
drop table Test
I have a very simple requirement but I'm struggling to find a way around this.
I have a very simple query:
SELECT
ServiceCode,
StartDate,
Available,
Nights,
BookingID
FROM #tmpAvailability
LEFT JOIN vwRSBooking B
ON B.Depart = A.StartDate
AND B.ServiceCode = A.SupplierCode
AND B.StatusID IN (2640, 2621)
ORDER BY StartDate;
Made up of 2 tables
#tmpAvailability which consists of the following fields:
SupplierCode
StartDate
Available
vwRSBooking which consists of the following fields
BookingID
DepartDate
Code
Nights
StatusID
Departure and startdate can be joined to link the first day, and the servicecode and suppliercode can be joined to make sure that the availability is linked to the same supplier.
Which produces an output like this:
Code | Dates | Available | Nights | BookingID
TEST | 2018-01-04 | 1 | NULL | NULL
TEST | 2018-01-05 | 1 | NULL | NULL
TEST | 2018-01-06 | 0 | 4 | 123456
TEST | 2018-01-07 | 0 | NULL | NULL
TEST | 2018-01-08 | 0 | NULL | NULL
TEST | 2018-01-09 | 0 | NULL | NULL
TEST | 2018-01-10 | 1 | NULL | NULL
TEST | 2018-01-11 | 1 | NULL | NULL
TEST | 2018-01-12 | 1 | NULL | NULL
TEST | 2018-01-13 | 0 | NULL | 234567
TEST | 2018-01-14 | 0 | NULL | NULL
TEST | 2018-01-15 | 0 | NULL | NULL
What I need is when the BookingID in for 4 days that the bookingID and the nights are spread across those days, for example:
Code | Dates | Available | Nights | BookingID
TEST | 2018-01-04 | 1 | NULL | NULL
TEST | 2018-01-05 | 1 | NULL | NULL
TEST | 2018-01-06 | 0 | 4 | 123456
TEST | 2018-01-07 | 0 | 4 | 123456
TEST | 2018-01-08 | 0 | 4 | 123456
TEST | 2018-01-09 | 0 | 4 | 123456
TEST | 2018-01-10 | 1 | NULL | NULL
TEST | 2018-01-11 | 1 | NULL | NULL
TEST | 2018-01-12 | 1 | NULL | NULL
TEST | 2018-01-13 | 0 | 3 | 234567
TEST | 2018-01-14 | 0 | 3 | 234567
TEST | 2018-01-15 | 0 | 3 | 234567
TEST | 2018-01-16 | 1 | NULL | NULL
If anyone has any ideas on how to solve it would be most appreciated.
Andrew
You could replace your vwRSBooking with another view which uses a CTE to obtain all the dates the booking covers. Then use the view's coverdate for joining to the #tmpAvailability table:
CREATE VIEW vwRSBookingFull
AS
WITH cte ( bookingid, nights, depart, code, coverdate)
AS (SELECT bookingid,
nights,
depart,
code,
depart
FROM vwRSBooking
UNION ALL
SELECT c.bookingid,
c.nights,
c.depart,
c.code,
DATEADD(d, 1, c.coverdate)
FROM cte c
WHERE DATEDIFF(d, c.depart, c.coverdate) < (c.nights - 1))
SELECT c.bookingid,
c.nights,
c.depart,
c.code,
c.coverdate
FROM cte c
GO
You will need a calendar table with all the dates in the date range your dates may fall into. For this example, I build one for January 2018. We can then join onto this table to create the additional rows.
Here is the sample code I used. You can see it at SQL Fiddle.
CREATE TABLE code (
code varchar(max),
dates date,
available int,
nights int,
bookingid int
)
INSERT INTO code VALUES
('TEST','2018-01-04','1',NULL,NULL),
('TEST','2018-01-05','1',NULL,NULL),
('TEST','2018-01-06','0',4,123456),
('TEST','2018-01-07','0',NULL,NULL),
('TEST','2018-01-08','0',NULL,NULL),
('TEST','2018-01-09','0',NULL,NULL),
('TEST','2018-01-10','1',NULL,NULL),
('TEST','2018-01-11','1',NULL,NULL),
('TEST','2018-01-12','1',NULL,NULL),
('TEST','2018-01-13','0',3,234567),
('TEST','2018-01-14','0',NULL,NULL),
('TEST','2018-01-15','0',NULL,NULL)
CREATE TABLE dates (
dates date
)
INSERT INTO dates VALUES
('2018-01-01'),('2018-01-02'),('2018-01-03'),('2018-01-04'),('2018-01-05'),('2018-01-06'),('2018-01-07'),('2018-01-08'),('2018-01-09'),('2018-01-10'),('2018-01-11'),('2018-01-12'),('2018-01-13'),('2018-01-14'),('2018-01-15'),('2018-01-16'),('2018-01-17'),('2018-01-18'),('2018-01-19'),('2018-01-20'),('2018-01-21'),('2018-01-22'),('2018-01-23'),('2018-01-24'),('2018-01-25'),('2018-01-26'),('2018-01-27'),('2018-01-28'),('2018-01-29'),('2018-01-30'),('2018-01-31')
Here is the query based on this dataset:
SELECT
code.code,
dates.dates,
code.available,
code.nights,
code.bookingid
FROM code
LEFT JOIN dates ON
dates.dates >= code.dates
AND dates.dates < DATEADD(DAY,nights,code.dates)
Edit: Here is an example using your initial query as a subquery to join your result set onto the dates table if you want a copy & paste. Still requires creating the dates table.
SELECT
ServiceCode,
StartDate,
Available,
Nights,
BookingID
FROM (
SELECT
ServiceCode,
StartDate,
Available,
Nights,
BookingID
FROM #tmpAvailability
LEFT JOIN vwRSBooking B
ON B.Depart = A.StartDate
AND B.ServiceCode = A.SupplierCode
AND B.StatusID IN (2640, 2621)
) code
LEFT JOIN dates ON
dates.dates >= code.dates
AND dates.dates < DATEADD(DAY,nights,code.dates)
ORDER BY StartDate;
I have 1 table as attendance
|AttendanceresultId | AccountID | Intime | Outtime | ShiftID |
| 1 | 1234 | 2016-06-21 06:56:00 | NULL | 1 |
| 2 | 1234 | NULL | 2016-06-21 17:02:00 | 1 |
| 3 | 1234 | 2016-06-22 06:56:00 | NULL | 1 |
| 4 | 1234 | NULL | 2016-06-22 17:02:00 | 1 |
| 5 | 1235 | 2016-06-21 22:55:00 | NULL | 3 |
| 6 | 1235 | NULL | 2016-06-22 06:00:00 | 3 |
| 7 | 1235 | 2016-06-22 22:55:00 | NULL | 3 |
| 8 | 1235 | NULL | 2016-06-23 07:00:00 | 3 |
Another table is shift table
| ShiftId | Starttime | Endtime |
| 1 | 07:00:00.00 | 16:00:00.00 |
| 3 | 23:00:00.00 | 06:00:00.00 |
I want to calculate the total number of working hours and over time hours for an employee lets say,
Expected Output,
| AccountID | NormalHours | OvertimeHours |
| 1234 | 18:08 Hrs | 02:04 Hrs |
| 1235 | 14:10 Hrs | 01:00 Hrs |
I am newbie in sql server can any one suggest anything
I broke this into four Parts to adjust for the lack of the lag function in 2008:
1) Common Table Expression to organize the records (so as not to count on the identity field)
2) Calculates the total minutes worked and worked for overtime without using the lag function
3) I then summarize those minutes - and split them into hours and minutes.
4) I then format them.
That said, there are quite a few possible places where I think real world data might create issues.
Updated version that does not use the lag function
WITH TimeRecords (AccountID, InOutTime, Intime, OutTime, Shift,
RowNumber) AS
(SELECT att.AccountID, COALESCE(att.Intime,att.OutTime),
att.Intime, att.OutTime, att.Shift,
ROW_NUMBER() OVER(ORDER BY att.AccountID,
COALESCE(att.InTime, att.OutTime) )
FROM dbo.attendance att)
,
ShiftTime AS (
SELECT trecout.AccountID ,
trecin.InTime,
trecout.OutTime,
-- sh.EndTime ,
-- Number of Minutes for the entire shift
CASE WHEN trecout.OutTime IS NOT NULL
THEN DATEDIFF(mi,
trecin.InTime,
trecout.OutTime)
ELSE 0
END AS TotalTime ,
-- Number of Minutes for overtime
CASE WHEN trecout.OutTime IS NOT NULL
THEN DATEDIFF(mi, sh.EndTime,
CAST(trecout.OutTime AS TIME))
ELSE 0
END AS OverTime
FROM TimeRecords trecout
JOIN TimeRecords trecin
ON trecout.RowNumber -1 = trecin.RowNumber
JOIN dbo.ShiftID sh
ON trecout.[Shift] = sh.ShiftID
)
,
-- Summarize the data
ShiftTimeSum
AS ( SELECT st.AccountID ,
FLOOR(SUM(st.TotalTime - st.OverTime) / 60) AS RegularHours ,
SUM(st.TotalTime - st.OverTime) % 60 AS RegularMinutes ,
SUM(st.OverTime) / 60 AS OverTimeHours ,
SUM(st.OverTime) % 60 AS OverTimeMinutes
FROM ShiftTime st
GROUP BY st.AccountID
)
-- Now format
SELECT sts.AccountID ,
CAST(sts.RegularHours AS VARCHAR(5)) + ':'
+ RIGHT(CAST(( 100 + sts.RegularMinutes ) AS CHAR(3)), 2) AS RegularTime ,
CAST(sts.OverTimeHours AS VARCHAR(5)) + ':'
+ RIGHT(CAST(( 100 + sts.OverTimeMinutes ) AS CHAR(3)), 2) AS OverTime
FROM ShiftTimeSum sts;
I have a table (TABLE1) that lists all employees with their Dept IDs, the date they started and the date they were terminated (NULL means they are current employees).
I would like to have a resultset (TABLE2) , in which every row represents a day starting since the first employee started( in the sample table below, that date is 20090101 ), till today. (the DATE field). I would like to group the employees by DeptID and calculate the total number of employees for each row of TABLE2.
How do I this query? Thanks for your help, in advance.
TABLE1
DeptID EmployeeID StartDate EndDate
--------------------------------------------
001 123 20100101 20120101
001 124 20090101 NULL
001 234 20110101 20120101
TABLE2
DeptID Date EmployeeCount
-----------------------------------
001 20090101 1
001 20090102 1
... ... 1
001 20100101 2
001 20100102 2
... ... 2
001 20110101 3
001 20110102 3
... ... 3
001 20120101 1
001 20120102 1
001 20120103 1
... ... 1
This will work if you have a date look up table. You will need to specify the department ID. See it in action.
Query
SELECT d.dt, SUM(e.ecount) AS RunningTotal
FROM dates d
INNER JOIN
(SELECT b.dt,
CASE
WHEN c.ecount IS NULL THEN 0
ELSE c.ecount
END AS ecount
FROM dates b
LEFT JOIN
(SELECT a.DeptID, a.dt, SUM([count]) AS ecount
FROM
(SELECT DeptID, EmployeeID, 1 AS [count], StartDate AS dt FROM TABLE1
UNION ALL
SELECT DeptID, EmployeeID,
CASE
WHEN EndDate IS NOT NULL THEN -1
ELSE 0
END AS [count], EndDate AS dt FROM TABLE1) a
WHERE a.dt IS NOT NULL AND DeptID = 1
GROUP BY a.DeptID, a.dt) c ON c.dt = b.dt) e ON e.dt <= d.dt
GROUP BY d.dt
Result
| DT | RUNNINGTOTAL |
-----------------------------
| 2009-01-01 | 1 |
| 2009-02-01 | 1 |
| 2009-03-01 | 1 |
| 2009-04-01 | 1 |
| 2009-05-01 | 1 |
| 2009-06-01 | 1 |
| 2009-07-01 | 1 |
| 2009-08-01 | 1 |
| 2009-09-01 | 1 |
| 2009-10-01 | 1 |
| 2009-11-01 | 1 |
| 2009-12-01 | 1 |
| 2010-01-01 | 2 |
| 2010-02-01 | 2 |
| 2010-03-01 | 2 |
| 2010-04-01 | 2 |
| 2010-05-01 | 2 |
| 2010-06-01 | 2 |
| 2010-07-01 | 2 |
| 2010-08-01 | 2 |
| 2010-09-01 | 2 |
| 2010-10-01 | 2 |
| 2010-11-01 | 2 |
| 2010-12-01 | 2 |
| 2011-01-01 | 3 |
| 2011-02-01 | 3 |
| 2011-03-01 | 3 |
| 2011-04-01 | 3 |
| 2011-05-01 | 3 |
| 2011-06-01 | 3 |
| 2011-07-01 | 3 |
| 2011-08-01 | 3 |
| 2011-09-01 | 3 |
| 2011-10-01 | 3 |
| 2011-11-01 | 3 |
| 2011-12-01 | 3 |
| 2012-01-01 | 1 |
Schema
CREATE TABLE TABLE1 (
DeptID tinyint,
EmployeeID tinyint,
StartDate date,
EndDate date)
INSERT INTO TABLE1 VALUES
(1, 123, '2010-01-01', '2012-01-01'),
(1, 124, '2009-01-01', NULL),
(1, 234, '2011-01-01', '2012-01-01')
CREATE TABLE dates (
dt date)
INSERT INTO dates VALUES
('2009-01-01'), ('2009-02-01'), ('2009-03-01'), ('2009-04-01'), ('2009-05-01'),
('2009-06-01'), ('2009-07-01'), ('2009-08-01'), ('2009-09-01'), ('2009-10-01'),
('2009-11-01'), ('2009-12-01'), ('2010-01-01'), ('2010-02-01'), ('2010-03-01'),
('2010-04-01'), ('2010-05-01'), ('2010-06-01'), ('2010-07-01'), ('2010-08-01'),
('2010-09-01'), ('2010-10-01'), ('2010-11-01'), ('2010-12-01'), ('2011-01-01'),
('2011-02-01'), ('2011-03-01'), ('2011-04-01'), ('2011-05-01'), ('2011-06-01'),
('2011-07-01'), ('2011-08-01'), ('2011-09-01'), ('2011-10-01'), ('2011-11-01'),
('2011-12-01'), ('2012-01-01')
you need somthing along these lines.
SELECT *
, ( SELECT COUNT(EmployeeID) AS EmployeeCount
FROM TABLE1 AS f
WHERE t.[Date] BETWEEN f.BeginDate AND f.EndDate
)
FROM ( SELECT DeptID
, BeginDate AS [Date]
FROM TABLE1
UNION
SELECT DeptID
, EndDate AS [Date]
FROM TABLE1
) AS t
EDIT since OP clarified that he wants all the dates here is the updated solution
I have excluded a Emplyee from Count if his job is ending on that date.But if you want to include change t.[Date] < f.EndDate to t.[Date] <= f.EndDate in the below solution. Plus I assume the NULL value in EndDate mean Employee still works for Department.
DECLARE #StartDate DATE = (SELECT MIN(StartDate) FROM Table1)
,#EndDate DATE = (SELECT MAX(EndDate) FROM Table1)
;WITH CTE AS
(
SELECT DISTINCT DeptID,#StartDate AS [Date] FROM Table1
UNION ALL
SELECT c.DeptID, DATEADD(dd,1,c.[Date]) AS [Date] FROM CTE AS c
WHERE c.[Date]<=#EndDate
)
SELECT * ,
EmployeeCount=( SELECT COUNT(EmployeeID)
FROM TABLE1 AS f
WHERE f.DeptID=t.DeptID AND t.[Date] >= f.StartDate
AND ( t.[Date] < f.EndDate OR f.EndDate IS NULL )
)
FROM CTE AS t
ORDER BY 1
OPTION ( MAXRECURSION 0 )
here is SQL Fiddler demo.I have added another department and added an Employee to it.
http://sqlfiddle.com/#!3/5c4ec/1