How would l write SQL to label quantities until they run out? - sql

I would like to label quantities (in the quantity table) using the labels assigned (see label assignment table) until the quantity goes to 0. Then I know that I am done labeling that particular ID.
label assignment table is as follows:
ID | Label | Quantity
1 aaa 10
1 bbb 20
2 ccc 20
And my quantity table:
ID | Total Quantity
1 60
2 20
And I would like to get the following result:
ID | Label | Quantity
1 aaa 10 (read from reference table, remaining 50)
1 bbb 20 (read from reference table, remaining 30)
1 [NULL] 30 (no label in reference table, remaining 0)
2 ccc 20 (read from reference table, remaining 0)

You can do it with a simple JOIN and UNION operation so as to include 'not covered' quantities:
SELECT la.ID, la.Label, la.Quantity
FROM label_assignment AS la
INNER JOIN quantity AS q ON la.ID = q.ID
UNION
SELECT q.ID, NULL AS Label, q.TotalQuantity - la.TotalQuantity
FROM quantity AS q
INNER JOIN (
SELECT ID, SUM(Quantity) AS TotalQuantity
FROM label_assignment
GROUP BY ID
) AS la ON q.ID = la.ID AND q.TotalQuantity > la.TotalQuantity
Demo here

DECLARE #PerLabelQuantity TABLE(Id int, Label varchar(10), Quantity int);
INSERT INTO #PerLabelQuantity
VALUES (1, 'aaa', 10), (1, 'bbb', 20), (2, 'ccc', 20);
DECLARE #QuantityRequired TABLE(Id int, TotalQuantity int);
INSERT INTO #QuantityRequired
VALUES (1, 60), (2, 20);
SELECT t.Id,
CASE WHEN o.Overflowed = 1 THEN NULL ELSE t.Label END AS Label,
CASE WHEN o.Overflowed = 1 THEN t.QuantityStillNeeded
WHEN t.QuantityStillNeeded < 0 THEN t.Quantity + t.QuantityStillNeeded
ELSE t.Quantity END AS Quantity
FROM (
SELECT p.Id, p.Label, p.Quantity,
MAX(p.Label) OVER (PARTITION BY p.Id) AS LastLabel,
r.TotalQuantity - SUM(p.Quantity)
OVER (PARTITION BY p.Id
ORDER BY Label
ROWS UNBOUNDED PRECEDING) AS QuantityStillNeeded
FROM #PerLabelQuantity p
INNER JOIN #QuantityRequired r ON p.Id = r.Id) t
INNER JOIN (VALUES (0), (1)) o(Overflowed)
ON t.LastLabel = t.Label AND t.QuantityStillNeeded > 0 OR Overflowed = 0
WHERE t.QuantityStillNeeded > -t.Quantity; -- Remove this if you want labels with
-- 0 quantity used, but you'll need to tweak
-- the CASE expression for Quantity
The subquery calculates a set of used up labels and how many items remain afterward. If there is any quantity remaining after the last label, then we need to insert a row in the result set. To do this, I join on a two-element table but the join condition is only true when we are at the last label and there is quantity remaining. This is probably a confusing way to do this, and we could combine the UNION from George's answer with the subquery from mine to avoid this Overflow table.
Here's the changed (and probably preferable) query:
SELECT Id,
Label,
CASE WHEN QuantityStillNeeded < 0 THEN Quantity + QuantityStillNeeded
ELSE Quantity END AS Quantity
FROM (SELECT p.Id, p.Label, p.Quantity,
r.TotalQuantity - SUM(p.Quantity)
OVER (PARTITION BY p.Id
ORDER BY Label
ROWS UNBOUNDED PRECEDING) AS QuantityStillNeeded
FROM #PerLabelQuantity p
INNER JOIN #QuantityRequired r ON p.Id = r.Id) t
WHERE t.QuantityStillNeeded > -t.Quantity
UNION ALL
SELECT q.Id, NULL AS Label, q.TotalQuantity - la.TotalQuantity AS Quantity
FROM #QuantityRequired AS q
INNER JOIN (
SELECT Id, SUM(Quantity) AS TotalQuantity
FROM #PerLabelQuantity
GROUP BY Id) la ON q.ID = la.ID
WHERE q.TotalQuantity > la.TotalQuantity

Simplest answer I think, after getting ideas from the other answers: Just create a "FAKE" label for the missing amount:
DECLARE #PerLabelQuantity TABLE(Id int, Label varchar(10), Quantity int);
INSERT INTO #PerLabelQuantity
VALUES (1, 'aaa', 10), (1, 'bbb', 20), (2, 'ccc', 20);
SELECT *
FROM #PerLabelQuantity
DECLARE #QuantityRequired TABLE(Id int, TotalQuantity int);
INSERT INTO #QuantityRequired
VALUES (1, 60), (2, 20);
SELECT *
FROM #QuantityRequired
-- MAKE A FAKE LABEL LET'S CALL IT [NULL] WITH THE AMOUNT THAT IS NOT LABELED
-- i.e. WITH THE REMAINING AMOUNT
-- Probably should be done by copying the original data and the following
-- into a temp table but this is just for proof of concept
INSERT INTO #PerLabelQuantity( Id, Label, Quantity )
SELECT q.ID,
NULL,
ISNULL(q.TotalQuantity - p.TotalQuantityLabeled, q.TotalQuantity)
FROM #QuantityRequired q
LEFT JOIN (SELECT p.ID, SUM(Quantity) AS TotalQuantityLabeled
FROM #PerLabelQuantity p
GROUP BY p.Id) p ON
p.ID = q.ID
AND q.TotalQuantity - p.TotalQuantityLabeled > 0
SELECT *
FROM #PerLabelQuantity p

Related

Count multiple columns in a query for multiple criteria

I have a query that should count the number of items used by department.
The first two tables give me the units and the persons who used the items.
The third table tells who used what.
STAFF(EMPID,EMPNAME,UNITCTR)
CAFUNIT(UNITCTR, UNITDSC)
CAFTRXHD(BILLNO,TRXDATE,ITEMCODE,ITEMPRICE,ITEMDESC,ITEMPRICE,EMPID)
This is the query
SELECT a.UNITCTR, b.UNITDSC, COUNT(c.ITEMCODE)
FROM UNIT.STAFF a, UNIT.CAFUNIT b, UNIT.CAFTRXHID c
WHERE a.UNITCTR = b.UNITCTR
AND c.ITEMCODE IN ('397', '398', '399', '400', '401', '402', '403')
GROUP BY a.UNITCTR, b.UNITDSC
This returns the count of all used items by department for example Department A used 200 of these items, so I get the Department ID, name and the total count of items.
123|Cafeteria|200
where 200 is the sum of all of these items (397 to 403)
I need to know the count for each item by department for instance Department A used 10 boxes of item 397 and 5 of item B and …
123|Cafeteria|20|20|50|30|50|10|70
Using what is suggested here isn't working or I am not doing it right. Any ideas?
Hello after checking you request, follows a query what I think could help you, I'm using two queries with partition, one to group all items by unit and another to group items by unit and item code.
WITH STAFF AS (
SELECT * FROM (
VALUES
(1, 'John Smith','U1'),
(2, 'David Thompson','U2'),
(3, 'Stacey Leigh','U3')
) AS _ (EMPID,EMPNAME,UNITCTR)
),CAFUNIT AS (
SELECT * FROM (
VALUES
('U1', 'Unit 1'),
('U2', 'Unit 2'),
('U3', 'Unit 3')
) AS _ (UNITCTR,UNITDSC)
),CAFTRXHD as (
SELECT * FROM (
VALUES
(1, '2022-01-01','item 1',100,'Item desc 1',1),
(2, '2022-02-01','item 2',200,'Item desc 2',2),
(3, '2022-03-01','item 3',300,'Item desc 3',3),
(4, '2022-01-01','item 1',100,'Item desc 1',1),
(5, '2022-01-01','item 2',100,'Item desc 2',1),
(6, '2022-01-01','item 2',100,'Item desc 2',1),
(7, '2022-02-01','item 2',200,'Item desc 2',2),
(8, '2022-02-01','item 2',200,'Item desc 2',2),
(9, '2022-03-01','item 3',300,'Item desc 3',3),
(10, '2022-03-01','item 3',300,'Item desc 3',3),
(11, '2022-03-01','item 3',300,'Item desc 3',3)
) AS _ (BILLNO,TRXDATE,ITEMCODE,ITEMPRICE,ITEMDESC,EMPID)
),
--Get all item group by Unit
GetAllByUnit as (
SELECT
t.* FROM
(
SELECT
IT.UNITCTR,
IT.UNITDSC,
(SUM(ITEMPRICE) OVER (partition by IT.UNITCTR order by IT.UNITDSC)) as TotalValue
FROM
CAFTRXHD as HD
INNER JOIN STAFF as FF ON HD.EMPID = FF.EMPID
INNER JOIN CAFUNIT as IT ON FF.UNITCTR = IT.UNITCTR
) as t
GROUP BY t.UNITCTR, T.UNITDSC, T.TotalValue
),
--Get all item group by Unit and Item code
GetAllByUnitAndCode as (
SELECT
t.* FROM
(
SELECT
IT.UNITCTR,
IT.UNITDSC,
HD.ITEMCODE,
(SUM(ITEMPRICE) OVER (partition by IT.UNITCTR,HD.ITEMCODE order by IT.UNITDSC)) as TotalValue
FROM
CAFTRXHD as HD
INNER JOIN STAFF as FF ON HD.EMPID = FF.EMPID
INNER JOIN CAFUNIT as IT ON FF.UNITCTR = IT.UNITCTR
) as t
GROUP BY t.UNITCTR, T.UNITDSC, T.ITEMCODE, T.TotalValue
)
SELECT * FROM GetAllByUnit
--SELECT * FROM GetAllByUnitAndCode
The result
UNITCTR UNITDSC TotalValue
U1 Unit 1 400
U2 Unit 2 600
U3 Unit 3 1200
Comment the query last line --SELECT * FROM GetAllByUnit and remove the comment over SELECT * FROM GetAllByUnitAndCode
--SELECT * FROM GetAllByUnit
SELECT * FROM GetAllByUnitAndCode
The result here is top down not only one line
UNITCTR UNITDSC ITEMCODE TotalValue
U1 Unit 1 item 1 200
U1 Unit 1 item 2 200
U2 Unit 2 item 2 600
U3 Unit 3 item 3 1200
Best Regards

Sum Quantity and Filter Results

I have the following table with order id's and quantities. I need to be able to sum the quantity and retrieve the id's that that equal less than the provided number.
| id | quantity |
|------|----------|
| 100 | 1 |
| 200 | 25 |
| 300 | 15 |
For example, I need the id's where the sum of quantity equals less than 25.
When I try the following it only provides me the first id (100).
Select *
from (
select *,
SUM (Quantity) OVER (ORDER BY Id) AS SumQuantity
from dbo.Orders
) as A
where SumQuantity <= 25
Is it possible to adjust this query where it will provide me id 100 and 300, since the sum total of those orders is less than 25?
I know I can use a where clause on for quantity less than 25, but the important thing here is I need to be able to sum the quantity and pull id's that give me less than the provided number.
Thank you in advance!
Perhaps you want to order by the quantity instead of id?
Select o.*
from (select o.*, SUM (Quantity) OVER (ORDER BY quantity) AS SumQuantity
from dbo.Orders
) o
where SumQuantity <= 25;
This chooses the smallest values so you will get the most rows.
Group by Id and set the condition in the HAVING clause:
select Id, SUM(Quantity) AS SumQuantity
from Orders
group by Id
having SUM(Quantity) <= 25
See the demo.
Results:
Id | SumQuantity
100 | 1
200 | 25
300 | 15
If you want to include all the columns you can modify your query to not ORDER BY id but PARTITION BY id:
select *
from (
select *,
SUM (Quantity) OVER (PARTITION BY Id) AS SumQuantity
from Orders
) as A
where SumQuantity <= 25
For this dataset:
CREATE TABLE Orders([id] varchar(6), [quantity] int);
INSERT INTO Orders([id], [quantity])VALUES
('100', '1'), ('100', '2'),
('200', '25'), ('200', '3'),
('300', '15'), ('300', '5');
Results:
id | quantity | SumQuantity
100 | 1 | 3
100 | 2 | 3
300 | 15 | 20
300 | 5 | 20
See the demo.
Setup:
Your threshold can vary, so let's make it into a variable:
declare #threshold int = 25;
But I also imagine that your table values can vary, like if we add another row only having a quantity of 2:
declare #orders table (id int, quantity int)
insert #orders values (100,1), (200,25), (300,15), (400, 2);
Solution:
For this, we'll need a recursive kind of cross joining:
with
traverse as (
select ids = convert(nvarchar(255), id),
id,
quantity
from #orders
where quantity < #threshold
union all
select ids =
convert(nvarchar(255), tv.ids + ',' +
convert(nvarchar(255), o.id)),
o.id,
quantity = tv.quantity + o.quantity
from traverse tv
cross join #orders o
where tv.id < o.id
and tv.quantity + o.quantity < #threshold
)
select t.ids, t.quantity
from traverse t;
which will produce:
Explanation:
The above code is an algorithm that builds a tree. It starts with your base id's and quantities as nodes (the anchor part of the CTE). It trims anything not meeting the threshold.
It then adds edges by cross joining with orders table again (the recursive part of the CTE), but it only includes the following:
Id's that are greater than the last id considered in the current node (this is so that we avoid duplicate considerations, such as ids = '300,400' and ids = '400,300').
Ids where the sum of quantities is less than the threshold.
Warnings:
But beware, the type of problem you're considering will have computational complexity considerations. But because of the trimming conditions, it will be more efficient than doing all the cross joins first and then filtering the result set at the end.
Also, keep in mind that you may get rows in your table where there is no single set of numbers that will sum up to less than 25. Rather, you can get different paths to that sum. The way I produce the results here will help you identify such a situation.
cross join is perfect for this task, try:
declare #tbl table (id int, quantity int);
insert into #tbl values
(100, 1), (200, 25), (300, 15), (400, 10);
select distinct case when t1.id > t2.id then t1.id else t2.id end,
case when t1.id < t2.id then t1.id else t2.id end
from #tbl t1
cross join #tbl t2
where t1.id <> t2.id
and t1.quantity + t2.quantity < 25

Retrieve specific value if set, or general if not

I have to write a query which will import some data from table. Table structure is like:
Item_ID
Plant
Price
I have second table with
Item_ID
Plant
Second table is a key, and I have to match rows from first table to get valid price. Seems to be easy, however:
In first table column plant might determinate specific plant, or have value 'ALL'. What I want to do is retrieve price for given plant, if it is set, or get price for value 'ALL' if there is no row for given plant. In other words:
If first.Plant = second.Plant
return price
Else If first.Plant = 'ALL'
return price
Else
return NULL
I can't use simple ON first.Plant = second.Plant OR first.Plant = 'ALL', because there might be two rows: one for given plant and second for rest with value 'ALL'. I need to return only first price in that case. E.g.
Item_ID | Plant | Price
2 | M1 | 10,0
2 | All | 12,0
1 | All | 9,0
In that case for Item_ID = 2 and Plant = M1 the only valid price = 10, but for Item_ID = 2 and Plant = M2 price = 12, and for any Item_ID = 1 price = 9
I hope You understood something after my explanation ;)
Using ROW_Number and a Common Table Expression you can ensure that ROW_NUMBER is partitioned by Item_ID and Plant and ordered such that if there are two rows then 'All' is second. You then simple select the rows with a row number = 1:
Setup
CREATE TABLE #Price
(
Item_ID int,
Plant Varchar(20),
Price Decimal(6,2)
)
CREATE TABLE #Plant
(
Item_ID int,
Plant VARCHAR(20)
)
INSERT INTO #Price
VALUES (2, 'M1', 10.0),
(2, 'All', 12.0),
(1, 'All', 9.0)
INSERT INTO #Plant
VALUES (2, 'M1'),
(2, 'M2'),
(1, 'M1'),
(1, 'M2')
Here's the query for SQL Server >= 2012
;WITH CTE
AS
(
SELECT PL.Item_ID, PL.Plant, PR.PRice, ROW_NUMBER() OVER (PARTITION BY Pl.Item_ID, Pl.Plant ORDER BY IIF(PR.Plant = 'All', 1, 0)) AS RN
FROM #Plant PL
INNER JOIN #Price PR
ON PL.Item_Id = PR.Item_Id AND (PL.Plant = PR.Plant OR PR.Plant = 'ALL')
)
SELECT Item_Id, Plant, Price
FROM CTE
WHERE RN = 1
And here's a version using CASE which will work for all SQL Server >= 2008 R2
;WITH CTE
AS
(
SELECT PL.Item_ID, PL.Plant, PR.PRice, ROW_NUMBER() OVER (PARTITION BY Pl.Item_ID, Pl.Plant ORDER BY CASE WHEN PR.Plant = 'All' Then 1 Else 0 End) AS RN
FROM #Plant PL
INNER JOIN #Price PR
ON PL.Item_Id = PR.Item_Id AND (PL.Plant = PR.Plant OR PR.Plant = 'ALL')
)
SELECT Item_Id, Plant, Price
FROM CTE
WHERE RN = 1

Product Final Price after Many Discount given

I have two tables.
One table of Ids and their prices, and second table of discounts per Id.
In the table of discounts an Id can has many Discounts, and I need to know the final price of an Id.
What is the Best way to query it (in one query) ?
The query should be generic for many discounts per id (not only 2 as mentioned below in the example)
For example
Table one
id price
1 2.00
2 2.00
3 2.00
Table two
id Discount
1 0.20
1 0.30
2 0.40
3 0.50
3 0.60
Final result:
id OrigPrice PriceAfterDiscount
1 2.00 1.12
2 2.00 1.20
3 2.00 0.40
Here's another way to do it:
SELECT T1.ID, T1.Price, T1.Price * EXP(SUM(LOG(1 - T2.Discount)))
FROM T1 INNER JOIN T2 ON T1.ID = T2.ID
GROUP BY T1.ID, T1.Price
The EXP/LOG trick is just another way to do multiplication.
If you have entries in T1 without discounts in T2, you could change the INNER JOIN to a LEFT JOIN. You would end up with the following:
ID Price Discount
4 2.00 NULL
Your logic can either account for the null in the discounted price column and take the original price instead, or just add a 0 discount record for those.
Generally it can be done with a trick with LOG/EXP functions but it is complex.
Here is a basic example:
declare #p table(id int, price money)
declare #d table(id int, discount money)
insert into #p values
(1, 2),
(2, 2),
(3, 2)
insert into #d values
(1, 0.2),
(1, 0.3),
(2, 0.4),
(3, 0.5),
(3, 0.6)
select p.id,
p.price,
p.price * ca.discount as PriceAfterDiscount
from #p p
cross apply (select EXP(SUM(LOG(1 - discount))) as discount FROM #d where id = p.id) ca
For simpler(cursor based approach) you will need a recursive CTE, but in this case you need some unique ordering column in Discounts table to run it correctly. This is shown in #Tanner`s answer.
And finally you can approach this with a regular cursor
I believe this produces the desired results using a CTE to iterate through the discounts. The solution below is re-runnable in isolation.
Edited: to include data that might not have any discounts applied in the output with a left join in the first part of the CTE.
CREATE TABLE #price
(
id INT,
price DECIMAL(5, 2)
);
CREATE TABLE #discount
(
id INT,
discount DECIMAL(5, 2)
);
INSERT INTO #price
(
id,
price
)
VALUES
(1, 2.00),
(2, 2.00),
(3, 2.00),
(4, 3.50); -- no discount on this item
INSERT INTO #discount
(
id,
discount
)
VALUES
(1, 0.20),
(1, 0.30),
(2, 0.40),
(3, 0.50),
(3, 0.60);
-- new temporary table to add a row number to discounts so we can iterate through them
SELECT d.id,
d.discount,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY d.discount) rn
INTO #GroupedDiscount
FROM #discount AS d;
-- note left join in first part of cte to get prices that aren't discounted included
WITH cte
AS (SELECT p.id,
p.price,
CASE
WHEN gd.discount IS NULL THEN
p.price
ELSE
CAST(p.price * (1.0 - gd.discount) AS DECIMAL(5, 2))
END AS discountedPrice,
gd.rn
FROM #price AS p
LEFT JOIN #GroupedDiscount AS gd
ON gd.id = p.id
AND gd.rn = 1
UNION ALL
SELECT cte.id,
cte.price,
CAST(cte.discountedPrice * (1.0 - gd.discount) AS DECIMAL(5, 2)) AS discountedPrice,
cte.rn + 1 AS rn
FROM cte
INNER JOIN #GroupedDiscount AS gd
ON gd.id = cte.id
AND gd.rn = cte.rn + 1
)
SELECT cte.id,
cte.price,
MIN(cte.discountedPrice) AS discountedPrice
FROM cte
GROUP BY id,
cte.price;
DROP TABLE #price;
DROP TABLE #discount;
DROP TABLE #GroupedDiscount;
Results:
id price discountedPrice
1 2.00 1.12
2 2.00 1.20
3 2.00 0.40
4 3.50 3.50 -- no discount
As others have said, EXP(SUM(LOG())) is the way to do the calculation. Here is basically same approach from yet another angle:
WITH CTE_Discount AS
(
SELECT Id, EXP(SUM(LOG(1-Discount))) as TotalDiscount
FROM TableTwo
GROUP BY id
)
SELECT t1.id, CAST(Price * COALESCE(TotalDiscount,1) AS Decimal(18,2)) as FinalPRice
FROM TableOne t1
LEFT JOIN CTE_Discount d ON t1.id = d.id
SQLFIddle Demo

Calculate in join in sql

I am trying to achieve staggered calculation in joins in sql 2008. I can have n number of rows for 1 job id. I have created a sample below
CREATE TABLE Job
(
JobID INT NOT NULL,
Amount INT NOT NULL
);
INSERT INTO Job (JobID, Amount)
VALUES (1, 25),
(1, 45),
(1, 40),
(2, 25),
(3, 26),
(3, 26);
now the discount for JobID = 1 is 80 , So what I am expecting in output of query result is below:
If the Amount > Discount , so show the finalvalue = Amount - Discount
but if Amount < Discount , then show Finalvalue = Amount - Amount ,
and if Discount is still left , deduct the same from the subsequent rows.
Job ID Amount FinalValue
1 25 0
1 45 0
1 40 30
Can all this be done in a join?
Here you are:
EDIT: ATTENTION: You should add a column for sorting. My approach is partitioning AND sorting by JobID which makes the output random...
EDIT: Sorry, did not add the tables...
CREATE TABLE Job
(
JobID INT NOT NULL,
Amount INT NOT NULL
);
INSERT INTO Job (JobID, Amount)
VALUES (1, 25), (1, 45), (1, 40), (2, 25), (3, 26), (3, 26);
CREATE TABLE Discount
(
JobID INT NOT NULL,
Discount INT NOT NULL
);
INSERT INTO Discount(JobID,Discount)VALUES(1,80),(2,0),(3,10);
WITH myCTE AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY Job.JobID ORDER BY Job.JobID) AS inx
,Job.JobID
,Job.Amount
,Discount.Discount
FROM Job
INNER JOIN Discount ON Job.JobID=Discount.JobID
)
SELECT * FROM myCTE
CROSS APPLY
(
SELECT SUM(x.Amount)
FROM myCTE AS x
WHERE x.JobID=myCTE.JobID
AND x.inx<=myCTE.inx
) AS AmountCummulativ(AmountCummulativ)
CROSS APPLY(SELECT AmountCummulativ-myCTE.Discount) AS DiscountCalculated(DiscountCalculated)
CROSS APPLY(SELECT CASE WHEN DiscountCalculated<0 THEN 0 ELSE DiscountCalculated END) AS DiscountResolved(DiscountResolved)
Hope this helps
I think what you are looking for can be done using a case statement
select a.jobid,a.Amount,case when a.amount > b.discount then a.amount - b.discount else 0 end final_value
from Job a inner join Job_discount b on a.jobid = b.jobid
You can look at the results here http://sqlfiddle.com/#!3/f9a46/1
I had to assume the discount table structure
I added some sequence (row number) for the Job table (calling it JobOrder), to increment the sums. You can change the order as it is now for JobId, Amount!
With JobOrder as (
-- Job order by id and amount
select row_number() over (order by JobID, Amount asc) as rowno, JobId, Amount from Job
),
JobSumIncr as (
select
JobID,
Amount,
(select sum(Amount)
from JobOrder j2
where j2.JobID = j.JobID and j2.RowNo <= j.RowNo
) as AmountTotal
from JobOrder j
)
select
j.JobID,
j.Amount,
j.AmountTotal,
d.Discount,
(case when d.Discount>=j.AmountTotal then 0 else j.AmountTotal-d.Discount end) as FinalValue
from
JobSumIncr j left join Discount d on j.JobID = d.JobID;
Asuming that your Discount table is something like:
CREATE TABLE Discount (
JobID INT,
Discount INT
);
SqlFiddle here! and for a more secure Sql (checking null values and see the discount left) see this SQLFiddle too.
A version which keeps track of the discount left, see sqlfiddle-2 above.
You can try following code : 80 is the discount amount
Select JobID, Amount,
case when SUM (Amount) OVER (partition by JobID ORDER BY JobID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 80 then SUM(Amount) OVER (partition by JobID ORDER BY JobID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 80 else 0 end as FinalValue
from Job