Get last value in sequence - sql

I am trying to return the last value in a sequence of a particular event, my inital thought was to use LAST_VALUE() but I can't get this to work. I could do this with subqueries and joins however is there a window function that would give this result far easier?
Right now the query is pulling the max amount but what I want is the last amount based on the seq column
SQL Fiddle
Data
| PaymentID | Description | Result | Seq |
|-----------|----------------------|--------|-----|
| 1 | Entered Payment Page | Yes | 1 |
| 1 | Amount Entered | 50 | 2 |
| 1 | Amount Entered | 60 | 3 |
| 1 | Amount Entered | 20 | 4 |
| 1 | Amount Confirmed | Yes | 5 |
| 2 | Entered Payment Page | Yes | 1 |
| 2 | Amount Entered | 100 | 2 |
| 2 | Amount Confirmed | Yes | 3 |
| 3 | Entered Payment Page | Yes | 1 |
| 3 | Amount Entered | 4 | 2 |
| 3 | Amount Confirmed | No | 3 |
| 3 | Amount Entered | 8 | 4 |
| 3 | Amount Confirmed | Yes | 5 |
Current Query Result
| PaymentID | InPayment | Amount | Confirmed |
|-----------|-----------|--------|-----------|
| 1 | Yes | 60 | Yes |
| 2 | Yes | 100 | Yes |
| 3 | Yes | 8 | Yes |
Desired result
| PaymentID | InPayment | Amount | Confirmed |
|-----------|-----------|--------|-----------|
| 1 | Yes | 20 | Yes |
| 2 | Yes | 100 | Yes |
| 3 | Yes | 8 | Yes |

You can use row_number() and conditional aggregation:
select paymentid,
max(case when description = 'Entered Payment Page' then result end) as inpayment,
max(case when description = 'Amount Entered' then result end) as amount_entered,
max(case when description = 'Amount Confirmed' then result end) as amount_confirmed
from (select t.*,
row_number() over (partition by paymentid, description order by seq desc) as seqnum
from paymentinfo t
) t
where seqnum = 1
group by paymentid;
Here is a SQL Fiddle.

Related

SQL Count depending on certain conditions

I have two tables.
One have userid and email (users table). The other have payments information (payments table) from the userid in users.
users
+--------+------------+
| Userid | Name |
+--------+------------+
| 1 | Alex T |
| 2 | Jeremy T |
| 3 | Frederic A |
+--------+------------+
payments
+--------+-----------+------------+----------+
| Userid | ValuePaid | PaidMonths | Refunded |
+--------+-----------+------------+----------+
| 1 | 1 | 12 | null |
| 1 | 20 | 12 | null |
| 1 | 20 | 12 | null |
| 1 | 20 | 1 | null |
| 2 | 1 | 1 | null |
| 2 | 20 | 12 | 1 |
| 2 | 20 | 12 | null |
| 2 | 20 | 1 | null |
| 3 | 1 | 12 | null |
| 3 | 20 | 1 | 1 |
| 3 | 20 | 1 | null |
+--------+-----------+------------+----------+
I want to count the PaidMonths taking in consideration the following rules:
If ValuePaid < 10 PaidMonths should be = 0.23 (even if in the column the value seen is any other mumber).
If Refund=1 the PaidMonths should be = 0.
Based on this when i join both tables by userid, and sum the PaidMonths based in the previousrules, i expect to see as result:
+--------+------------+------------+
| userid | Name | paidMonths |
+--------+------------+------------+
| 1 | Alex T | 25.23 |
| 2 | Jeremy T | 13.23 |
| 3 | Frederic A | 1.23 |
+--------+------------+------------+
Can you help me to achieve this in the most elegant way? Should a temporary table be used?
The following gives your desired results, using apply with case expression to map your values:
select u.UserID, u.Name, Sum(pm) PaidMonths
from users u join payments p on p.userid=u.userid
cross apply (values(
case
when valuepaid <10 then 0.23
when Refunded=1 then 0
else PaidMonths end
))x(pm)
group by u.UserID, u.Name
See Working Fiddle

How to set succeeding row values as empty if same with previous row

How can I convert this table:
+---------------------+-------------------------------+-------+-------+
| PropertyName | Emergency | Count | Total |
+---------------------+-------------------------------+-------+-------+
| IH | No | 8 | 12 |
| IH | No Water | 1 | 12 |
| IH | Smoke Alarm not working | 1 | 12 |
| IH | Broken Lock - Exterior | 1 | 12 |
| IH | Leaking Water | 1 | 12 |
| GG | No | 5 | 10 |
| GG | Leaking Water | 3 | 10 |
| GG | Property Damage (Significant) | 1 | 10 |
| GG | Toilet - Clogged (1 Bathroom) | 1 | 10 |
| PLB | No | 5 | 10 |
| PLB | Resident Locked Out | 2 | 10 |
| PLB | Smoke Alarm not working | 1 | 10 |
| PLB | Tub - Clogged (1 Bathroom) | 1 | 10 |
| PLB | Leaking Water | 1 | 10 |
+---------------------+-------------------------------+-------+-------+
to something like this:
+---------------------+-------------------------------+-------+-------+
| PropertyName | Emergency | Count | Total |
+---------------------+-------------------------------+-------+-------+
| IH | No | 8 | 12 |
| | No Water | 1 | |
| | Smoke Alarm not working | 1 | |
| | Broken Lock - Exterior | 1 | |
| | Leaking Water | 1 | |
| GG | No | 5 | 10 |
| | Leaking Water | 3 | |
| | Property Damage (Significant) | 1 | |
| | Toilet - Clogged (1 Bathroom) | 1 | |
| PLB | No | 5 | 10 |
| | Resident Locked Out | 2 | |
| | Smoke Alarm not working | 1 | |
| | Tub - Clogged (1 Bathroom) | 1 | |
| | Leaking Water | 1 | |
+---------------------+-------------------------------+-------+-------+
What I want to do is if the PropertyName is the same as the previous row succeeding row should be omitted. This should be the same for the Total.
I can't try LAG/LEAD because we are only using SQL SERVER 2008 - i know it's a bummer.
You can generate row numbers in a CTE expression, then select top most row numbers per PropertyName:
;with tableWithRowNums as(
select ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum,
PropertyName,
Emergency,
Count,
Total
from the_table ),
first_rows as (
select min(rownum) as first_row,
PropertyName
from tableWithRowNums
group by rowNums.PropertyName
)
select ISNULL(first_rows.PropertyName, ''),
tableWithRowNums.Emergency,
tableWithRowNums.Count,
tableWithRowNums.Total
from tableWithRowNums
left join first_rows on first_rows.first_row = tableWithRowNums.rownum
order by tableWithRowNums.rownum;
In general, this is better done at the application level. But, in your case, yo just seem to want the values on the "No" row. You can do this with some case expressions:
select (case when Emergency = 'No' then PropertyName end) as PropertyName,
Emergency, Count,
(case when Emergency = 'No' then Total end) as total
from t
order by max(total) over (partition by PropertyName) desc, -- you seem to want the largest total first
t.PropertyName,
(case when Emergency = 'No' then 1 else 2 end),
t.Total desc;
The ORDER BY is very important. SQL queries are not guaranteed to return results in any particular order unless the outermost SELECT has an ORDER BY.

Map replenishment to requirement based on field value - SQL Server

I am attempting to find which "replenishment" (a positive transaction quantity) can be matched to a "requirement" (a negative transaction quantity).
The basic logic would be: For a given requirement, find the first available replenishment (whether that replenishment be from existing inventory, or from an upcoming change).
I am working with a table dbo_purchases_new that looks like this:
| Element_ID | Element | Transaction_Date | Transaction_Quantity | Total_Inventory |
|:----------:|:----------:|:----------------:|:--------------------:|:---------------:|
| | STOCK | | 5 | 5 |
| MO302 | Make_Order | 1/3/2019 | 1 | 6 |
| SO105 | Sale | 2/1/2019 | -1 | 5 |
| SO106 | Sale | 2/1/2019 | -1 | 4 |
| MO323 | Make_Order | 2/2/2019 | 1 | 5 |
| SO107 | Sale | 2/4/2019 | -1 | 4 |
| SO191 | Sale | 2/5/2019 | -1 | 3 |
| SO123 | Sale | 2/6/2019 | -1 | 2 |
| SO166 | Sale | 3/1/2019 | -1 | 1 |
| SO819 | Sale | 3/5/2019 | -1 | 0 |
| SO603 | Sale | 3/10/2019 | -4 | -3 |
| MO400 | Make_Order | 3/15/2019 | 1 | -2 |
| MO459 | Make_Order | 3/15/2019 | 1 | -1 |
| MO460 | Make_Order | 3/18/2019 | 1 | 0 |
| MO491 | Make_Order | 3/19/2019 | 1 | 1 |
| MO715 | Make_Order | 4/1/2019 | 3 | 4 |
| SO100 | Sale | 4/2/2019 | -1 | 3 |
| SO322 | Sale | 4/3/2019 | -1 | 2 |
| SO874 | Sale | 4/4/2019 | -1 | 1 |
| SO222 | Sale | 4/5/2019 | -1 | 0 |
| MO999 | Make_Order | 4/5/2019 | 1 | 1 |
| SO999 | Sale | 4/6/2019 | -1 | 0 |
that is being created as a result of this question.
I am now attempting to track which Make_Order will fulfill which Sale by tracking the Transaction_Quantity.
Ideally, the resulting dataset would look like this, where Replenishment and Replenishment_Date are newly added columns:
| Element_ID | Element | Transaction_Date | Transaction_Quantity | Total_Inventory | Replenishment | Replenishment_Date |
|:----------:|:----------:|:----------------:|:--------------------:|:---------------:|:-------------:|:------------------:|
| | STOCK | | 5 | 5 | NULL | NULL |
| MO302 | Make_Order | 1/3/2019 | 1 | 6 | NULL | NULL |
| SO105 | Sale | 2/1/2019 | -1 | 5 | STOCK | NULL |
| SO106 | Sale | 2/1/2019 | -1 | 4 | STOCK | NULL |
| MO323 | Make_Order | 2/2/2019 | 1 | 5 | NULL | NULL |
| SO107 | Sale | 2/4/2019 | -1 | 4 | STOCK | NULL |
| SO191 | Sale | 2/5/2019 | -1 | 3 | STOCK | NULL |
| SO123 | Sale | 2/6/2019 | -1 | 2 | STOCK | NULL |
| SO166 | Sale | 3/1/2019 | -1 | 1 | MO302 | 1/3/2019 |
| SO819 | Sale | 3/5/2019 | -1 | 0 | MO323 | 2/2/2019 |
| SO603 | Sale | 3/10/2019 | -4 | -3 | MO460 | 3/18/2019 |
| MO400 | Make_Order | 3/15/2019 | 1 | -2 | NULL | NULL |
| MO459 | Make_Order | 3/15/2019 | 1 | -1 | NULL | |
| MO460 | Make_Order | 3/18/2019 | 1 | 0 | NULL | NULL |
| MO491 | Make_Order | 3/19/2019 | 1 | 1 | NULL | NULL |
| MO715 | Make_Order | 4/1/2019 | 3 | 4 | NULL | NULL |
| SO100 | Sale | 4/2/2019 | -1 | 3 | MO491 | 3/19/2019 |
| SO322 | Sale | 4/3/2019 | -1 | 2 | MO715 | 4/1/2019 |
| SO874 | Sale | 4/4/2019 | -1 | 1 | MO715 | 4/1/2019 |
| SO222 | Sale | 4/5/2019 | -1 | 0 | MO715 | 4/1/2019 |
| MO999 | Make_Order | 4/5/2019 | 1 | 1 | NULL | NULL |
| SO999 | Sale | 4/6/2019 | -1 | 0 | SO999 | 4/5/2019 |
The ruleset would essentially be:
For a given requirement (a negative transaction quantity of arbitrary value), find which replenishment (a positive transaction quantity of arbitrary value) satisfies it.
Stock is assigned to the first requirements until it runs out. NOTE
-- it could be the case that stock does not exist, so this only applies IF stock does exist
Then, map replenishments to requirements based on the
Transaction_Date in ASC order
I am very confused on how to accomplish this. I imagine some pseudocode would look something like:
for curr in transaction_quantity:
if curr < 0:
if stock.exists() and stock.notempty():
fill in data from that
else:
find next replenishment
fill in data from that
else:
next
Right now, I have this so far, but I know that it will not run. I am very confused on where to go from here. I have tried looking at posts like this, but that did not have an answer. I then tried looking up CURSOR, but that was very confusing to me and I am unsure how I can apply that to this problem.
/****** WiP Script ******/
SELECT
[jerry].[dbo].[purchases_new].*,
CASE WHEN Transaction_Quantity < 0 THEN -- (SELECT Element_ID FROM the_current_row WHERE transaction_quantity > 0)
ELSE NULL AS "Replenishment",
-- (SELECT Transaction_Date FROM [jerry].[dbo].[purchases_new] WHERE Element_ID
-- Not sure how to grab the correct date of the element id from the column before
FROM
[jerry].[dbo].[purchases_new]
Any assistance is appreciated. I have been pulling my hair out on this problem. The comments contain additional information.
NOTE - I have continually tried to update this question as users have requested more information.
Here is one attempt. You will need to modify if with another layer of abstraction for offsets if you need to support transaction increments/decrements > 1. It basically aligns the order of sales with the order of debits and then uses that as join back to the main dataset.
Sql Fiddle
The idea is to put additions and subtractions into two sets, orderd chronologically by set, while also remembering order of each item back into the main list. This way, you can align each subtraction with the nearest addition. This is pretty straightforward when dealing with 1's.
Edit --> Dealing with values > 1.
Computing Transaction_Amount > (+/-)1 adds a little complexity, but still solvable. Now we need to stretch each addition and subtraction transaction set out by the Transaction_Amount, so the dataset is lengthened, however, the original algorithm will still be applied to a now longer dataset. This will allow for the recording of "partial fulfillments". So (12 A 5) would equate to (12 A 1), (12 A 1), (12 A 1), (12 A 1), (12 A 1). Now, when the subtractors are lengthened in similar fashion, (with all rows in the same order as the first of the sequence) the alignment will still work and addition and subtractions can be matched with the nearest neighbor(s).
DECLARE #T TABLE(Element_ID NVARCHAR(50),Element NVARCHAR(50), Transaction_Date DATETIME,Transaction_Quantity INT,Total_Inventory INT)
INSERT #T VALUES
('MO301','Make_Order','1/1/2019',5,1),
('MO302','Make_Order','1/3/2019',1,2),
('SO105','Sale','2/1/2019',-2,1),
('SO106','Sale','2/1/2019',-1,0),
('MO323','Make_Order','2/2/2019',1,1),
('SO107','Sale','2/4/2019',-1,0),
('SO191','Sale','2/5/2019',-1,-1),
('SO123','Sale','2/6/2019',-1,-2),
('SO166','Sale','3/1/2019',-1,-3),
('SO603','Sale','3/2/2019',-1,-4),
('MO400','Make_Order','3/15/2019',1,-3),
('MO459','Make_Order','3/15/2019',1,-2),
('MO460','Make_Order','3/18/2019',1,-1),
('MO491','Make_Order','3/19/2019',1,0)
;WITH Normalized AS
(
SELECT *, RowNumber = ROW_NUMBER() OVER (ORDER BY (SELECT 0)), IsAdd = CASE WHEN Transaction_Quantity>0 THEN 1 ELSE 0 END FROM #T
)
,ReplicateAmount AS
(
SELECT Element_ID, Element, Transaction_Date, Transaction_Quantity=ABS(Transaction_Quantity) ,Total_Inventory, RowNumber, IsAdd
FROM Normalized
UNION ALL
SELECT R.Element_ID, R.Element, R.Transaction_Date, Transaction_Quantity=(R.Transaction_Quantity - 1), R.Total_Inventory, R.RowNumber, R.IsAdd
FROM ReplicateAmount R INNER JOIN Normalized N ON R.RowNumber = N.RowNumber
WHERE ABS(R.Transaction_Quantity) > 1
)
,NormalizedAgain AS
(
SELECT Element_ID, Element, Transaction_Date, Transaction_Quantity=1, Total_Inventory, RowNumber = ROW_NUMBER() OVER (ORDER BY RowNumber), IsAdd FROM ReplicateAmount
)
,Additives AS
(
SELECT *, AddedOrder = ROW_NUMBER() OVER (ORDER BY (SELECT 0)) FROM NormalizedAgain WHERE IsAdd=1
)
,Subtractions AS
(
SELECT Element_ID, Element, Transaction_Date, Transaction_Quantity=-1 , Total_Inventory, RowNumber, SubtractedOrder = ROW_NUMBER() OVER (ORDER BY (SELECT 0))FROM NormalizedAgain WHERE IsAdd=0
)
,WithTies AS
(
SELECT
S.RowNumber,
S.Element_ID,
BoughtFromRowNumber = A.RowNumber,
SoldToID =S.Element_ID,
BoughFromID=A.Element_ID,
S.Element,
S.Transaction_Date,
S.Transaction_Quantity,
S.Total_Inventory
FROM
Additives A
LEFT OUTER JOIN Subtractions S ON A.AddedOrder=S.SubtractedOrder
UNION
SELECT
A.RowNumber,
A.Element_ID,
BoughtFromRowNumber = S.RowNumber,
SoldToID = NULL,
BoughFromID=NULL,
A.Element,
A.Transaction_Date,
A.Transaction_Quantity,
A.Total_Inventory
FROM
Additives A
LEFT OUTER JOIN Subtractions S ON A.AddedOrder=S.SubtractedOrder
)
SELECT
T.RowNumber,
T.Element_ID,
T.Element,
T.Transaction_Date,
T.Transaction_Quantity,
T.Total_Inventory,
T2.SoldToID,
T.BoughFromID
FROM
WithTies T
LEFT OUTER JOIN WithTies T2 ON T2.BoughtFromRowNumber= T.RowNumber
WHERE
NOT T.RowNumber IS NULL
ORDER BY
T.RowNumber

SQL calculating sum and number of distinct values within group

I want to calculate
(1) total sales amount
(2) number of distinct stores per product
in one query, if possible. Suppose we have data:
+-----------+---------+-------+--------+
| store | product | month | amount |
+-----------+---------+-------+--------+
| Anthill | A | 1 | 1 |
| Anthill | A | 2 | 1 |
| Anthill | A | 3 | 1 |
| Beetle | A | 1 | 1 |
| Beetle | A | 3 | 1 |
| Cockroach | A | 1 | 1 |
| Cockroach | A | 2 | 1 |
| Cockroach | A | 3 | 1 |
| Anthill | B | 1 | 1 |
| Beetle | B | 2 | 1 |
| Cockroach | B | 3 | 1 |
+-----------+---------+-------+--------+
I have tried this with no luck:
select
[product]
,[month]
,[amount]
,cnt_distinct_stores = count(distinct(stores))
from dbo.temp
group by
[product]
,[month]
order by 1,2
Would there be possible any combination of GROUP BY clause with window functions like SUM(amount) OVER(partition by [product],[month] ORDER BY [month] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
Try
SELECT product,
SUM(amount),
COUNT(DISTINCT store)
FROM dbo.temp
GROUP BY product

SQL Server query with sums from multiple tables

I have 3 tables that I am trying to report from that are all related but have a different number of records. I want a summary line for each order that shows job description, total price and total cost.
My tables are a follows:
Prices
| Order | Line # | Description | Price |
+-------+--------+--------------+-------+
| 1 | 1 | Line 1 job#1 | 100 |
| 1 | 2 | Line 2 job#1 | 30 |
| 2 | 1 | Line 1 job#2 | 100 |
| 3 | 1 | Line 1 job#3 | 75 |
Cost lines
| Order | Line # | Cost record | Cost |
+-------+--------+-------------+------+
| 1 | 1 | 1 | 80 |
| 1 | 2 | 2 | 80 |
| 1 | 2 | 3 | 50 |
| 2 | 1 | 1 | 75 |
| 3 | 1 | 1 | 50 |
| 3 | 1 | 2 | 50 |
Order Header
| Order | Description | Sales Person |
+-------+-------------+--------------+
| 1 | Order # 1 | 1 |
| 1 | Order #2 | 2 |
| 1 | Order #3 | 1 |
I keep getting way to many associated rows. I've been trying subqueries with sums but I just can't get it to work.
Expected result:
| Order | Description | Price | Cost | Sales Person |
+-------+-------------+-------+------+--------------+
| 1 | Order #1 | 130 | 210 | 1 |
| 2 | Order #2 | 100 | 75 | 2 |
| 3 | Order #3 | 75 | 100 | 1 |
I assume there is a mistake in your sample data and the first column should read 1, 2 and 3 rather than three times 1. At least your desired result makes that seem very plausible.
Join the costs and prices to the orders and then GROUP BY the orders and calculate the sum for the costs and prices.
SELECT o.[Order],
o.[Description],
sum(p.[Price]) [Price],
sum(c.[Cost]) [Cost],
o.[Sales Person]
FROM [Order Header] o
LEFT JOIN [Cost lines] c
ON c.[Order] = o.[Order]
LEFT JOIN [Prices] p
ON p.[Order] = o.[Order]
GROUP BY o.[Order],
o.[Description],
o.[Sales Person];