Compare top record against Top record -1 - sql-server

I was asked to create a report comparing all clients most recent order and their previous order, and then compare and return only those who placed orders with a higher amount as their next order. (I really hope this makes sense)
The order history table is laid out in such a way as each customer has an Order number that is sequential to that Customer (E.G. If a customer places 5 orders, then their top order number is 5, which should make this easier.) So for a customer with 5 orders, I would want to compare order #'s 4 and 5, and then only return this customer if Order #5 was for a higher Dollar amount.
The Order amount is stored in a different table, but they are linked by a guid reference (ID).
SELECT TOP 1 CO.OrderNumber
,COD.Amount
FROM cust_OrderDetail COD
INNER JOIN dbo.cust_Order CO ON Cod.cust_OrderID = CO.ID
INNER JOIN Customer c ON CO.Customer = c.ID
WHERE COD.Amount > (SELECT COD1.Amount
FROM cust_OrderDetail COD1
INNER JOIN dbo.cust_Order CO1 ON Cod1.cust_OrderID = CO1.ID
WHERE CO1.Ordernumber = (This is where I fall apart)
I hope this makes sense. I fall apart right there at the end. I know how to link in all the other details and everything else that is needed here. It is just this one comparison that kicks my teeth in.

Assuming your query returns Customer and CustomerOrder info correctly
Approach: Get your top 2 records per customer in CTE and then compare the Topmost record with the previous one.
WITH Top2 AS (
SELECT *
FROM
(
SELECT c.ID, CO.OrderNumber ,COD.Amount,
ROW_NUMBER() OVER(PARTITION BY c.ID ORDER BY c.ID, CO.OrderNumber DESC) Rnk
FROM cust_OrderDetail COD
INNER JOIN dbo.cust_Order CO ON Cod.cust_OrderID = CO.ID
INNER JOIN Customer c ON CO.Customer = c.ID
) T WHERE Rnk <= 2)
SELECT * FROM
(SELECT * FROM Top2 Where Rnk = 1) T1
LEFT JOIN (SELECT * FROM Top2 Where Rnk = 2) T2
ON T1.ID = T2.ID
AND T1.Amount > T2.amount

Related

How do I get the most updated data from the database based on the entry date for various entry ID that is multiple rows

I have 3 tables, namely CALCULATED INPUT, RAW MATERIAL INPUT and COMPANY
select * from [LTBU_MKT_RAW_CALULATED_DATA] e
inner join
(select t2.COMPANY_ENTRY_ID, t2.ENTRY_ID from LTBU_MKT_RAW_MAT_INPUT as t2) b on b.ENTRY_ID = e.ENTRY_ID
inner join
(select h.[COMPANY],h.[GUID] from LTBU_MKT_COMPANY as h) g on b.COMPANY_ENTRY_ID = g.GUID
(Select c.COMPANY,Max(t1.ENTRY_DATE) as MaxDate
from [inet].[dbo].[LTBU_MKT_RAW_CALULATED_DATA] t1
inner join
(select t2.COMPANY_ENTRY_ID, t2.ENTRY_ID from LTBU_MKT_RAW_MAT_INPUT as t2) b on b.ENTRY_ID = t1.ENTRY_ID
inner join
(select t3.[COMPANY],t3.[GUID] from LTBU_MKT_COMPANY as t3) c on b.COMPANY_ENTRY_ID = c.GUID
inner join
(SELECT t2.ENTRY_ID,t2.CHEMICAL_NAME,t2.CHEMICAL_QTY_KG,t2.CHMEICAL_CAL_PRICE,t2.CHMEICAL_PROCESS from [inet].[dbo].[LTBU_MKT_RAW_CALULATED_DATA] as t2) a on a.ENTRY_ID = t1.ENTRY_ID
group by c.COMPANY)
But it not able to combine all the information due to the MAX(ENTRY_Date)
Any help or comments is appreciated.
You can use a window function in the order by. Something like...
order by row_number() over (partition by ENTRY_ID order by ENTRY_Date desc)
I’m not sure which ENTRY_ID you want to use since more than one table has it. Be sure you reference it accordingly.

Is there a way to UPDATE TOP (N) with inner join where N is a field of such inner join?

I'm trying to create a script that synchronizes Sales and Inventory tables. For that I wrote an UPDATE on the Inventory table (which has 1 record per item of inventory present) like this:
UPDATE TOP (q.QuantitySold) i
SET i.Converted = 1,
i.CartID = q.CartID,
i.ReservedDate = GETDATE()
FROM Inventory i
INNER JOIN
(
SELECT product.ProductID, sales.CartID, COUNT(sales.ID) AS QuantitySold
FROM Products product
INNER JOIN Sales sales ON sales.ProductID = product.ProductID
WHERE <conditions>
GROUP BY product.ProductID, sales.CartID
) q ON q.ProductID = i.ProductID
WHERE i.Converted = 0 AND i.CartID IS NULL
But it's not working, error says q.QuantitySold couldn't be bound.
Is there a way to update N records of inventory (equal to the quantity sold) without using a cursor? I refuse to give up like that.
Note: this is a simplified version of the actual query.
You could use ROW_NUMBER to enumerate the inventory items that you need to update.
WITH cteProducts AS(
SELECT product.ProductID, sales.CartID, COUNT(sales.ID) AS QuantitySold
FROM Products product
INNER JOIN Sales sales ON sales.ProductID = product.ProductID
WHERE <conditions>
GROUP BY product.ProductID, sales.CartID
),
cteInventory AS(
SELECT *,
ROW_NUMBER() OVER( PARTITION BY ProductID ORDER BY (SELECT NULL)) AS rn /*Change the ORDER BY for an actual column if needed, probably for FIFO*/
FROM Inventory
WHERE i.Converted = 0
AND i.CartID IS NULL
)
UPDATE i
SET i.Converted = 1,
i.CartID = q.CartID,
i.ReservedDate = GETDATE()
FROM cteInventory i
INNER JOIN cteProducts q ON q.ProductID = i.ProductID
WHERE i.rn <= q.QuantitySold;

CASE Statement with condition

I think this will be easier to show an example first and then explain:
SELECT P.ID,
(CASE WHEN PC.NewCostPrice IS NULL
THEN P.Cost ELSE MAX(PC.Date) PC.NewCostPrice
END)
FROM price AS P
LEFT OUTER JOIN priceChange as PC
ON P.ID = PC.ID
So in the example, if the NewCostPrice IS NULL, meaning there wasn't a price change, then I want the normal cost (P.Cost). However, if there was a price change, I want the most recent (MAX(Date)) price change. I am not sure how to incorporate that into the CASE statement.
I feel like it can be done with a subquery and having clause but that didn't really work out when I tried. Any suggestions?
Thanks!
There are 2 approaches you might consider - I would test both to see which performs better for your situation.
Use ROW_NUMBER() in subquery to find most recent price change of all price changes, then join that to prices to get correct price.
Use correlated subquery (many ways of this, either in SELECT as in other answer or with OUTER APPLY) to get only most recent price change for each row of prices
If your price table is very large and you are getting a large number of prices at once, method #1 will likely be better so the correlated subquery doesn't run for every single row of the result set.
If your final query pulls back a relatively small number of records instead of huge result sets for your server, then the correlated subquery could be better for you.
1. The ROW_NUMBER() approach
SELECT
P.ID,
COALESCE(PC.NewCostPrice, P.Cost) AS LatestPrice
FROM Price AS P
LEFT OUTER JOIN (
SELECT
ID,
ROW_NUMBER() OVER (PARTITION BY ID
ORDER BY [Date] DESC) AS RowId,
NewCostPrice
FROM PriceChange
) PC
ON P.ID = PC.ID
AND PC.RowId = 1 -- Only most recent
2a. Correlated subquery (SELECT)
SELECT
P.ID,
COALESCE((
SELECT TOP 1
NewCostPrice
FROM PriceChange PC
WHERE PC.ID = P.ID
ORDER BY PC.[Date] DESC
), P.Cost) AS LatestPrice
FROM Price AS P
2b. Correlated subquery with OUTER APPLY
SELECT
P.ID,
COALESCE(PC.NewCostPrice, P.Cost) AS LatestPrice
FROM Price AS P
OUTER APPLY (
SELECT TOP 1
NewCostPrice
FROM PriceChange PC
WHERE PC.ID = P.ID
ORDER BY PC.[Date] DESC
) PC
Whether you use 2a or 2b is more likely a preference in how you want to maintain the query going forward.
Easy way
SELECT distinct P.ID,
ISNULL((SELECT TOP 1 PC1.NewCostPrice FROM priceChange as PC1 WHERE PC1.ID = p.id ORDER BY PC1.Date DESC), p.cost)
FROM price AS P
Here I assume PC.ID is not a primary key, or it makes no sense to join with ID while there could be different price on the same item.
From your query I assume you just want to fetch the latest NewCostPrice sorted by Date, by joining priceChange
SELECT
P.ID,
CASE
WHEN PC.NewCostPrice IS NULL THEN P.Cost
ELSE PC.NewCostPrice
END AS NewPrice
FROM
price AS P
LEFT JOIN
(SELECT *, RANK() OVER (PARTITION BY ID ORDER BY [Date] DESC) as rk FROM priceChange) PC ON P.ID = PC.ID AND PC.rk = 1
SELECT P.ID
,(CASE
WHEN PC.NewCostPrice IS NULL
THEN P.Cost
ELSE (SELECT TOP 1 PC1.NewCostPrice
FROM priceChange PC1
WHERE PC1.ID = PC.ID
GROUP BY PC1.NewCostPrice, PC1.Date
ORDER BY PC1.Date DESC
)
END
)
FROM price AS P
LEFT OUTER JOIN priceChange as PC
ON P.ID = PC.ID

SQL Join on 3 tables

Here is my query:
select custnmbr,custname,slprsnid,cdatetime,cdur,cnumber,cext,
finalcalledpartynumber,sono,invno,ordamt,invamt,adduser
from table1 calls left join table2 cust
on (calls.number = cust.phone1 or calls.cext = cust.phone1)
left outer join table3 sales on (cust.custnmbr = sales.custno
and sales.adddate = #date)
where (cnumber = #phone or cext = #phone) and cdatetime >= #date
Here is what I am trying to do:
Get all the calls from table 1, and get the customer from table 2. Then get all the sales from table 3 and the customer from table 2.
What I am getting is all the calls, the customer, and then if there is an order for that customer I get that as well. What I want is all the orders as well.
Just looking for some pointers on joining 3 tables.
You have to think about how you're going to handle the calls and sales results since there does not appear to be a correlation between calls and sales. So, a customer with 3 calls and 4 sales will produce 12 rows in the result set. This is where we could help you better if you provide more specifics.
The concept is that if calls is a necessary requirement, then you could do something like...
SELECT ca.CallId, ca.TimeStarted, ...,
c.CustomerId, c.FirstName, ...,
s.SaleDate, s.SaleAmount, ...
FROM calls AS ca
INNER JOIN customers AS c ON ca.CustomerId = c.CustomerId
OUTER APPLY ( --or CROSS APPLY, depending on your needs
SELECT s.SaleDate, s.SaleAmount, ...
FROM sales AS s
WHERE s.CustomerId = c.CustomerId
AND s.SaleDate = ca.CallDate --It would help if this relationship existed
)
Generally I would start with the table you want ALL the data from, the one that you are going to form the basis of your navigation to the other entities.:
SELECT
C.*,
Ca.*,
S.*
FROM Sales S
LEFT JOIN Customer C
ON (S.CustomerId = C.CustomerId) -- your condition
LEFT JOIN Calls Ca
ON (C.CustomerId = Ca.CustomerId) -- your condition

SQL Server 2005 Syntax Help - "Select Info based upon Max Value of Sub Query"

The objective is below the list of tables.
Tables:
Table: Job
JobID
CustomerID
Value
Year
Table: Customer
CustomerID
CustName
Table: Invoice
SaleAmount
CustomerID
The Objective
Part 1: (easy) I need to select all invoice records and sort by Customer (To place nice w/ Crystal Reports)
Select * from Invoice as A inner join Customer as B on A.CustomerID = B.CustomerID
Part 2: (hard) Now, we need to add two fields:
JobID associated with that customer's job that has the Maximum Value (from 2008)
Value associated with that job
Pseudo Code
Select * from
Invoice as A
inner join Customer as B on A.CustomerID = B.CustomerID
inner join
(select JobID, Value from Jobs where Job:JobID has the highest value out of all of THIS customer's jobs from 2008)
General Thoughts
This is fairly easy to do If I am only dealing with one specific customer:
select max(JobId), max(Value) as MaxJobID from Jobs where Value = (select max(Value) from Jobs where CustomerID = #SpecificCustID and Year = '2008') and CustomerID = SpecificCustID and CustomerID = '2008'
This subquery determines the max Value for this customer in 2008, and then its a matter of choosing a single job (can't have dupes) out of potential multiple jobs from 2008 for that customer that have the same value.
The Difficulty
What happens when we don't have a specific customer ID to compare against? If my goal is to select ALL invoice records and sort by customer, then this subquery needs access to which customer it is currently dealing with. I suppose this can "sort of" be done through the ON clause of the JOIN, but that doesn't really seem to work because the sub-sub query has no access to that.
I'm clearly over my head. Any thoughts?
How about using a CTE. Obviously, I can't test, but here is the idea. You need to replace col1, col2, ..., coln with the stuff you want to select.
Inv( col1, col2, ... coln)
AS
(
SELECT col1, col2, ... coln,
ROW_NUMBER() OVER (PARTITION BY A.CustomerID
ORDER BY A.Value DESC) AS [RowNumber]
FROM Invoice A INNER JOIN Customer B ON A.CustomerID = B.CustomerID
WHERE A.CustomerID = #CustomerID
AND A.Year = #Year
)
SELECT * FROM Inv WHERE RowNumber = 1
If you don't have a CustomerID, this will return the top value for each customer (that will hurt on performance tho).
The row_number() function can give you what you need:
Select A.*, B.*, C.JobID, C.Value
from
Invoice as A
inner join Customer as B on A.CustomerID = B.CustomerID
inner join (
select JobID, Value, CustomerID,
ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY Value DESC) AS Ordinal
from Jobs
WHERE Year = 2008
) AS C ON (A.CustomerID = C.customerID AND C.Ordinal = 1)
The ROW_NUMBER() function in this query will order by value in descending order and the PARTITION BY clause will do this separately for each different value of CustomerID. This means that the highest Value for each customer will always be 1, so we can join to that value.
The over function is an awesome, but often neglected function. You can use it in a subquery to pull back your valid jobs, like so:
select
a.*
from
invoice a
inner join customer b on
a.customerid = b.customerid
inner join (select customerid, max(jobid) as jobid, maxVal from
(select customerid,
jobid,
value,
max(value) over (partition by customerid) as maxVal
from jobs
where Year = '2008') s
where s.value = s.maxVal
group by customerid, maxVal) c on
b.customerid = c.customerid
and a.jobid = c.jobid
Essentially, that first inner query looks like this:
select
customerid,
jobid,
value,
max(value) over (partition by customerid) as maxVal
from jobs
where Year = '2008'
You'll see that this pulls back all of the jobs, but with that additional column which lets you know what the maximum value is for each customer. With the next subquery, we filter out any rows that have value and maxVal equal. Additionally, it finds the max JobID based on customerid and maxVal, because we need to pull back one and only one JobID (as per the requirements).
Now, you have a complete listing of CustomerID and JobID that meet the conditions of having the highest JobID that contains the maximum Value for that CustomerID in a given year. All that's left is to join it to Invoice and Customer, and you're good to go.
Just to be complete with the non row_number solution for those < MSSQL 2005. Personanly, I find it easier to follow myslef...but that could be biased considering how much time I spend in MSSQL 2000 vs 2005+.
SELECT *
FROM Invoice as A
INNER JOIN Customer as B ON
A.CustomerID = B.CustomerID
INNER JOIN (
SELECT
CustomerId,
--MAX in case dupe Values.
==If UC on CustomerId, Value (or CustomerId, Year, Value) then not needed
MAX(JobId) as JobId
FROM Jobs
JOIN (
SELECT
CustomerId,
MAX(Value) as MaxValue
FROM Jobs
WHERE Year = 2008
GROUP BY
CustomerId
) as MaxValue ON
Jobs.CustomerId = MaxValue.CustomerId
AND Jobs.Value = MaxValue.MaxValue
WHERE Year = 2008
GROUP BY
CustomerId
) as C ON
B.CustomerID = C.CustomerID

Resources