Joining 2 tables by latest date - sql

2 tables
select * from general_data
ID | currecny | DATE_ACT
1 USD 01/12/2015
2 USD 02/12/2015
3 GBP 01/12/2015
4 GBP 03/12/2015
5 USD 06/12/2015
select * from currency_data
currecny | RATE_DATE | Rate
USD 04/12/2015 3.51
USD 02/12/2015 3.46
GBP 01/12/2015 4.62
USD 01/12/2015 3.50
I want to match ID to it's valid currency.
Currency is matched by currency name, and 'date'.
--if there is no such date in 'currency_data' table, it should be matched to previous date.
So desired output:
ID | rate |
1 3.50
2 3.46
3 4.62
4 4.62
5 3.51
My attempt:
SELECT gd.ID,
(SELECT rate
FROM currency_data cd
WHERE cd.currecny = gd.currecny
AND gd.date_act >= cd.rate_date
AND ROWNUM = 1
order by cd.rate_date) rate
FROM general_data gd
order by 1
Getting syntax error on order by, but I need order to get the latest.

This is a pain to do in Oracle. In Oracle 12g, you can use fetch first 1 row only:
SELECT gd.ID,
(SELECT rate
FROM currency_data cd
WHERE cd.currecny = gd.currecny AND
gd.date_act >= cd.rate_date
ORDER BY cd.rate_date
FETCH FIRST 1 ROW ONLY
) rate
FROM general_data gd
ORDER BY 1;
But that only works with the most recent release. Unfortunately, using two subqueries to use rownum is problematic, because Oracle's scoping rules do not allow nested correlations.
But, there is a solution:
SELECT gd.ID,
(SELECT MAX(rate) KEEP (DENSE_RANK FIRST ORDER BY cd.rate_date)
FROM currency_data cd
WHERE cd.currecny = gd.currecny AND
gd.date_act >= cd.rate_date
) rate
FROM general_data gd
ORDER BY 1;
KEEP to the rescue.

SELECT gd.ID, cd.Rate
FROM general_data gd
LEFT OUTER JOIN currency_data cd
ON cd.currency = gd.currency
AND cd.rate_date =
(
SELECT max(rate_date)
FROM currency_data cd
WHERE cd.currency = gd.currency
AND cd.rate_date <= gd.date_act
)
ORDER BY 1
Try it on SQLFIDDLE

Related

Getting latest price of different products from control table

I have a control table, where Prices with Item number are tracked date wise.
id ItemNo Price Date
---------------------------
1 a001 100 1/1/2003
2 a001 105 1/2/2003
3 a001 110 1/3/2003
4 b100 50 1/1/2003
5 b100 55 1/2/2003
6 b100 60 1/3/2003
7 c501 35 1/1/2003
8 c501 38 1/2/2003
9 c501 42 1/3/2003
10 a001 95 1/1/2004
This is the query I am running.
SELECT pr.*
FROM prices pr
INNER JOIN
(
SELECT ItemNo, max(date) max_date
FROM prices
GROUP BY ItemNo
) p ON pr.ItemNo = p.ItemNo AND
pr.date = p.max_date
order by ItemNo ASC
I am getting below values
id ItemNo Price Date
------------------------------
10 a001 95 2004-01-01
6 b100 60 2003-01-03
9 c501 42 2003-01-03
Question is, is my query right or wrong? though I am getting my desired result.
Your query does what you want, and is a valid approach to solve your problem.
An alternative option would be to use a correlated subquery for filtering:
select p.*
from prices p
where p.date = (select max(p1.date) from prices where p1.itemno = p.itemno)
The upside of this query is that it can take advantage of an index on (itemno, date).
You can also use window functions:
select *
from (
select p.*, rank() over(partition by itemno order by date desc) rn
from prices p
) p
where rn = 1
I would recommend benchmarking the three options against your real data to assess which one performs better.

How to get latest records based on two columns of max

I have a table called Inventory with the below columns
item warehouse date sequence number value
111 100 2019-09-25 12:29:41.000 1 10
111 100 2019-09-26 12:29:41.000 1 20
222 200 2019-09-21 16:07:10.000 1 5
222 200 2019-09-21 16:07:10.000 2 10
333 300 2020-01-19 12:05:23.000 1 4
333 300 2020-01-20 12:05:23.000 1 5
Expected Output:
item warehouse date sequence number value
111 100 2019-09-26 12:29:41.000 1 20
222 200 2019-09-21 16:07:10.000 2 10
333 300 2020-01-20 12:05:23.000 1 5
Based on item and warehouse, i need to pick latest date and latest sequence number of value.
I tried with below code
select item,warehouse,sequencenumber,sum(value),max(date) as date1
from Inventory t1
where
t1.date IN (select max(date) from Inventory t2
where t1.warehouse=t2.warehouse
and t1.item = t2.item
group by t2.item,t2.warehouse)
group by t1.item,t1.warehouse,t1.sequencenumber
Its working for latest date but not for latest sequence number.
Can you please suggest how to write a query to get my expected output.
You can use row_number() for this:
select *
from (
select
t.*,
row_number() over(
partition by item, warehouse
order by date desc, sequence_number desc, value desc
) rn
from mytable t
) t
where rn = 1

Tracing original Value through Iteration SQL

Suppose there is a data collection system that, whenever a record is altered, it is then saved as a new record with a prefix (say M-[most recent number in que and is unique]).
Suppose I am given the following data set:
Customer | Original_Val
1 1020
2 1011
3 1001
I need to find the most recent value for each customer given the following table:
Customer | Most_Recent_Val | Pretained_To_Val | date
1 M-2000 M-1050 20170225
1 M-1050 M-1035 20170205
1 M-1035 1020 20170131
1 1020 NULL 20170101
2 M-1031 1011 20170105
2 1011 NULL 20161231
3 1001 NULL 20150101
My desired output would be:
Customer | Original_Val | Most_Recent_Val | date
1 1020 M-2000 20170225
2 1011 M-1031 20170105
3 1001 1001 20150101
For customer 1, there are 4 levels i.e (M-2000 <- M-1050 <- M-1035 <- 1020) Note that there would be no more than 10 levels of depth for each customer.
Much Appreciated! Thanks in advance.
Find the min and max of each customer and then join it together. Something like this:
Select
[min].Customer
,[min].Most_Recent_Val as Original_Val
,[max].Most_Recent_Val as Most_Recent_Val
,[max].date
From
(
Select
Customer
,Most_Recent_Val
,date
From
table t1
inner join (
Select
Customer
,MIN(date) as MIN_Date
From
table
Group By
Customer
) t2 ON t2.Customer = t1.Customer
and t2.MIN_Date = t1.Date
) [min]
inner join (
Select
Customer
,Most_Recent_Val
,date
From
table t1
inner join (
Select
Customer
,MAX(date) as MAX_Date
From
table
Group By
Customer
) t2 ON t2.Customer = t1.Customer
and t2.MAX_Date = t1.Date
) [max] ON [max].Customer = [min].Customer

How to Retrieve Maximum Value of Each Group? - SQL

There is a table tbl_products that contains data as shown below:
Id Name
----------
1 P1
2 P2
3 P3
4 P4
5 P5
6 P6
And another table tbl_inputs that contains data as shown below:
Id Product_Id Price Register_Date
----------------------------------------
1 1 10 2010-01-01
2 1 20 2010-10-11
3 1 30 2011-01-01
4 2 100 2010-01-01
5 2 200 2009-01-01
6 3 500 2011-01-01
7 3 270 2010-10-15
8 4 80 2010-01-01
9 4 50 2010-02-02
10 4 92 2011-01-01
I want to select all products(id, name, price, register_date) with maximum date in each group.
For Example:
Id Name Price Register_Date
----------------------------------------
3 P1 30 2011-01-01
4 P2 100 2010-01-01
6 P3 500 2011-01-01
10 P4 92 2011-01-01
select
id
,name
,code
,price
from tbl_products tp
cross apply (
select top 1 price
from tbl_inputs ti
where ti.product_id = tp.id
order by register_date desc
) tii
Although is not the optimum way you can do it like:
;with gb as (
select
distinct
product_id
,max(register_date) As max_register_date
from tbl_inputs
group by product_id
)
select
id
,product_id
,price
,register_date
from tbl_inputs ti
join gb
on ti.product_id=gb.product_id
and ti.register_date = gb.max_register_date
But as I said earlier .. this is not the way to go in this case.
;with cte as
(
select t1.id, t1.name, t1.code, t2.price, t2.register_date,
row_number() over (partition by product_id order by register_date desc) rn
from tbl_products t1
join tbl_inputs t2
on t1.id = t2.product_id
)
select id, name, code, price, register_date
from cte
where rn = 1
Something like this..
select id, product_id, price, max(register_date)
from tbl_inputs
group by id, product_id, price
you can use the max function and the group by clause. if you only need results from the table tbl_inputs you even don't need a join
select product_id, max(register_date), price
from tbl_inputs
group by product_id, price
if you need field from the tbl_prducts you have to use a join.
select p.name, p. code, i.id, i.price, max(i.register_date)
from tbl_products p join tbl_inputs i on p.id=i.product_id
grooup by p.name, p. code, i.id, i.price
Try this:
SELECT id, product_id, price, register_date
FROM tbl_inputs T1 INNER JOIN
(
SELECT product_id, MAX(register_date) As Max_register_date
FROM tbl_inputs
GROUP BY product_id
) T2 ON(T1.product_id= T2.product_id AND T1.register_date= T2.Max_register_date)
This is, of course, assuming your dates are unique. if they are not, you need to add the DISTINCT Keyword to the outer SELECT statement.
edit
Sorry, I didn't explain it very well. Your dates can be duplicated, it's not a problem as long as they are unique per product id. if you can have duplicated dates per product id, then you will have more then one row per product in the outcome of the select statement I suggested, and you will have to find a way to reduce it to one row per product.
i.e:
If you have records like that (when the last date for a product appears more then once in your table with different prices)
id | product_Id | price | register_date
--------------------------------------------
1 | 1 | 10.00 | 01/01/2000
2 | 1 | 20.00 | 01/01/2000
it will result in having both of these records as outcome.
However, if the register_date is unique per product id, then you will get only one result for each product id.

How to display rows that when added together equal zero

Been searching for a few weeks for a solution to this but have come up blank.
I have table of data similar to this:
client_ref supplier_key client_amount
1111 GBP 10
1111 GBP -10
1111 EUR 50
2222 CHF -22.5
2222 CHF -20
3333 EUR -27
3333 EUR -52
3333 EUR 79
I need to extract all items where the client_ref and supplier_key match and the total of the client_amount equals zero. The output would look like this:
client_ref supplier_key client_amount
1111 GBP 10
1111 GBP -10
3333 EUR -27
3333 EUR -52
3333 EUR 79
I have written the following that returns the totals but I need any help you could provide to change this to show the rows that make up the totals rather than just the overall results.
SELECT tbis.client_ref ,tbis.supplier_key ,sum(tbis.client_amount)
FROM [XXXX].[dbo].[transaction] tbis
WHERE tbis.tbis.client_amount !=0
GROUP BY tbis.client_ref, tbis.supplier_key
HAVING sum(tbis.client_amount) =0
ORDER BY sum(tbis.client_amount)
Hope this makes sense and my first post is OK. Please feel free to critique my post.
Try this instead:
SELECT t1.*
FROM transactions AS t1
INNER JOIN
(
SELECT
tbis.client_ref ,
tbis.supplier_key,
sum(tbis.client_amount) AS total
FROM transactions tbis
WHERE tbis.client_amount !=0
GROUP BY tbis.client_ref, tbis.supplier_key
HAVING sum(tbis.client_amount) =0
) AS t2 ON t1.client_ref = t2.client_ref
AND t1.supplier_key = t2.supplier_key
ORDER BY t2.total;
SQL Fiddle Demo
One possible approach is to use the SUM() windowing function:
SELECT *
FROM
( SELECT tbis.client_ref ,tbis.supplier_key,tbis.client_amount,
SUM(tbis.client_amount) OVER (
PARTITION BY tbis.client_ref, tbis.supplier_key) AS total_client_amount
FROM [XXXX].[dbo].[transaction] tbis
WHERE tbis.client_amount !=0
)
WHERE total_client_amount = 0
SQL Fiddle
SELECT client_ref ,supplier_key ,sum(client_amount)
FROM transaction
WHERE client_amount <> 0
GROUP BY client_ref, supplier_key
HAVING sum(client_amount) = 0
SELECT
*
FROM
tbis
INNER JOIN
(
SELECT
client_ref, supplier_key
FROM
tbis
GROUP by client_ref, supplier_key
HAVING sum(client_amount) = 0
) match
ON match.client_ref = tbis.client_ref
AND match.supplier_key = tbis.supplier_key
Demo at http://sqlfiddle.com/#!3/a3447/8/0
SELECT t.* FROM Table1 AS t INNER JOIN
(SELECT [client_ref], [supplier_key], SUM([client_amount]) as Total
FROM Table1 GROUP BY [client_ref], [supplier_key]) AS sumTable
ON t.[client_ref] = sumTable.[client_ref] AND
t.[supplier_key] = sumTable.[supplier_key]
WHERE Total = 0;
SAMPLE FIDDLE
Make a new query for the data you want and join it by inner join to the query that produces the sums to restrict it to the lines you want.