SQL find nearest date without going over, or return the oldest record - sql

I have a view in SQL Server with prices of items over time. My users will be passing a date variable and I want to return the closest record without going over, or if no such record exists return the oldest record present. For example, with the data below, if the user passes April for item A it will return the March record and for item B it will return the June record.
I've tried a lot of variations with Union All and Order by but keep getting a variety of errors. Is there a way to write this using a Case Statement?
example:
case when min(Month)>Input Date then min(Month)
else max(Month) where Month <= Input Date?
Sincere apologies for attaching sample dataset as an image, I couldn't get it to format right otherwise.
Sample Dataset

You can use SELECT TOP (1) with order by DATE DESC + Item type + date comparison to get the latest. ORDER BY will order records by date, then you get the latest either this month (if exists) or earlier months.

Here's a rough outline of a query (without more of your table it's hard to be exact):
WITH CTE AS
(
SELECT
ITEM,
PRICE,
MIN(ACTUAL_DATE) OVER (PARTITION BY ITEM ORDER BY ITEM) AS MIN_DATE,
MAX(INPUT_DATE<=ACTUAL_DATE) OVER (PARTITION BY ITEM ORDER BY ITEM,ACTUAL_DATE) AS MATCHED_DATE
FROM TABLE
)
SELECT
CTE.ITEM,
CTE.PRICE,
CASE
WHEN
CTE.MATCHED_DATE IS NOT NULL
THEN
CTE.MATCHED_DATE
ELSE
CTE.MIN_DATE
END AS MOSTLY_MATCHED_DATE
FROM CTE
GROUP BY
CTE.ITEM,
CTE.PRICE
The idea is that in a Common Table Expression, you use the PARTITION BY function to identify the key date for each item, record by record, and then you do a test in aggregate to pull either your matched record or your default record.

Related

SQL - Update column based on date comparasion with previous occurrence

I have a huge table;
I want to create a third column based on the time difference between two dates for the same id. If the difference is less than a month, then it's active, if it is between 1-2 months then inactive and anything more than 2 is dormant. The expected outcome is below;( note last entries don't have activity definitions as I don't have previous occurrences.)
My question would be, how to do such operation.
case when date_>=date_add((select max(date_) from schema.table),-30) then 'Active' when date_<date_add((select max(date_) from schema.table),-30) and date_>= date_add((select max(date_) from schema.table),-60) then 'Inactive' when date_<date_add((select max(date_) from schema.table),-60) then 'Dormant3' end as Activity
the code I came up with is not what I need as it only checks for the final entry date in the table. What I need is more akin to a for loop and checking the each row and comparing it to the previous occurrence.
edit:
By partitioning over id and dense ranking them, I reached something that almost works. I just need to compare to the previous element in the dense rank groups.
Create base data first using LEAD()
Then compare than with original row.
SELECT ID, DATE,
CASE
WHEN DATEDIFF(DATE,PREVIOUS_DATE) <=30 THEN 'Active'
DATEDIFF(DATE,PREVIOUS_DATE) between 31 and 60 'Active'
ELSE 'Dormant'
END as Activity
(SELECT ID, DATE, LEAD(DATE) OVER( partition by id ORDER BY DATE) PREVIOUS_DATE FROM MYTABLE) RS

Sort many ids in a table in SQL Server

I have been given a task which I should look on items table and grab first item of 2019 and last item for 2019 and set the active flags on them as active , the query I wrote only I can grab one by one depends on the store, and it takes days to finish if I have no other choice, here is my query in SQL Server:
SELECT *
FROM NODES
WHERE NODE ID = 5562
AND DATE BETWEEN '2019/01/01' AND '2019/12/30'
Basically I need the first and the last item for the year, but the problem is every Node is a specific store which has many record and I have run the query for million of records in many Nodes, is it possible if I for example say OK SQL from the given nodes take first and last item for 2019 and display to me and then update their active flag = 'Y'
Is it possible with a CTE, do I need a CTE at all?
Thank you
If I understood correctly, you could try using a CTE with a windowed function to fetch only the first row from each store after ordering by date in ascending order and the first row from each store after ordering by date in descending order.
For instance :
CREATE TABLE NODES (NodeId int,NodeDate DATETIME2,status NVARCHAR(128))
INSERT INTO NODES(NodeId,NodeDate,Status) VALUES
(1,'2019/01/01','inactive'),
(1,'2019/03/01','inactive'),
(1,'2019/06/01','inactive'),
(1,'2019/09/01','inactive'),
(1,'2019/12/01','inactive'),
(2,'2019/01/01','inactive'),
(2,'2019/03/01','inactive'),
(2,'2019/06/01','inactive'),
(2,'2019/09/01','inactive'),
(2,'2019/12/01','inactive'),
(3,'2019/01/01','inactive'),
(3,'2019/03/01','inactive'),
(3,'2019/06/01','inactive'),
(3,'2019/09/01','inactive'),
(3,'2019/12/01','inactive')
;WITH cte AS
(
SELECT status,
ROW_NUMBER() OVER (PARTITION BY NodeId ORDER BY NodeDate ASC) AS FirstDate,
ROW_NUMBER() OVER (PARTITION BY NodeId ORDER BY NodeDate DESC) AS LastDate
FROM NODES
WHERE NodeDate >= '2019/01/01' AND NodeDate < '2020/01/01'
)
UPDATE CTE SET status = 'active'
WHERE FirstDate = 1 OR LastDate = 1
SELECT * FROM NODES
Try it online
Please do note however that this operation can be non deterministic if multiple rows have the same date.
See also :
Get top 1 row of each group

How to get the second row by date and by id, without group by (not sure about row number)

My dataset is about sales, each line corresponds to an invoice. It is possible to have 2 registers in the same day for the same customer, if he had bought twice in that day.
As you can see in the image below, the blue square shows us that customer 355122 (id_cliente = customer_id) bought twice (275831N and 275826N invoice's id) in the same day (2020-12-19) (penult_data = second-last date). This query is meant to be a support table, to left join the main table and bring those results.
First of all, i've created a row number just over customer_id (blue arrow, aux), so that I could just join with aux = 2 (that should be the second-last register), but in cases that the customer bought twice that day, the second-last invoice is not the second-last date he bought. I need the second-last DATE. He can buy 1,2,3,4,5 times a day, so I cannot assume a correct aux number to filter.
Then, for some reason, I also created an aux2, it's a row number over customer and date, but it really didn't help. I needed something that would repeat the index for the same date, so that index = 2 would be the second-last date.
I cannot use group by because i'm retrieving the salesman id (penult_vend), the store id (penult_empe), and so on from the second-last date
This is the output of part of the query I'm using (as I said, the support table to left join the main table). I'm filtering to this customer's id.
Does somebody knows any function or method to make this work?
I'm using google big query.
Thanks
Assuming the column penult_data has only date information without time of the day, you can find the second to last "date" and then the last "invoice" on that date by using the DENSE_RANK() and ROW_NUMBER() functions:
dense_rank() over(partition by id_cliente
order by penult_data desc) as rnd,
row_number() over(partition by id_cliente, penult_data
order by penult_nf desc) as rnf,
Then, you can use the filtering condition:
where rnd = 2 and rnf = 1

I want NAV price as per (Today date minus 1) date

I have two tables. One is NAV where product daily new price is updated. Second is TDK table where item wise stock is available.
Now I want to get a summery report as per buyer name where all product wise total will come and from table one latest price will come.
I have tried below query...
SELECT dbo.TDK.buyer, dbo.NAV.Product_Name, sum(dbo.TDK.TD_UNITS) as Units, sum(dbo.TDK.TD_AMT) as 'Amount',dbo.NAV.NAValue
FROM dbo.TDK INNER JOIN
dbo.NAV
ON dbo.TDK.Products = dbo.NAV.Product_Name
group by dbo.TDK.buyer, dbo.NAV.Product_Name, dbo.NAV.NAValue
Imnportant: Common columns in both tables...
Table one NAV has column as Products
Table two TDK has column as Product_Name
If I have NAValue 4 records for one product then this query shows 4 lines with same total.
What I need??
I want this query to show only one line with latest NAValue price.
I want display one more line with Units*NAValue (latest) as "Latest Market Value".
Please guide.
What field contains the quote date? I am assuming you have a DATIME field, quoteDate, in dbo.NAV table and my other assumption is that you only store the Date part (i.e. mid-night, time = 00:00:00).
SELECT
t.buyer,
n.Product_Name,
sum(t.TD_UNITS) as Units,
sum(t.TD_AMT) as 'Amount',
n.NAValue
FROM dbo.TDK t
INNER JOIN dbo.NAV n
ON t.Products = n.Product_Name
AND n.quoteDate > getdate()-2
group by t.buyer, n.Product_Name, n.NAValue, n.QuoteDate
GetDate() will give you the current date and time. Subtracting 2 would get it before yesterday but after the day before yesterday.
Also, add n.quoteDate in your select and group by. Even though you don't need it, in case that one day you have a day of bad data with double record in NAV table, one with midnight time and another with 6 PM time.
Your code looks like SQL Server. I think you just want APPLY:
SELECT t.buyer, n.Product_Name, t.TD_UNITS as Units, t.TD_AMT as Amount, n.NAValue
FROM dbo.TDK t CROSS APPLY
(SELECT TOP (1) n.*
FROM dbo.NAV n
WHERE t.Products = n.Product_Name
ORDER BY ?? DESC -- however you define "latest"
) n;

Access: Compare current field value in subquery

I am trying to create a subquery in MS Access where the having clause compares a value on the current record. I created the queries separate, but am having a hard time trying to combine them.
I have the following query, which is a Purchase Order list (POsFullDetail), and should show the first occurrence of the date of a PO given the Stock number (Stockum):
SELECT POsFullDetail.PO, POsFullDetail.OrderDate, POsFullDetail.StockNum,
(SELECT First(POsFullDetail.OrderDate) AS FirstOfOrderDate
FROM POsFullDetail
GROUP BY POsFullDetail.StockNum
HAVING POsFullDetail.StockNum = POsFullDetail.StockNum.Value
ORDER BY First(POsFullDetail.OrderDate)
) AS First_Date
FROM POsFullDetail;
The statement that I am trying to work with is POsFullDetail.StockNum.Value
The way it is set up, it's asking for a value. When I created the subquery separate I entered the stock number directly.
The subquery gives you the first order date per stocknum.
When using it as a subquery, you are no longer interested in the first order date per stocknum, but in the first order date for the stocknum.
SELECT POsFullDetail.PO, POsFullDetail.OrderDate, POsFullDetail.StockNum,
(
SELECT First(SameStockNum.OrderDate) AS FirstOfOrderDate
FROM POsFullDetail AS SameStockNum
WHERE SameStockNum.StockNum = POsFullDetail.StockNum
) AS First_Date
FROM POsFullDetail;
As you see, you must use a table alias, so you can link the table to itself. Though working with the same table you call it one time POsFullDetail and one time SameStockNum which enables you to link by SameStockNum.StockNum = POsFullDetail.StockNum.