SQL to arrange calculate times as per given conditions - tsql

I've the following schema and the data:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[EntryExitLogs]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[EntryExitLogs](
[DeviceLogId] [int] NOT NULL,
[EmployeeCode] [nvarchar](50) NOT NULL,
[LogDate] [datetime] NOT NULL,
[Direction] [nvarchar](255) NOT NULL,
CONSTRAINT [PK_EntryExitLogs] PRIMARY KEY CLUSTERED
(
[EmployeeCode] ASC,
[LogDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
GO
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('435859','30032','2014-01-21 07:04:41','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('438019','30032','2014-01-21 08:59:09','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441564','30032','2014-01-21 16:57:35','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441263','30032','2014-01-21 19:09:19','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441264','30032','2014-01-21 19:10:20','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('439928','34035','2014-01-21 08:29:59','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('437962','34035','2014-01-21 08:30:12','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('437992','34035','2014-01-21 08:47:33','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('440203','34035','2014-01-21 13:38:56','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('442858','34035','2014-01-21 16:34:08','in');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('442860','34035','2014-01-21 16:35:11','out');
INSERT INTO [dbo].[EntryExitLogs]([DeviceLogId],[EmployeeCode],[LogDate],[Direction]) VALUES('441283','34035','2014-01-21 19:16:58','out');
I've written SQL to calculate In and out times like this:
;WITH cte AS (
SELECT ROW_NUMBER() OVER(
PARTITION BY lt.EmployeeCode ORDER BY lt.EmployeeCode,
lt.LogDate
) AS RowNo,
lt.DeviceLogId,
lt.EmployeeCode,
lt.LogDate,
lt.Direction
FROM EntryExitLogs lt
)
SELECT i.EmployeeCode,
i.LogDate AS InTime,
(
SELECT MIN(o.LogDate)
FROM cte AS o
WHERE o.EmployeeCode = i.EmployeeCode
AND o.RowNo = (i.RowNo + 1)
AND o.Direction = 'out'
) AS OutTime
FROM cte AS i
WHERE i.Direction = 'in'
ORDER BY i.EmployeeCode, i.LogDate
I'm getting the output (but not as desired by me), but I expect the output in the following manner (comments are given for more information against each row):
EmployeeCode InTime OutTime Comments
30032 21-Jan-2014 07:04:41 21-Jan-2014 08:59:09
30032 21-Jan-2014 16:57:35 21-Jan-2014 19:09:19
30032 21-Jan-2014 19:10:20 NULL If no OUT is specified for the last IN then it should be NULL
34035 21-Jan-2014 08:29:59 21-Jan-2014 13:38:56 Earliest IN and Latest OUT to be taken in case of multiple IN & OUT
34035 21-Jan-2014 16:34:08 21-Jan-2014 19:16:58 Earliest IN and Latest OUT to be taken in case of multiple IN & OUT
Please find the schema for this here
Kindly help me to achieve this.

This should work:
;WITH TrueOut AS --select only the latest "out" in between two "ins"
(
SELECT *
FROM EntryExitLogs a
WHERE direction='out'
AND
ISNULL((SELECT MIN(LogDate) FROM EntryExitLogs b
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='out'
AND b.LogDate>a.LogDate),'9999-12-31')
>=
ISNULL((SELECT MIN(LogDate) FROM EntryExitLogs b
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='in'
AND b.LogDate>a.LogDate),'9999-12-31')
),
TrueIn AS --select only the earlies "in" in between two "outs"
(
SELECT *
FROM EntryExitLogs a
WHERE direction='in'
AND
ISNULL((SELECT MAX(LogDate) FROM EntryExitLogs b
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='out'
AND b.LogDate<a.LogDate),'1900-01-01')
>=
ISNULL((SELECT MAX(LogDate) FROM EntryExitLogs b
WHERE a.EmployeeCode=b.Employeecode
AND b.direction='in'
AND b.LogDate<a.LogDate),'1900-01-01')
)
-- For every in select the next out
SELECT a.EmployeeCode, a.LogDate InTime,
(SELECT MIN(LogDate)
FROM TrueOut b
WHERE a.EmployeeCode=b.EmployeeCode
AND a.LogDate<b.LogDate) OutTIme
FROM TrueIn a

Related

Issue with PK violation on insert

I have a scenario where almost all of the tables have issues with the PK value as follows. This results is a database error or the violation of the PK insert.
When using the DBCC CheckIdent it displays an inconsistency between the next value and the current one.
Can anyone have a reason for the mismatch happening on several tables?
Since this database is then replicate, I'm afraid this error will propagate across the environment.
I adapted this script to fix it, but really trying to figure out the root of the problem.
/** Version 3.0 **/
if object_id('tempdb..#temp') is not null
drop table #temp
;
with cte as (
SELECT
distinct
A.TABLE_CATALOG AS CATALOG,
A.TABLE_SCHEMA AS "SCHEMA",
A.TABLE_NAME AS "TABLE",
B.COLUMN_NAME AS "COLUMN",
IDENT_SEED (A.TABLE_NAME) AS Seed,
IDENT_INCR (A.TABLE_NAME) AS Increment,
IDENT_CURRENT (A.TABLE_NAME) AS Curr_Value
, DBPS.row_count AS NumberOfRows
FROM INFORMATION_SCHEMA.TABLES A
inner join INFORMATION_SCHEMA.COLUMNS B on b.TABLE_NAME = a.TABLE_NAME and b.TABLE_SCHEMA = a.TABLE_SCHEMA
inner join sys.identity_columns IC on OBJECT_NAME (IC.object_id) = a.TABLE_NAME
inner join sys.dm_db_partition_stats DBPS ON DBPS.object_id =IC.object_id
inner join sys.indexes as IDX ON DBPS.index_id =IDX.index_id
WHERE A.TABLE_CATALOG = B.TABLE_CATALOG AND
A.TABLE_SCHEMA = B.TABLE_SCHEMA AND
A.TABLE_NAME = B.TABLE_NAME AND
COLUMNPROPERTY (OBJECT_ID (B.TABLE_NAME), B.COLUMN_NAME, 'IsIdentity') = 1 AND
OBJECTPROPERTY (OBJECT_ID (A.TABLE_NAME), 'TableHasIdentity') = 1 AND
A.TABLE_TYPE = 'BASE TABLE'
)
select 'DBCC CHECKIDENT ('''+A.[SCHEMA]+'.'+a.[TABLE]+''', reseed)' command
, ROW_NUMBER() OVER(ORDER BY a.[SCHEMA], a.[TABLE] asc) AS ID
, A.Curr_Value
, a.[TABLE]
into #temp
from cte A
ORDER BY A.[SCHEMA], A.[TABLE]
declare #i int = 1, #count int = (select max(ID) from #temp)
declare #text varchar(max) = ''
select #COUNT= count(1) FROM #temp
WHILE #I <= #COUNT
BEGIN
SET #text = (SELECT command from #temp where ID=#I)
EXEC (#text + ';')
print #text
select Curr_Value OldValue, ident_current([TABLE]) FixValue, [TABLE] from #temp where ID=#I
SET #I = #I + 1
SET #text='';
END
go
maybe someone or something with enough permissions made a mistake by reseeding?
As simple as this:
create table testid (
id int not null identity (1,1) primary key,
data varchar (3)
)
insert into testid (data) values ('abc'),('cde')
DBCC CHECKIDENT ('testid', RESEED, 1)
insert into testid (data) values ('bad')

Is there a better way to do update PostgreSQL

I'm trying to update the ended_at and active columns in the test_subscription table when the max period_end has not passed.
I'm using the below query but I doubt it's the most idiomatic way. Any suggestions on improvements are very much welcome.
Creating the tables:
CREATE TABLE test_subscription (
id INTEGER PRIMARY key,
started_at timestamp,
ended_at TIMESTAMP,
active boolean
);
CREATE TABLE test_invoice (
id INTEGER PRIMARY key,
subscription_id INTEGER,
period_start timestamp,
period_end timestamp
);
INSERT INTO test_subscription (id, started_at, ended_at, active)
values(1, '2017-01-01 00:00:00', NULL, TRUE);
INSERT INTO test_subscription (id, started_at, ended_at, active)
values(2, '2017-01-01 00:00:00', NULL, TRUE);
INSERT INTO test_invoice (id, subscription_id, period_start, period_end)
values(1, 1, '2017-01-01 00:00:00', '2017-12-01 00:00:00');
INSERT INTO test_invoice (id, subscription_id, period_start, period_end)
values(2, 1, '2017-12-02 00:00:00', '2019-12-01 00:00:00');
INSERT INTO test_invoice (id, subscription_id, period_start, period_end)
values(3, 2, '2017-01-01 00:00:00', '2017-12-01 00:00:00');
I'm updating using the below.
UPDATE test_subscription
SET ended_at = (CASE WHEN (SELECT
MAX(period_end)
FROM test_invoice
WHERE test_subscription.id = test_invoice.subscription_id
) < now()
THEN (SELECT MAX(period_end)
FROM test_invoice
WHERE test_subscription.id = test_invoice.subscription_id
)
ELSE NULL
end),
active = (CASE WHEN (SELECT MAX(period_end)
FROM test_invoice
WHERE test_subscription.id = test_invoice.subscription_id
) < now()
THEN TRUE
ELSE FALSE
end);
Updates like that are usually faster if you first collect all the aggregates, then run the update using that intermediate result. Co-related sub-queries tend to be much slower.
update test_subscription s
set ended_at = case when t.latest_end < current_timestamp then t.latest_end end,
active = t.latest_end < current_timestamp
from (
select s.id,
max(i.period_end) as latest_end
from test_subscription s
join test_invoice i on s.id = i.subscription_id
group by s.id
) t
where t.id = s.id;
Online example: http://rextester.com/NMMF41667
You can put the MAX within CASE
UPDATE test_subscription s
SET ( ended_at, active ) = (SELECT MAX(CASE
WHEN period_end < NOW() THEN
period_end
END),
MAX(CASE
WHEN period_end < NOW() THEN 'TRUE'
ELSE 'FALSE'
END) :: BOOLEAN
FROM test_invoice i
WHERE s.id = i.subscription_id);
Demo

sql compare columns to get result

I have the following issue. I have products with 3 different states. Parent, Child and products which are orphans. I am setting Parents as 1, Children as 2 and Orphans as 0. I am struggling to get the Orphan to set to 0. I realise that counting the amount of Parent PLU's is where I am going wrong but I do not know how to resolve this issue. Any help would be appreciated. (As you maybe able to tell, I am a noob and constructive criticism would be appreciated)
Kind Regards,
Jason.
Picture of results from query
declare #OrderID int = 1635
declare #Store char(3) = '001'
declare #SortedBy smallint = 2
DECLARE #tbl TABLE (DetailID int, OrderID int, PLU nvarchar(35), ParentPLU nvarchar(35))
INSERT INTO #tbl (DetailID, OrderID, PLU, ParentPLU)
SELECT DetailID, OrderDetails.OrderID, OrderDetails.PLU, OrderDetails.ParentPLU
FROM OrderDetails
INNER JOIN PLU
ON PLU.PLU = OrderDetails.PLU
WHERE OrderDetails.OrderID = #OrderID
AND OrderDetails.OrderStore = #Store
SELECT DetailID, OrderID, PLU, ParentPLU,
CASE WHEN ( SELECT COUNT(DISTINCT ParentPLU)
FROM #tbl
WHERE ParentPLU IN (SELECT PLU FROM #tbl)
) > 0 AND ParentPLU = '' THEN 1
WHEN ( SELECT COUNT(DISTINCT ParentPLU)
FROM #tbl
WHERE ParentPLU IN (SELECT PLU FROM #tbl)
) > 0 THEN 2
ELSE
0
END AS ParentChild,
ROW_NUMBER() OVER (ORDER BY
CASE WHEN #SortedBy = 1 THEN OrderID END ASC,
CASE WHEN #SortedBy = 2 THEN DetailID END ASC
) AS ID
FROM #tbl
You can use coalesce to get your desired result. First subquery checks for parent state, second for children. If both are null, then it is orphan
select
DetailID, OrderID, PLU, ParentPLU
, coalesce((
select
distinct 1
from
#tbl b
where
a.PLU = b.ParentPlu
)
, (
select
distinct 2
from
#tbl b
where
b.PLU = a.ParentPlu
), 0)
from
#tbl a

Can I insert a dynamic number of rows into a table using values from the table?

I want to insert a dynamic number of rows into a table, based on information in that table.
I can do it using the code below, but I'm wondering if there's a way to avoid the loop.
The commented out section was my best attempt at what I was trying to do, but it gave me an error of:
"The reference to column "iCount" is not allowed in an argument to a TOP, OFFSET, or FETCH clause. Only references to columns at an outer scope or standalone expressions and subqueries are allowed here."
DECLARE #TableX TABLE (
TDate DATE
, TType INT
, Fruit NVARCHAR(20)
, Vegetable NVARCHAR(20)
, Meat NVARCHAR(20)
, Bread NVARCHAR(20)
)
INSERT INTO #TableX VALUES
('2016-11-10',1,'Apple','Artichoke',NULL,NULL)
, ('2016-11-10',1,'Banana','Beet',NULL,NULL)
, ('2016-11-10',1,'Canteloupe','Cauliflower',NULL,NULL)
, ('2016-11-10',1,'Durian','Daikon',NULL,NULL)
, ('2016-11-10',2,NULL,NULL,'Rabbit','Rye')
, ('2016-11-10',2,NULL,NULL,'Sausage','Sourdough')
, ('2016-11-11',1,'Elderberry','Eggplant',NULL,NULL)
, ('2016-11-11',2,NULL,NULL,'Turkey','Tortilla')
, ('2016-11-11',2,NULL,NULL,'Venison','Vienna')
SELECT * FROM #TableX
DECLARE #BlankRow TABLE (
ID INT IDENTITY
, TDate DATE
, TType INT
, iCount INT
)
DECLARE #Counter1 INT = 0
, #RowCount INT
; WITH BR1
AS (
SELECT TDate, TType, COUNT(*) AS iCount
FROM #TableX
WHERE TType = 1
GROUP BY TDate, TType
)
, BR2
AS (
SELECT TDate, TType, COUNT(*) AS iCount
FROM #TableX
WHERE TType = 2
GROUP BY TDate, TType
)
INSERT INTO #BlankRow
SELECT ISNULL(BR1.TDate, BR2.TDate) AS TDate,
CASE WHEN ISNULL(BR1.iCount,0) < ISNULL(BR2.iCount,0) THEN 1 ELSE 2 END AS TType,
ABS(ISNULL(BR1.iCount,0) - ISNULL(BR2.iCount,0)) AS iCount
FROM BR1
FULL JOIN BR2
ON BR1.TDate = BR2.TDate
WHILE #Counter1 < (SELECT MAX(ID) FROM #BlankRow)
BEGIN
SET #Counter1 += 1
SET #RowCount = (SELECT iCount FROM #BlankRow WHERE ID = #Counter1)
INSERT INTO #TableX
SELECT TOP (#RowCount) tx.TDate, br.TType, NULL, NULL, NULL, NULL
FROM #TableX tx
LEFT JOIN #BlankRow br
ON tx.TDate = br.TDate
WHERE br.ID = #Counter1
END
/*INSERT INTO #TableX
SELECT TOP (tx.iCount) tx.TDate, br.TType, NULL, NULL, NULL, NULL
FROM #TableX tx
JOIN #BlankRow br
ON tx.TDate = br.TDate*/
SELECT *
FROM #TableX
ORDER BY TDate, TType,
ISNULL(Fruit,REPLICATE(CHAR(255),20)),
ISNULL(Vegetable,REPLICATE(CHAR(255),20)),
ISNULL(Meat,REPLICATE(CHAR(255),20)),
ISNULL(Bread,REPLICATE(CHAR(255),20))
The data is silly, I know, but my end goal is to have two different Tablix's in ReportBuilder that end up with the same number of rows so the headers of my groups show up at the same place on the page.
Something like this:
declare #TableX table(TDate date
,TType int
,Fruit nvarchar(20)
,Vegetable nvarchar(20)
,Meat nvarchar(20)
,Bread nvarchar(20)
);
insert into #TableX values
('2016-11-10',1,'Apple','Artichoke',NULL,NULL)
,('2016-11-10',1,'Banana','Beet',NULL,NULL)
,('2016-11-10',1,'Canteloupe','Cauliflower',NULL,NULL)
,('2016-11-10',1,'Durian','Daikon',NULL,NULL)
,('2016-11-10',2,NULL,NULL,'Rabbit','Rye')
,('2016-11-10',2,NULL,NULL,'Sausage','Sourdough')
,('2016-11-11',1,'Elderberry','Eggplant',NULL,NULL)
,('2016-11-11',2,NULL,NULL,'Turkey','Tortilla')
,('2016-11-11',2,NULL,NULL,'Venison','Vienna');
with DataRN as
(
select *
,row_number() over (partition by TDate, TType order by TDate) rn
from #TableX
)
,RowsRN as
(
select tt.TDate
,tt.TType
,td.rn
from (select distinct TDate, TType
from #TableX
) tt
full join (select distinct t1.TDate
,row_number() over (partition by t1.TDate, t1.TType order by t1.TDate) rn
from #TableX t1
) td
on(tt.TDate = td.TDate)
)
select r.TDate
,r.TType
,d.Fruit
,d.Vegetable
,d.Meat
,d.Bread
from DataRN d
full join RowsRN r
on(d.TDate = r.TDate
and d.TType = r.TType
and d.rn = r.rn
)
order by r.TDate
,r.TType
,isnull(d.Fruit,REPLICATE(CHAR(255),20))
,isnull(d.Vegetable,REPLICATE(CHAR(255),20))
,isnull(d.Meat,REPLICATE(CHAR(255),20))
,isnull(d.Bread,REPLICATE(CHAR(255),20))
In response to your comment, here is how you would use another cte to generate the full list of dates that you would need, if you havn't got a Dates reference table already (These are tremendously useful):
declare #MinDate date = (select min(TDate) from #TableX);
declare #MaxDate date = (select max(TDate) from #TableX);
with Dates as
(
select #MinDate as DateValue
union all
select dateadd(d,1,DateValue)
from Dates
where DateValue < #MaxDate
)
select DateValue
from Dates
option (maxrecursion 0);

Calculating Running Totals

I am trying to re-calculate a few different columns of data for a particular EmployeeID.
I want the hrs_YTD column to keep a running total. What is a good way of updating this info?
HRS_YTD currently has 0.00 values. I wan't to achieve the results in the table below.
ID | CHEKDATE | CHEKNUMBR | HRS | HRS_YTD
EN344944 | 01/1/2014 | dd1001 | 40.00 | 40.00
EN344944 | 01/8/2014 | dd1002 | 30.00 | 70.00
EN344944 | 1/15/2014 | dd1003 | 32.50 | 102.50
etc.....
DECLARE #k_external_id varchar(32)
SET #k_external_id = 'EN344944'
SELECT * INTO #tmpA
FROM dbo.gp_check_hdr a
WHERE a.EMPLOYID = #k_external_id
SELECT a.ID, a.CHEKNMBR, a.CHEKDATE,
(SELECT CAST(SUM(a.[hours]) as decimal(18,2)) FROM #tmpA b
WHERE (b.CHEKDATE <= a.CHEKDATE and YEAR(b.CHEKDATE) = 2013)) AS hrs_ytd
FROM #tmpA a
WHERE YEAR(a.CHEKDATE) = 2013
I really don't know if I can alias a table like I did with #tmpA b, but it's worked for me in the past. That doesn't mean its a good way of doing things though. Can someone show me a way to achieve the results I need?
havent tested this, but you can give this a try
DECLARE #k_external_id varchar(32)
SET #k_external_id = 'EN344944'
SELECT g1.primarykey, g1.ID,g1.CHEKDATE, g1.CHEKNUMBR, g1.HRS ,(SELECT SUM(g2.HRS)
FROM dbo.gp_check_hdr g2
WHERE g2.ID = #k_external_id AND
(g2.primarykey <= g1.primarykey)) as HRS_YTD
FROM dbo.gp_check_hdr g1
WHERE g1.ID = #k_external_id
ORDER BY g1.primarykey;
http://www.codeproject.com/Articles/300785/Calculating-simple-running-totals-in-SQL-Server
The way I'd do this is a combination of a computed column and a user defined function.
The function allows to aggregate the data. In a computed column, you can only work with fields of the same row, hence calling a function (which is allowed) is necessary.
The computed column allows this to work continuously without any additional queries or temp tables, etc. Once it's set, you don't need to run nightly updates or triggers or anything of the sort to keep the data updated, including when records change or get deleted.
Here's my solution ... and SqlFiddle: http://www.sqlfiddle.com/#!3/cd8d6/1/0
Edit:
I've updated this to reflect your need to calculate the running totals per employee. SqlFiddle also updated.
The function:
Create Function udf_GetRunningTotals (
#CheckDate DateTime,
#EmployeeID int
)
Returns Decimal(18,2)
As
Begin
Declare #Result Decimal(18,2)
Select #Result = Cast(Sum(rt.Hrs) As Decimal(18,2))
From RunningTotals rt
Where rt.CheckDate <= #CheckDate
And Year(rt.CheckDate) = Year(#CheckDate)
And rt.EmployeeID = #EmployeeID
Return #Result
End
The Table Schema:
Create Table [dbo].[RunningTotals](
[ID] [int] Identity(1,1) NOT NULL,
[EmployeeID] [int] NOT NULL,
[CheckDate] [datetime] NOT NULL,
[CheckNumber] [int] NOT NULL,
[Hrs] [decimal](18, 2) NOT NULL,
[Hrs_Ytd] AS ([dbo].[udf_GetRunningTotals]([CheckDate],[EmployeeID])), -- must add after table creation and function creation due to inter-referencing of table and function
Constraint [PK_RunningTotals3] Primary Key Clustered (
[ID] ASC
) With (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
)
) On [PRIMARY]
Result will tally up the YTD hrs for each year.
Note --
You cannot create the function or the table as is since they reference each other.
First, create the table with all but the computed column;
Then, create the function.
Finally, alter the table and add the computed column.
Here's a full running test script:
-- Table schema
Create Table [dbo].[RunningTotals](
[ID] [int] Identity(1,1) NOT NULL,
[EmployeeID] [int] NOT NULL,
[CheckDate] [datetime] NOT NULL,
[CheckNumber] [int] NOT NULL,
[Hrs] [decimal](18, 2) NOT NULL,
Constraint [PK_RunningTotals3] Primary Key Clustered (
[ID] ASC
) With (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
)
) On [PRIMARY]
Go
-- UDF Function to compute totals
Create Function udf_GetRunningTotals (
#CheckDate DateTime,
#EmployeeID int
)
Returns Decimal(18,2)
As
Begin
Declare #Result Decimal(18,2)
Select #Result = Cast(Sum(rt.Hrs) As Decimal(18,2))
From RunningTotals rt
Where rt.CheckDate <= #CheckDate
And Year(rt.CheckDate) = Year(#CheckDate)
And rt.EmployeeID = #EmployeeID
Return #Result
End
Go
-- Add the computed column to the table
Alter Table RunningTotals Add [Hrs_Ytd] As (dbo.udf_GetRunningTotals(CheckDate, EmployeeID))
Go
-- Insert some test data
Insert into RunningTotals Values (334944, '1/1/2014', '1001', 40.00)
Insert into RunningTotals Values (334944, '1/5/2014', '1002', 30.00)
Insert into RunningTotals Values (334944, '1/15/2014', '1003', 32.50)
Insert into RunningTotals Values (334945, '1/5/2014', '1001', 10.00)
Insert into RunningTotals Values (334945, '1/6/2014', '1002', 20.00)
Insert into RunningTotals Values (334945, '1/8/2014', '1003', 12.50)
-- Test the computed column
Select * From RunningTotals
Your sub query should work just fine.
I used a table variable in place of a temp table.
I also limited the results inserted in the temp table to 2013 to simplify the final select statement and limit the results in the temp table to just what you need. The only other thing is joining the sub query to the main query using the ID but what you have should work as you are limiting the result in your temp table to a specific ID.
DECLARE
#k_external_id varchar(32)
,#k_reporting_year int
SET #k_external_id = 'EN344944'
SET #k_reporting_year = 2013
DECLARE #temp TABLE(
ID NVARCHAR(32)
,CheckDate DATE
,CheckNumber NVARCHAR(6)
,HRS DECIMAL(18,2)
)
INSERT INTO #temp (
ID
,CheckDate
,CheckNumber
,HRS
)
SELECT
ID
,CHEKDATE
,CHEKNMBR
,[hours]
FROM
dbo.gp_check_hdr
WHERE
EMPLOYID = #k_external_id
AND YEAR(a.CHEKDATE) = #k_reporting_year
SELECT
ID
,CheckDate
,CheckNumber
,HRS
,(SELECT SUM(HRS) FROM #temp b WHERE a.ID = b.ID AND b.CheckDate <= a.CheckDate) AS hrs_ytd
FROM
#temp a