I have a table that shows the interest rate on an account on a given day. I'm looking to query the interest rate on one day and the interest rate for the next day. Specifically, I'm looking to find accounts where the interest rate changed from one day to the next.
My data looks like this
Account Number / Loan Number / Date / Interest Rate
1234 / 5656 / 1/1/18 / 12%
1234 / 5656 / 1/2/18 / 12%
1234 / 5656 / 1/3/18 / 0%
1234 / 5656 / 1/4/18 / 0%
I want the query to return just the two days where the interest rate changed from 12% to 0%.
With the above data, it would return the following:
Account Number / Loan Number / Date / Interest Rate / Next Day / Next Day Rate
1234 / 5656 / 1/2/18 / 12% / 1/3/18 / 0%
The code I'm using (see below) is returning the same Date multiple times and modifying the "Next Day Rate" by some factor I cannot identify.
This is what I have so far.
select
tc.loanaccountid AS 'Account'
, l.LoanNumber AS 'Loan Number'
, tc.trialbalancedate AS 'Date'
, tc.interestrate AS 'Interest Rate'
, tb.trialbalancedate AS 'Next Day'
, tb.interestrate AS 'Next Day Rate'
from dbo.dailytrialbalance tc
join dbo.loanaccount l on tc.loanaccountid = l.loanaccountid
left join dbo.dailytrialbalance tb on dateadd(day, 1, tc.trialbalancedate) =
tb.trialbalancedate
where tc.PortfolioCodeId = '10'
and tc.interestrate = '0'
and tb.interestrate > '0'
I'm still learning SQL, so any help is appreciated. Thanks.
Here is a way with LEAD for SQL Server 2012 onward. I added the (SELECT NULL) as a place holder for something more realistic to determine the difference since you have duplicate data for 1/2/18:
with cte as(
select
tc.loanaccountid AS 'Account'
, l.LoanNumber AS 'Loan Number'
, tc.trialbalancedate AS 'Date'
, tc.interestrate AS 'Interest Rate'
, NextDay = lead(tc.trialbalancedate) over (partition by tc.loanaccountid order by tc.trialbalancedate, (select null))
, NextDayRate = lead(tc.interestrate) over (partition by tc.loanaccountid order by tc.trialbalancedate, (select null))
from dbo.dailytrialbalance tc
join dbo.loanaccount l on tc.loanaccountid = l.loanaccountid)
select *
from cte
where NextDayRate = 0 and [Interest Rate] != 0
Here is it in action, with your test data:
declare #testData table (AccountNumber int, LoanNumber int, Date date, InterestRate varchar(3))
insert into #testData
values
(1234,5656,'20180101','12%'),
(1234,5656,'20180102','12%'),
(1234,5656,'20180103','0%'),
(1234,5656,'20180104','0%')
;with cte as(
select
AccountNumber
,LoanNumber
,[Date]
,InterestRate
,NextDay = lead([Date]) over (partition by AccountNumber order by [Date])
,NextDayRate = lead(InterestRate) over (partition by AccountNumber order by [Date])
from #testData)
select *
from cte
where NextDayRate = '0%' and InterestRate != '0%'
Related
I have data with an amount of 1 month and want to change it to 30 days.
if 1 month the amount is 20000 then per day is 666.67
The following are sample data and results:
Account
Project
Date
Segment
Amount
Acc1
1
September 2022
Actual
20000
Result :
I need a query using sql server
You may try a set-based approach using an appropriate number table and a calculation with windowed COUNT().
Data:
SELECT *
INTO Data
FROM (VALUES
('Acc1', 1, CONVERT(date, '20220901'), 'Actual', 20000.00)
) v (Account, Project, [Date], Segment, Amount)
Statement for all versions, starting from SQL Server 2016 (the number table is generated using JSON-based approach with OPENJSON()):
SELECT d.Account, d.Project, a.[Date], d.Segment, a.Amount
FROM Data d
CROSS APPLY (
SELECT
d.Amount / COUNT(*) OVER (ORDER BY (SELECT NULL)),
DATEADD(day, CONVERT(int, [key]), d.[Date])
FROM OPENJSON('[1' + REPLICATE(',1', DATEDIFF(day, d.[Date], EOMONTH(d.[Date]))) + ']')
) a (Amount, Date)
Statement for SQL Server 2022 (the number table is generated with GENERATE_SERIES()):
SELECT d.Account, d.Project, a.[Date], d.Segment, a.Amount
FROM Data d
CROSS APPLY (
SELECT
d.Amount / COUNT(*) OVER (ORDER BY (SELECT NULL)),
DATEADD(day, [value], d.[Date])
FROM GENERATE_SERIES(0, DATEDIFF(day, d.[Date], EOMONTH(d.[Date])))
) a (Amount, Date)
Notes:
Both approaches calculate the days for each month. If you always want 30 days per month, replace DATEDIFF(day, d.[Date], EOMONTH(d.[Date])) with 29.
There is a rounding issue with this calculation. You may need to implement an additional calculation for the last day of the month.
You can use a recursive CTE to generate each day of the month and then divide the amount by the number of days in the month to achive the required output
DECLARE #Amount NUMERIC(18,2) = 20000,
#MonthStart DATE = '2022-09-01'
;WITH CTE
AS
(
SELECT
CurrentDate = #MonthStart,
DayAmount = CAST(#Amount/DAY(EOMONTH(#MonthStart)) AS NUMERIC(18,2)),
RemainingAmount = CAST(#Amount - (#Amount/DAY(EOMONTH(#MonthStart))) AS NUMERIC(18,2))
UNION ALL
SELECT
CurrentDate = DATEADD(DAY,1,CurrentDate),
DayAmount = CASE WHEN DATEADD(DAY,1,CurrentDate) = EOMONTH(#MonthStart)
THEN RemainingAmount
ELSE DayAmount END,
RemainingAmount = CASE WHEN DATEADD(DAY,1,CurrentDate) = EOMONTH(#MonthStart)
THEN 0
ELSE CAST(RemainingAmount-DayAmount AS NUMERIC(18,2)) END
FROM CTE
WHERE CurrentDate < EOMONTH(#MonthStart)
)
SELECT
CurrentDate,
DayAmount
FROM CTE
In case you want an equal split without rounding errors and without loops you can use this calculation. It spreads the rounding error across all entries, so they are all as equal as possible.
DECLARE #Amount NUMERIC(18,2) = 20000,
#MonthStart DATE = '20220901'
SELECT DATEADD(DAY,Numbers.i - 1,#MonthStart)
, ShareSplit.Calculated_Share
, SUM(ShareSplit.Calculated_Share) OVER (ORDER BY (SELECT NULL)) AS Calculated_Total
FROM (SELECT DISTINCT number FROM master..spt_values WHERE number BETWEEN 1 AND DAY(EOMONTH(#MonthStart)))Numbers(i)
CROSS APPLY(SELECT CAST(ROUND(#Amount * 100 / DAY(EOMONTH(#MonthStart)),0) * 0.01
+ CASE
WHEN Numbers.i
<= ABS((#Amount - (ROUND(#Amount * 100 / DAY(EOMONTH(#MonthStart)),0) / 100.0 * DAY(EOMONTH(#MonthStart)))) * 100)
THEN 0.01 * SIGN(#Amount - (ROUND(#Amount * 100 / DAY(EOMONTH(#MonthStart)),0) / 100.0 * DAY(EOMONTH(#MonthStart))))
ELSE 0
END AS DEC(18,2)) AS Calculated_Share
)ShareSplit
I am doing a case for a subscription-based company that wants an overview of their monthly sales. This means that I need to sum up the revenue from all active subscriptions, grouped by month. I have managed to work my way through the data to create a table that looks like the one below. Each row is a contract with a start date, end date, proposition (either standard or discount), and the corresponding price per month.
ContractStartDate
ContractEndDate
PropositionReference
PropositionPrice
2018-01-03
NULL
Standard
4.5
2019-01-17
NULL
Discount
2
2018-02-09
2019-01-17
Standard
4.5
...
...
...
...
I want to have the revenue for each month, starting from the minimum ContractStartDate (the first time they received any revenue), for both Standard and Discount propositions. So I would like to have something that looks like this:
Month
RevenueStandard
RevenueDiscount
2017-07
90
30
2017-08
85.5
80
2017-09
180
60
...
2022-10
3862
1136
For each month and each proposition (Standard or Discount), I would need to go over every contract and sum up the proposition price if the startdate was after that month and the enddate was before that month (or there is no enddate).
This is the code I tried, but I feel like I am really far off the solution:
SELECT
MonthYear, PropositionReference,
SUM(CASE WHEN STRFTIME("%m %Y", ContractStartDate) <= MonthYear
AND (ContractEndDate IS NULL OR STRFTIME("%m %Y", ContractEndDate) >= MonthYear)
AND PropositionReference = "Standard"
THEN PropositionPrice ELSE 0 END) AS RevenueStandard,
SUM(CASE WHEN STRFTIME("%m %Y", ContractStartDate) <= MonthYear
AND (ContractEndDate IS NULL OR STRFTIME("%m %Y", ContractEndDate) >= MonthYear)
AND PropositionReference = "Discount"
THEN PropositionPrice ELSE 0 END) AS RevenueDiscount
FROM (SELECT *, STRFTIME("%m %Y", ContractStartDate) AS MonthYear FROM Combined)
GROUP BY MonthYear, PropositionReference
ORDER BY MonthYear, PropositionReference
The following could be the basis if your question has been interpreted correctly.
WITH
range AS (
SELECT min(contractstartdate) AS low, max(coalesce(contractenddate,date('now'))) AS high FROM combined
),
t AS (
SELECT
low AS month,
(
SELECT
coalesce(sum(propositionprice),0)
FROM combined
WHERE propositionreference = 'Standard'
AND strftime('%s',low) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
)
AS stnd,
(
SELECT
coalesce(sum(propositionprice),0)
FROM combined
WHERE propositionreference = 'Discount'
AND strftime('%s',low) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
)
AS dscnt
FROM range
UNION ALL
SELECT date(month,'+1 month'),
(
SELECT
coalesce(sum(propositionprice),0)
FROM combined
WHERE propositionreference = 'Standard'
AND strftime('%s',date(month,'+1 month')) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
)
AS stnd,
(
SELECT
coalesce(sum(propositionprice),0)
FROM combined
WHERE propositionreference = 'Discount'
AND strftime('%s',date(month,'+1 month')) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
)
AS dscnt
FROM t
WHERE date(month,'+1 month') < (SELECT max(coalesce(contractenddate,date('now'))) FROM combined)
LIMIT 500 /* just in case to stop continuous loop */
)
SELECT * FROM t;
So this is
first creating a CTE (Common Table Expression (temp table that exists during the execution)) that consists of a single row with two values the lowest started date and the highest end date (if any null then current date ).
second creating another cte, but a recursive one where the first row is the first month, the second the next month .... until the last month (or in this case for a maximum of 500 iterations (to prevent accidental endless loop)).
each iteration retrieves the sum of the respective type for rows from the combined table that include the date being processed.
As a demo, based upon your data then:-
DROP TABLE IF EXISTS combined;
CREATE TABLE IF NOT EXISTS combined (ContractStartDate TEXT,ContractEndDate TEXT,PropositionReference TEXT,PropositionPrice REAL);
INSERT INTO combined VALUES
('2018-01-03', NULL, 'Standard', 4.5)
,('2019-01-17', NULL, 'Discount', 2)
,('2018-02-09', '2019-01-17', 'Standard', 4.5)
;
WITH
range AS (
SELECT min(contractstartdate) AS low, max(coalesce(contractenddate,date('now'))) AS high FROM combined
),
t AS (
SELECT
low AS month,
(
SELECT
coalesce(sum(propositionprice),0)
FROM combined
WHERE propositionreference = 'Standard'
AND strftime('%s',low) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
)
AS stnd,
(
SELECT
coalesce(sum(propositionprice),0)
FROM combined
WHERE propositionreference = 'Discount'
AND strftime('%s',low) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
)
AS dscnt
FROM range
UNION ALL
SELECT date(month,'+1 month'),
(
SELECT
coalesce(sum(propositionprice),0)
FROM combined
WHERE propositionreference = 'Standard'
AND strftime('%s',date(month,'+1 month')) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
)
AS stnd,
(
SELECT
coalesce(sum(propositionprice),0)
FROM combined
WHERE propositionreference = 'Discount'
AND strftime('%s',date(month,'+1 month')) BETWEEN strftime('%s',contractStartDate) AND strftime('%s',coalesce(contractEndDate,date('now')))
)
AS dscnt
FROM t
WHERE date(month,'+1 month') < (SELECT max(coalesce(contractenddate,date('now'))) FROM combined)
LIMIT 500 /* just in case to stop continuous loop */
)
SELECT * FROM t;
DROP TABLE IF EXISTS combined;
results in :-
I'm working on building a select statement for a sales rep commission report that uses postgresql tables. I want it to show these columns:
-Customer No.
-Part No.
-Month-to-date Qty (MTD Qty)
-Year-to-date Qty (YTD Qty)
-Month-to-date Extended Selling Price (MTD Extended)
-Year-to-date Extended Selling Price (YTD Extended)
The data is in two tables:
Sales_History (one record per invoice and this table includes Cust. No. and Invoice Date)
Sales_History_Items (one record per part no. per invoice and this table includes Part No., Qty and Unit Price).
If I do a simple query that combines these two tables, this is what it looks like:
Date / Cust / Part / Qty / Unit Price
Apr 1 / ABC Co. / WIDGET / 5 / $11
Apr 4 / ABC Co. / WIDGET / 8 / $11.50
Apr 1 / ABC Co. / GADGET / 1 / $30
Apr 7 / XYZ Co. / WIDGET / 3 / $11.50
etc.
This is the final result I want (one line per customer per part):
Cust / Part / Qty / MTD Qty / MTD Sales / YTD Qty / YTD Sales
ABC Co. / WIDGET / 13 / $147 / 1500 / $16,975
ABC Co. / GADGET / 1 / $30 / 7 / $210
XYZ Co. / WIDGET / 3 / $34.50 / 18 / $203.40
I’ve been able to come up with this SQL statement so far, which does not get me the extended selling columns (committed_qty * unit_price) per line and then summarize them by cust no./part no., and that’s my problem:
with mtd as
(SELECT sales_history.cust_no, part_no, Sum(sales_history_items.committed_qty) AS MTDQty
FROM sales_history left JOIN sales_history_items
ON sales_history.invoice_no = sales_history_items.invoice_no where sales_history_items.part_no is not null and sales_history.invoice_date >= '2020-04-01' and sales_history.invoice_date <= '2020-04-30'
GROUP BY sales_history.cust_no, sales_history_items.part_no),
ytd as
(SELECT sales_history.cust_no, part_no, Sum(sales_history_items.committed_qty) AS YTDQty
FROM sales_history left JOIN sales_history_items
ON sales_history.invoice_no = sales_history_items.invoice_no where sales_history_items.part_no is not null and sales_history.invoice_date >= '2020-01-01' and sales_history.invoice_date <= '2020-12-31' GROUP BY sales_history.cust_no, sales_history_items.part_no),
mysummary as
(select MTDQty, YTDQty, coalesce(ytd.cust_no,mtd.cust_no) as cust_no,coalesce(ytd.part_no,mtd.part_no) as part_no
from ytd full outer join mtd on ytd.cust_no=mtd.cust_no and ytd.part_no=mtd.part_no)
select * from mysummary;
I believe that I have to nest another couple of aggregate queries in here that would group by cust_no, part_no, unit_price but then have those extended price totals (qty * unit_price) sum up by cust_no, part_no.
Any assistance would be greatly appreciated. Thanks!
Do this in one go with filter expressions:
with params as (
select '2020-01-01'::date as year, 4 as month
)
SELECT h.cust_no, i.part_no,
SUM(i.committed_qty) AS YTDQty,
SUM(i.committed_qty * i.unit_price) as YTDSales,
SUM(i.committed_qty) FILTER
(WHERE extract('month' from h.invoice_date) = p.month) as MTDQty,
SUM(i.committed_qty * i.unit_price) FILTER
(WHERE extract('month' from h.invoice_date) = p.month) as MTDSales
FROM params p
CROSS JOIN sales_history h
LEFT JOIN sales_history_items i
ON i.invoice_no = h.invoice_no
WHERE i.part_no is not null
AND h.invoice_date >= p.year
AND h.invoice_date < p.year + interval '1 year'
GROUP BY h.cust_no, i.part_no
If I follow you correctly, you can do conditional aggregation:
select sh.cust_no, shi.part_no,
sum(shi.qty) mtd_qty,
sum(shi.qty * shi.unit_price) ytd_sales,
sum(shi.qty) filter(where sh.invoice_date >= date_trunc('month', current_date) mtd_qty,
sum(shi.qty * shi.unit_price) filter(where sh.invoice_date >= date_trunc('month', current_date) mtd_sales
from sales_history sh
left join sales_history_items shi on sh.invoice_no = shi.invoice_no
where shi.part_no is not null and sh.invoice_date >= date_trunc('year', current_date)
group by sh.cust_no, shi.part_no
The logic is to filter on the current year, and use simple aggregation to compute the "year to date" figures. To get the "month to date" columns, we can just filter the aggregate functions.
I currently generate a user's "monthly_return" between two months using the code below. How would I turn "monthly_return" into a cumulative "linked" return similar to the StackOverflow question linked below?
Similar question: Running cumulative return in sql
I tried:
exp(sum(log(1 + cumulative_return) over (order by date)) - 1)
But get the error:
PG::WrongObjectType: ERROR: OVER specified, but log is not a window function nor an aggregate function LINE 3: exp(sum(log(1 + cumulative_return) over (order by date)) - 1... ^ : SELECT portfolio_id, exp(sum(log(1 + cumulative_return) over (order by date)) - 1) FROM (SELECT date, portfolio_id, (value_cents * 0.01 - cash_flow_cents * 0.01) / (lag(value_cents * 0.01, 1) over ( ORDER BY portfolio_id, date)) - 1 AS cumulative_return FROM portfolio_balances WHERE portfolio_id = 16 ORDER BY portfolio_id, date) as return_data;
The input data would be:
1/1/2017: $100 value, $100 cash flow
1/2/2017: $100 value, $0 cash flow
1/3/2017: $100 value, $0 cash flow
1/4/2017: $200 value, $100 cash flow
The output would be:
1/1/2017: 0% cumulative return
1/2/2017: 0% cumulative return
1/3/2017: 0% cumulative return
1/4/2017: 0% cumulative return
My current code which shows monthly returns which are not linked (cumulative).
SELECT
date,
portfolio_id,
(value_cents * 0.01 - cash_flow_cents * 0.01) / (lag(value_cents * 0.01, 1) over ( ORDER BY portfolio_id, date)) - 1 AS monthly_return
FROM portfolio_balances
WHERE portfolio_id = 16
ORDER BY portfolio_id, date;
If you want a cumulative sum:
SELECT p.*,
SUM(monthly_return) OVER (PARTITION BY portfolio_id ORDER BY date) as running_monthly_return
FROM (SELECT date, portfolio_id,
(value_cents * 0.01 - cash_flow_cents * 0.01) / (lag(value_cents * 0.01, 1) over ( ORDER BY portfolio_id, date)) - 1 AS monthly_return
FROM portfolio_balances
WHERE portfolio_id = 16
) p
ORDER BY portfolio_id, date;
I don't see that this makes much sense, because you have the cumulative sum of a ratio, but that appears to be what you are asking for.
I am trying to create an Over Time Calculation based on some set criteria. It goes as follows.
Overtime is posted on any day that is over 8 hrs but an employee has to reach 40 total hrs first and the calculation starts at the 1st day moving forward in the week. The Overtime is calculated based on the percentage taken of the SUM total of the cost codes worked.
First you have to find the percentage of each cost code worked for the entire week per employee id. See Example below
Then each day that is Over 8 hrs you take the time on that code for the day and multiply it by the calculated percentage. At the end of the week the regular hours must total 40hrs if they have gone over 40 for the week. See below example
CREATE TABLE [Totals](
[Day] nvarchar (10) null,
[EmployeeID] [nvarchar](100) NULL,
[CostCode] [nvarchar](100) NULL,
[TotalTime] [real] NULL,)
INSERT Into Totals (day,employeeid, CostCode, TotalTime) VALUES
('1','1234','1', 2),
('1','1234','2', 7.5),
('2','1234','1', 1.5),
('2','1234','2', 8),
('3','1234','1', 1),
('3','1234','2', 6),
('4','1234','1', 2),
('4','1234','2', 8),
('5','1234','1', 2),
('5','1234','2', 8),
('1','4567','1', 2),
('1','4567','2', 8.5),
('2','4567','1', 1.5),
('2','4567','2', 7.6),
('3','4567','1', 1),
('3','4567','2', 5),
('4','4567','1', 2),
('4','4567','2', 8),
('5','4567','1', 2),
('5','4567','2', 8)
To get the percentage of each cost Worked it is the SUM total time of each cost per week / SUM total time of the entire week
SELECT employeeid,CostCode,SUM(totaltime) As TotalTime ,
ROUND(SUM(Totaltime) / (select SUM(TotalTime) from Totals where employeeid = '1234') * 100,0) as Percentage
from Totals WHERE EmployeeID = '1234' group by EmployeeID, CostCode
Percentage Calculated for the Week by Cost = 18% worked on Cost 1 and 82% on Cost 2
I would like to take the percentage results for the week and calculate the total time each day in the query
Results Example Day 1: for EmployeeID 1234
Day CostCode RegTime OverTime
1 1 1.73 .27
1 2 6.27 1.23
After editing i get your result, try this:
select calc.*
--select [day], CostCode, EmployeeID
--, CPr * DayEmpRT RegTime_old
, TotalTime - CPr * DayEmpOT RegTime
, CPr * DayEmpOT OverTime
from (
select Agr.*
--, round(EmpC1T / EmpT, 2) C1Pr
--, round(1 - (EmpC1T / EmpT), 2) C2Pr
, round(EmpCT / EmpT, 2) CPr
, case when DayEmpT > 8 then 8 else DayEmpT end DayEmpRT
, case when DayEmpT > 8 then DayEmpT - 8 else 0 end DayEmpOT
from (
select Totals.*
, SUM(TotalTime) over (partition by EmployeeID, [day]) DayEmpT
--, SUM(case when CostCode = 1 then TotalTime end) over (partition by EmployeeID) EmpC1T
, SUM(TotalTime) over (partition by EmployeeID, CostCode) EmpCT
, SUM(TotalTime) over (partition by EmployeeID) EmpT
from Totals
WHERE EmployeeID = '1234' ) Agr ) calc
order by 1,2,3
here is simplest way to calculate this:
select calc.*
, TotalTime * pr4R RegTime
, TotalTime * pr4O OverTime
from(
select Agr.*
, case when EmpT > 40 then round(40/EmpT, 2) else 1 end pr4R
, case when EmpT > 40 then round(1 - 40/EmpT, 2) else 1 end pr4O
from (
select Totals.*
, SUM(TotalTime) over (partition by EmployeeID) EmpT
from Totals
WHERE EmployeeID = '1234' ) Agr ) calc
but be watch on day 3, because there is only 7h.
The 1st query calculate days separately and live day 3.
The 2nd query scale all hours.
it could be another one, that calculate all emp rows but scale separatly RegTime and OverTime, with exception on day where is no 8h and increment it to 8h from OverTime.
This should help you get started...
-- % based on hours worked for each code on a DAILY basis (The original 21% in the question was based on this)
SELECT
T.EmployeeId,
T.Day,
T.CostCode,
T.TotalTime,
CAST(100.0 * T.TotalTime / X.DailyHours AS DECIMAL(10,2)) AS PercentageWorked
FROM #Totals T
INNER JOIN (
SELECT
EmployeeId,
Day,
SUM(TotalTime) AS DailyHours
FROM #Totals
GROUP BY EmployeeId, Day
) X ON X.EmployeeId = T.EmployeeId AND X.Day = T.Day
-- % based on hours worked for each code on a WEEKLY basis (The revised question)
SELECT
T.EmployeeId,
T.CostCode,
SUM(T.TotalTime) AS TotalTime,
CAST(100.0 * SUM(T.TotalTime) / X.WeeklyHours AS DECIMAL(10,2)) AS PercentageWorked
FROM #Totals T
INNER JOIN (
SELECT
EmployeeId,
SUM(TotalTime) AS WeeklyHours
FROM #Totals
GROUP BY EmployeeId
) X ON X.EmployeeId = T.EmployeeId
GROUP BY
T.EmployeeId,
T.CostCode,
X.WeeklyHours