Determine closest date to another date value teradata - sql

My dataset looks like this. For every combination of customerid,orderid and ship date, i would like to retrieve 1 process date that is less than or equal to the ship date. If the process date is greater than the ship date and no lower process date exist, then use the ship date as the process date
+-------------+----------+------------+--------------+--+
| Customer ID | Order ID | Ship Date | Process Date | |
+-------------+----------+------------+--------------+--+
| 1000 | 100 | 9/17/2020 | 9/17/2020 | |
| 1000 | 100 | 9/17/2020 | 10/16/2020 | |
| 1000 | 100 | 9/17/2020 | 9/16/2020 | |
| 2000 | 200 | 8/15/2020 | 8/13/2020 | |
| 2000 | 300 | 10/14/2020 | 10/13/2020 | |
| 3000 | 400 | 3/4/2020 | 4/2/2020 | |
| 3000 | 400 | 3/4/2020 | 3/3/2020 | |
| 3000 | 400 | 3/4/2020 | 3/5/2020 | |
| 4000 | 500 | 5/1/2020 | 5/3/2020 | |
| 5000 | 600 | 6/1/2020 | 7/1/2020 | |
| 5000 | 600 | 6/1/2020 | 7/2/2020
| 6000 | 700 | 7/14/2020 | 7/13/2020 | |
| 6000 | 700 | 7/14/2020 | 6/10/2020 | |
+-------------+----------+------------+--------------+--+ | |
+-------------+----------+------------+--------------+--+
Desired Output
+-------------+----------+------------+--------------+--+
| Customer ID | Order ID | Ship Date | Process Date | |
+-------------+----------+------------+--------------+--+
| 1000 | 100 | 9/17/2020 | 9/17/2020 | |
| 2000 | 200 | 8/15/2020 | 8/13/2020 | |
| 2000 | 300 | 10/14/2020 | 10/13/2020 | |
| 3000 | 400 | 3/4/2020 | 3/3/2020 | |
| 4000 | 500 | 5/1/2020 | 5/1/2020 | |
| 5000 | 600 | 6/1/2020 | 6/1/2020 | |
| 6000 | 700 | 7/14/2020 | 7/13/2020 | |
+-------------+----------+------------+--------------+--+
I tried using ROWNUM and date difference, but I'm stuck after getting the row number in ascending order.Not sure how to proceed ahead.

"If the process date is greater than the ship date and no lower process date exist, then use the ship date as the process date."
Do a GROUP BY. You can use MAX() to return the latest ProcessDate <= ShipDate. If no such ProcessDate exists, return ShipDate.
select CustomerID, orderID, ShipDate,
coalesce(MAX(case when ProcessDate <= ShipDate then ProcessDate end), ShipDate)
from tablename
group by CustomerID, orderID, ShipDate

I think you want filtering and row_number():
select t.*
from (select t.*,
row_number() over (partition by customer_id, order_id, ship_date order by process_date desc) as seqnum
from t
where process_date <= ship_date
) t
where seqnum = 1;
I'm not sure if customer_id and ship_date are really needed in the partition by clause. order_id seems sufficient.

This should return the expected result:
select CustomerID, orderID, ShipDate,
-- If the process date is greater than the ship date and no lower
-- process date exist, then use the ship date as the process date
least(ProcessDate, ShipDate)
from tablename
qualify
-- retrieve 1 process date that is less than or equal to the ship date
row_number()
over (partition by CustomerID, orderI
order by case when ProcessDate <= ShipDate then ProcessDate end desc nulls last) = 1

Related

how to do dynamic sum and substraction [duplicate]

I have a table in an Oracle db that has the following fields of interest: Location, Product, Date, Amount. I would like to write a query that would get a running total of amount by Location, Product, and Date. I put an example table below of what I would like the results to be.
I can get a running total but I can't get it to reset when I reach a new Location/Product. This is the code I have thus far, any help would be much appreciated, I have a feeling this is a simple fix.
select a.*, sum(Amount) over (order by Location, Product, Date) as Running_Amt
from Example_Table a
+----------+---------+-----------+------------+------------+
| Location | Product | Date | Amount |Running_Amt |
+----------+---------+-----------+------------+------------+
| A | aa | 1/1/2013 | 100 | 100 |
| A | aa | 1/5/2013 | -50 | 50 |
| A | aa | 5/1/2013 | 100 | 150 |
| A | aa | 8/1/2013 | 100 | 250 |
| A | bb | 1/1/2013 | 500 | 500 |
| A | bb | 1/5/2013 | -100 | 400 |
| A | bb | 5/1/2013 | -100 | 300 |
| A | bb | 8/1/2013 | 250 | 550 |
| C | aa | 3/1/2013 | 550 | 550 |
| C | aa | 5/5/2013 | -50 | 600 |
| C | dd | 10/3/2013 | 999 | 999 |
| C | dd | 12/2/2013 | 1 | 1000 |
+----------+---------+-----------+------------+------------+
Ah, I think I have figured it out.
select a.*, sum(Amount) over (partition by Location, Product order by Date) as Running_Amt
from Example_Table a
from Advanced SQL Functions in Oracle 10g book, it has this example.
SELECT dte "Date", location, receipts,
SUM(receipts) OVER(ORDER BY dte
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) "Running total"
FROM store
WHERE dte < '10-Jan-2006'
ORDER BY dte, location
I could type out all the answer or send you to where I learned it. :)
Check this out, it explains exactly what you are trying to do.
http://www.codeproject.com/Articles/300785/Calculating-simple-running-totals-in-SQL-Server

How to get Max date and sum of its rows SQL

I have following table,
+------+-------------+----------+---------+
| id | date | amount | amount2 |
+------+-------------+----------+---------+
| | | | 500 |
| 1 | 1/1/2020 | 1000 | |
+------+-------------+----------+---------+
| | | | 100 |
| 1 | 1/3/2020 | 1558 | |
+------+-------------+----------+---------+
| | | | 200 |
| 1 | 1/3/2020 | 126 | |
+------+-------------+----------+---------+
| | | | 500 |
| 2 | 2/5/2020 | 4921 | |
+------+-------------+----------+---------+
| | | | 100 |
| 2 | 2/5/2020 | 15 | |
+------+-------------+----------+---------+
| | | | 140 |
| 2 | 1/1/2020 | 5951 | |
+------+-------------+----------+---------+
| | | | 10 |
| 2 | 1/2/2020 | 1588 | |
+------+-------------+----------+---------+
| | | | 56 |
| 2 | 1/3/2020 | 1568 | |
+------+-------------+----------+---------+
| | | | 45 |
| 2 | 1/4/2020 | 12558 | |
+------+-------------+----------+---------+
I need to get each Id's max date and its amount and amount2 summations, how can I do this. according to above data, I need following output.
+------+-------------+----------+---------+
| | | | 300 |
| 1 | 1/3/2020 | 1684 | |
+------+-------------+----------+---------+
| | | | 600 |
| 2 | 2/5/2020 | 4936 | |
+------+-------------+----------+---------+
How can I do this.
Aggregate and use MAX OVER to get the IDs' maximum dates:
select id, [date], sum_amount, sum_amount2
from
(
select
id, [date], sum(amount) as sum_amount, sum(amount2) as sum_amount2,
max([date]) over (partition by id) as max_date_for_id
from mytable group by id, [date]
) aggregated
where [date] = max_date_for_id
order by id;
first is to use dense_rank() to find the row with latest date
dense_rank () over (partition by id order by [date] desc)
after that, just simply group by with sum() on the amount
select id, [date], sum(amount), sum(amount2)
from
(
select *,
dr = dense_rank () over (partition by id order by [date] desc)
from your_table
) t
where dr = 1
group by id, [date]

SQL Server 2014 - How to calculate percentage based on last NOT NULL value in a column?

I have the following table dates, items and sales as show below :
table Dates :
+---------+------------+------------+
| Date_ID | StartDates | EndDates |
+---------+------------+------------+
| 1 | 2016-07-01 | 2016-07-05 |
| 2 | 2016-07-06 | 2016-07-12 |
+---------+------------+------------+
table items :
+--------+----------+---------+
| ITM_ID | ITM_Name | ITM_Qty |
+--------+----------+---------+
| A0001 | Item A | 30 |
| B0001 | Item B | 50 |
+--------+----------+---------+
table sales :
+----------+------------+------------+-----------+
| Sales_ID | Sales_Date | Sales_Item | Sales_Qty |
+----------+------------+------------+-----------+
| S0001 | 2016-07-02 | A | 5 |
| S0002 | 2016-07-04 | A | 15 |
| S0003 | 2016-07-08 | B | 20 |
| S0004 | 2016-07-12 | A | 10 |
+----------+------------+------------+-----------+
I would like to calculate a percentage (act like a ratio of sales on current period compared to the previous period) and the available amount of item after each sales.
My expected output would be like this :
+------------+------------+---------+----------+----------+-----------+
| StartDates | EndDates | Item_ID | Sold_Qty | Percents | Available |
+------------+------------+---------+----------+----------+-----------+
| 2016-07-01 | 2016-07-05 | A0001 | 20 | 100 | 10 |
| 2016-07-01 | 2016-07-05 | B0001 | 0 | 0 | 50 |
| 2016-07-06 | 2016-07-12 | A0001 | 10 | 50 | 0 |
| 2016-07-06 | 2016-07-12 | B0001 | 20 | 100 | 30 |
+------------+------------+---------+----------+----------+-----------+
I hope the expected output will be possible but I have currently not get a working query yet.
As the table above, the column percents is a percentage sales of current period compared to the last period, i.e. on item A0001 first period has sold_qty is 20 and the second period is 10, therefore the percentage of second period is (10/20) * 100 = 50.
EDIT : for the case of item B0001, the sold_qty of the first period is 0, therefore the percentage count should not consider the value on the first period.
Try Below. For Calculating Percents i have used case statement that you can simplify it based on your requirement.
SELECT *,
CASE
WHEN SOLD_QTY = 0 THEN 0
WHEN LAG(SOLD_QTY)
OVER(
PARTITION BY ITM_ID
ORDER BY ITM_ID) = 0
OR LAG(SOLD_QTY)
OVER(
PARTITION BY ITM_ID
ORDER BY ITM_ID) IS NULL THEN 100
ELSE CONVERT(FLOAT, SOLD_QTY) / NULLIF(LAG(SOLD_QTY)
OVER(
PARTITION BY ITM_ID
ORDER BY ITM_ID), 0) * 100
END PERCENTS
FROM (SELECT T1.STARTDATES,
T1.ENDDATES,
T2.ITM_ID,
ISNULL(SUM(T3.SALES_QTY), 0) SOLD_QTY,
( T2.[ITM_QTY] ) - ISNULL(SUM(T3.SALES_QTY), 0)AS AVIL
FROM #TABLE1 T1
CROSS JOIN #TABLE2 T2
LEFT JOIN #TABLE3 T3
ON T3.[SALES_ITEM] = LEFT(T2.[ITM_ID], 1)
AND T3.SALES_DATE BETWEEN T1.STARTDATES AND T1.ENDDATES
GROUP BY T1.STARTDATES,
T1.ENDDATES,
T2.ITM_ID,
T2.[ITM_QTY])A

Running Total by Group SQL (Oracle)

I have a table in an Oracle db that has the following fields of interest: Location, Product, Date, Amount. I would like to write a query that would get a running total of amount by Location, Product, and Date. I put an example table below of what I would like the results to be.
I can get a running total but I can't get it to reset when I reach a new Location/Product. This is the code I have thus far, any help would be much appreciated, I have a feeling this is a simple fix.
select a.*, sum(Amount) over (order by Location, Product, Date) as Running_Amt
from Example_Table a
+----------+---------+-----------+------------+------------+
| Location | Product | Date | Amount |Running_Amt |
+----------+---------+-----------+------------+------------+
| A | aa | 1/1/2013 | 100 | 100 |
| A | aa | 1/5/2013 | -50 | 50 |
| A | aa | 5/1/2013 | 100 | 150 |
| A | aa | 8/1/2013 | 100 | 250 |
| A | bb | 1/1/2013 | 500 | 500 |
| A | bb | 1/5/2013 | -100 | 400 |
| A | bb | 5/1/2013 | -100 | 300 |
| A | bb | 8/1/2013 | 250 | 550 |
| C | aa | 3/1/2013 | 550 | 550 |
| C | aa | 5/5/2013 | -50 | 600 |
| C | dd | 10/3/2013 | 999 | 999 |
| C | dd | 12/2/2013 | 1 | 1000 |
+----------+---------+-----------+------------+------------+
Ah, I think I have figured it out.
select a.*, sum(Amount) over (partition by Location, Product order by Date) as Running_Amt
from Example_Table a
from Advanced SQL Functions in Oracle 10g book, it has this example.
SELECT dte "Date", location, receipts,
SUM(receipts) OVER(ORDER BY dte
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) "Running total"
FROM store
WHERE dte < '10-Jan-2006'
ORDER BY dte, location
I could type out all the answer or send you to where I learned it. :)
Check this out, it explains exactly what you are trying to do.
http://www.codeproject.com/Articles/300785/Calculating-simple-running-totals-in-SQL-Server

Query to find the first date after a specific grouped sum value

I have an article table that holds the current stock for each article. I need to know the last date when new stock has arrived, after running out of stock for that specific article.
The table looks like this.
+-----------+-----------------+-------+
| ArticleID | StockDate | Stock |
+-----------+-----------------+-------+
| 1 | 1/1/2012 10:15 | 100 |
| 1 | 2/1/2012 13:39 | -50 |
| 1 | 2/1/2012 15:17 | -50 |
| 1 | 4/1/2012 08:05 | 100 |
| 2 | 5/1/2012 09:48 | 50 |
| 1 | 6/1/2012 14:21 | -25 |
| 1 | 7/1/2012 16:01 | 10 |
| 2 | 8/1/2012 13:42 | -10 |
| 1 | 9/1/2012 09:56 | -85 |
| 1 | 13/1/2012 08:12 | 100 |
| 1 | 13/1/2012 10:50 | -15 |
+-----------+-----------------+-------+
The output should look like this.
+-----------+-----------------+
| ArticleID | StockDate |
+-----------+-----------------+
| 2 | 5/1/2012 09:48 |
| 1 | 13/1/2012 08:12 |
+-----------+-----------------+
How did i get this output? ArticleID 1 had a 100 in stock but reached 0 for the first time on 2/1/2012 15:17. Then new stock arrived and it hit 0 again at 9/1/2012 09:56. So the result should shows the first date after the last empty stock grouped by ArticleID. ArticleID 2 didn't had a 0 point, so the first stock date is shown.
I need a result set that can be joined with other queries. So a Stored Procedure does not work for me.
select ArticleID,stockdate from
(
select t.ArticleID, t.stockdate, ROW_NUMBER() Over (partition by t.articleid order by v.articleid desc, stockdate) rn
from yourtable t
left join
(
select ArticleID, MAX(stockdate) as msd from yourtable t1
cross apply (select sum(stock) as stockrt from yourtable where stockdate<=t1.stockdate and ArticleID=t1.ArticleID) rt
where stockrt = 0
group by articleid
) v
on t.ArticleID = v.ArticleID
and t.stockdate>v.msd
) v
where rn=1