Using Max with multiple columns from multiple table - sql

I have 3 tables t1=items (items) t2=sales offer catalogue dates
t3=sales offer catalogue item offer price (same item can be included multiple times)
t1
t1id
t1itemcode
1
A001
2
A002
3
A003
4
A004
5
A005
t2
t2id
t2endofferdate
1
2021-02-25
2
2021-03-28
3
2021-02-10
4
2021-04-10
t3
t3id
t3.t1id
t3.t2id
t3.formula
1---
1------
4------
10,80-----
2---
2------
2------
22,00-----
3---
2------
3------
13,00-----
4---
5------
2------
10,00-----
5---
2------
4------
11,25-----
6---
1------
3------
4,50------
I initially needed the itemid and the max(enddate).
I created the following SQL query:
SELECT t1.ID, max(t2.ENDDATE) ENDDATE
FROM t1
LEFT JOIN t3 ON t1.id = t3.t1id
LEFT JOIN t2 ON t3.t2id = t2.id
GROUP BY t1.id
This works fine and the output is OK. Item id not repeated multiple times and in the 2nd col the max date that appears combined with the item from t2 (item) and (t3).
ID ENDDATE
1 -- 2021-04-10
2 -- 2021-04-10
5 -- 2021-03-28
3 -- NULL
4 -- NULL
Now I have to add a col with the formula from t3. I believe that a subquery is needed, but I'm struggling with the syntax. Actually, I need the formula for the certain join of the item (t1) id with the enddate.
The result has to be like this:
ID ENDDATE FORMULA
1 -- 2021-04-10 10,80
2 -- 2021-04-10 11,25
5 -- 2021-03-28 10,00
Appreciate any help.

If you assign a row number based on the ENDDATE and then only take the first row per ID you can pull multiple column values.
WITH cte AS (
SELECT t1.ID, t2.ENDDATE, t3.FORMULA
, ROW_NUMBER() OVER (PARTITION BY t1.ID ORDER BY t2.ENDDATE DESC) RN
FROM t1
LEFT JOIN t3 ON t1.id = t3.t1id
LEFT JOIN t2 ON t3.t2id = t2.id
)
SELECT ID, ENDDATE, FORMULA
FROM cte
WHERE RN = 1;

Related

Value return incorrectly after calculated field

SELECT
t3.id,
t3.prod_ID,
MIN(diff) AS min_time
FROM
(SELECT
t1.id,
(UNIX_TIMESTAMP(t2.time_stamp_2) - UNIX_TIMESTAMP(t1.time_stamp)) AS diff
FROM
production t1
LEFT JOIN
process t2 ON t1.id = t2.id
HAVING
diff >= 0) tx
LEFT JOIN
production t3 ON t3.id = tx.id
GROUP BY
t3.id
After run, the returned result is:
id prod_ID min_time
-----------------------
1 2 1200
What it should return instead is
id prod_ID min_time
1 9 1200
I initially thought there was an error in joining, after multiple join test, same error result.
SQLFiddle
SQLFiddle_2
SQLFiddle_3
SQLFiddle_2 to clarify that I use Group By because I have multiple ID
SQLFiddle_3 to expand more.
After run on SQLFiddle_3, the returned result is:
id prod_ID min_time
-----------------------
1 2 1200
2 2 960
3 2 360
What it should be is
id prod_ID min_time
-----------------------
1 9 1200
2 2 960
3 3 360
In your query you GROUP BY t3.id only and you don't aggregate on t3.prod_ID, so the value returned is undefined.
I believe that you don't need to GROUP BY, or even rejoin to production.
Try this:
SELECT t1.id, t1.prod_ID,
(UNIX_TIMESTAMP(t2.time_stamp_2) - UNIX_TIMESTAMP(t1.time_stamp)) AS diff
FROM production t1 INNER JOIN process t2
ON t1.id = t2.id
AND (t1.id, t1.prod_ID) = (
SELECT p.id, p.prod_ID
FROM production p
WHERE p.id = t2.id AND (UNIX_TIMESTAMP(t2.time_stamp_2) - UNIX_TIMESTAMP(p.time_stamp)) >= 0
ORDER BY (UNIX_TIMESTAMP(t2.time_stamp_2) - UNIX_TIMESTAMP(p.time_stamp)) LIMIT 1
)
See the demo.
Results:
> id | prod_ID | diff
> -: | ------: | ---:
> 1 | 9 | 1200
> 2 | 2 | 960
Your query is very wrong indeed, your inner query is essentially making a list of the time differences, but then you throw all this good work away and don't join or group on the prod_id, so it essentially just picks the first value you inserted into your database. If you switch the order you insert the data so prod_id is inserted first then you get the result you want, but for the wrong reason.
Your inner query is sort of doing this:
SELECT
t1.id,
t1.prod_id,
(UNIX_TIMESTAMP(t2.time_stamp_2)-UNIX_TIMESTAMP(t1.time_stamp)) as diff
FROM production t1
left JOIN process t2
ON t1.id = t2.id
WHERE (UNIX_TIMESTAMP(t2.time_stamp_2)-UNIX_TIMESTAMP(t1.time_stamp)) > 0
If you run that you see the values you want:
id prod_id diff
1 2 1800
1 3 1380
1 9 1200
...but then your outer query does nothing with this.
There's many ways to do this, here's one simple method:
SELECT
t1.id,
t1.prod_id,
(UNIX_TIMESTAMP(t2.time_stamp_2)-UNIX_TIMESTAMP(t1.time_stamp)) as diff
FROM production t1
left JOIN process t2
ON t1.id = t2.id
WHERE (UNIX_TIMESTAMP(t2.time_stamp_2)-UNIX_TIMESTAMP(t1.time_stamp)) > 0
ORDER BY 3
LIMIT 1

SQL conditional join Teradata

I am trying to join the following two tables T1 and T2 on T1.id=T2.id, T1.MonA=T2.MonB such that
Whenever MonA=MonB (iow an entry can be found in both tables),
perform an ordinary join. This is the case for instace for ID A,
MonA=MonB=3
If a MonB entry is in table T2 but no equal MonA entry can be found in
Table T1, the join shall take from table T1 the row where MonA is
maximal. In the sample tables, this is the case for both last rows.
MonA from T1 which are not in T2 shall be ignored
The condition T1.id=T2.id is a necessary precondition, so this always needs to be true!
Table T1
ID MonA Data
A 2 BBB
A 3 CCC
B 4 DDD
B 5 EEE
B 11 EEE
Table T2
ID MonB Organ
A 3 Liver
B 5 Heart
B 7 Kidney
Here is, how the result should look like
ID MonA MonB Data Organ
A 3 3 CCC Liver
B 5 5 EEE Heart
B 11 7 EEE Kidney
I need this to be performed in Teradata SQL and honestly have no idea currently how to tackle the problem. Thanks for help!
EDIT: The may be several entries with identical ID, MonA=MonB, but different Data/Organ columns and i want all of them in the resulting table.
Lets do a join of t1 with t2 as follows
--gets all of the matching records by (id,mona) pairs from t1 with (id,monb) from t2
select a.id,a.mona,b.monb,a.data,b.organ
from t1 a
join t2 b
on a.id=b.id
and a.mona=b.monb
union all /*Here you want only from t2 not there in t1 by id*/
select b.id,x.mona,b.monb,x.data,b.organ
from t2 b
left join t1 a
on a.id=b.id
and a.mona=b.monb
left join (select row_number() over(partition by id order by mona desc) as rnk
,id
,mona
,data
from t1
)x
on b.id=x.id
and x.rnk=1 /*pick up only the largest values arranged by mona*/
where a.mona is null /*Gets only the missing records from t2 which are not in t1*/
This should return the expected result:
SELECT t1.id,t1.mona,t2.monb,t1.data,t2.organ
FROM t1
JOIN t2
ON t1.id=t2.id
QUALIFY
Row_Number()
Over (PARTITION BY t2.id, t2.organ
-- prefer same entry in both tables
ORDER BY CASE WHEN t1.mona = t2.monb THEN 1 ELSE 2 END
-- otherwise take max monA
,t1.mona DESC -- ) = 1

Join table by id and nearest date for every date

I have 2 tables:
TABLE 1
id date_measured value 1
1 01/01/2017 5
1 02/20/2017 6
1 04/01/2017 5
2 03/02/2017 5
2 04/02/2017 3
TABLE 2
id date_measured value 2
1 01/06/2017 5
1 03/01/2017 6
2 02/01/2017 5
2 03/09/2017 7
2 04/05/2017 4
I want to join it such that each id matches and the closest date matches so:
id date_measured1 value 1 date_measured2 value 2
1 01/01/2017 5 01/06/2017 5
1 02/20/2017 6 03/01/2017 6
2 02/01/2017 5 02/01/2017 5
2 03/02/2017 5 03/09/2017 7
2 04/02/2017 3 04/05/2017 4
etc. IE for each id for each date measured take the closest measured date in the other table and make it a row. Something closeish to
SELECT *
FROM table1 a
INNER JOIN table2 b
ON a.id = b.id
AND <date from a is closest date from b>
But I have no idea how to do the second part. Any suggestions?
In standard SQL, you can get the date using a correlated subquery:
select t1.*,
(select t2.date_measured
from table2 t2
where t2.id = t1.id
order by abs(t2.date_measured - t1.date_measured) asc
fetch first 1 row only
) as t2_date_measured
from table1 t1;
You can then join back to table2 to get additional information from that row.
The above is generic SQL (not necessarily standard SQL). Date/time functions tend to be peculiar to each database; so - may not work for the difference. Not all databases support fetch first 1 row only, but almost all support some mechanism for doing the same thing.
If you have window functions use ROW_NUMBER():
SQL DEMO I use postgresql so date function may vary on your rdbms
WITH cte as (
SELECT *,
t1.id as t1_id,
t1.date_measured as t1_date,
t1.value1,
t2.id as t2_id,
t2.date_measured as t2_date,
t2.value2,
date_part('day', age(t1.date_measured, t2.date_measured)) as days,
ROW_NUMBER() OVER (PARTITION BY t1.id, t1.date_measured
ORDER BY abs(date_part('day', age(t1.date_measured, t2.date_measured)))
) as rn
FROM table1 t1
JOIN table2 t2
ON t1.id = t2.id
)
SELECT *
FROM cte
WHERE rn = 1
ORDER BY t1_id, t1_date

If Statements in where clause?

So I have to run a query against a database schema: http://sqlfiddle.com/#!9/d0b643 , but an example schema would look like:
Table 1
itemID sale date salesmanID storeID
---------------------------------------------------
1 1/2015 1 1
1 3/2016 1 1
2 5/2016 2 1
2 1/2015 4 1
Table 2
itemID colorID price
--------------------------------------
1 1 23
1 2 10
1 3 13
2 1 11
2 2 14
2 3 18
Table 3
ColorID color
---------------------------------------
1 Red
2 Blue
3 Green
Table 4
SaleBegin SaleEnd ColorID salesmanID storeID
----------------------------------------------------------------
1/1/2014 12/31/2014 1 0 1
1/1/2015 12/31/2015 2 0 1
1/1/2016 12/31/2016 3 0 1
1/1/2014 12/31/2014 3 2 1
1/1/2015 12/31/2016 2 2 1
I need to have something in the where clause that pretty much says if there is a SalesmanID and the saleDate from Table1 falls between the StartDate and Enddate of Table4, use that color. Otherwise, if there is no salesmanID, use the StoreID (in this example they are all 1, but they could be different).
The current query I am adding this to is:
select t1.itemID,t3.color,t2.price
from table_1 t1
LEFT JOIN table_2 t2
ON t1.itemID = t2.itemID
LEFT JOIN table_3 t3
ON t2.colorID = t3.colorID
LEFT JOIN table_4 t4
ON t3.colorID = t4.colorID
WHERE t1.sale_date BETWEEN saleBegin and saleEnd;
How can I run this? The expected results should look like:
itemID color price
1 Blue 10
1 Green 13
2 Blue 14
2 Blue 14
I am trying to use MSSQL logic. and try it in SQL fiddle it run success.
Just try this code yourself to see if this is what you want.
select t1.itemID,t3.color,t2.price from table_1 t1
LEFT JOIN table_4 t41
on t1.sale_date BETWEEN t41.saleBegin and t41.saleEnd
and t41.salesmanID!=0 and t1.salesmanID=t41.salesmanID
LEFT JOIN table_4 t42
on t1.sale_date BETWEEN t42.saleBegin and t42.saleEnd
and t42.salesmanID=0 and t1.storeID=t42.storeID
INNER JOIN table_2 t2
ON t1.itemID = t2.itemID
INNER JOIN table_3 t3
ON t2.colorID = t3.colorID and t2.colorID=IF(ISNULL(t41.colorID),t42.colorID,t41.colorID)
order by t1.itemID,t3.color,t2.price;
I think this is what you want. You will get too many rows because of overlaps between store # and Sale dates. Once I changed your sample data table4 to have the last two rows be store #2 I get the results you posted.
1/1/2014 12/31/2014 3 2 2
1/1/2015 12/31/2016 2 2 2
Select t1.itemID,t3.color,t2.price
From table_1 t1
LEFT JOIN table_4 t4 ON t1.sale_date BETWEEN t4.saleBegin And t4.saleEnd And t1.SalesmanID = t4.SalesmanID
LEFT JOIN table_4 t4a ON t1.sale_date BETWEEN t4a.saleBegin And t4a.saleEnd And t1.StoreID = t4a.StoreID
LEFT JOIN table_2 t2 ON t1.itemID = t2.itemID And t2.ColorID = t4a.ColorID
LEFT JOIN table_3 t3 ON t2.colorID = t3.colorID
Where t4.SalesmanID Is Null
Group By t1.itemID,t3.color,t2.price
Union All
Select t1.itemID,t3.color,t2.price
From table_1 t1
LEFT JOIN table_4 t4 ON t1.sale_date BETWEEN t4.saleBegin And t4.saleEnd And t1.SalesmanID = t4.SalesmanID
LEFT JOIN table_2 t2 ON t1.itemID = t2.itemID And t2.ColorID = t4.ColorID
LEFT JOIN table_3 t3 ON t2.colorID = t3.colorID
Where t4.SalesmanID Is Not Null
itemID color price
1 Blue 10.000
1 Green 13.000
2 Blue 14.000
2 Blue 14.000

SQL nested Inner Joins? Or Where Clause On Inner Joins?

I am having an issue with a query I am trying to write. Unfortunately I can only query against the database and cannot change the schema. Here are some simplified example tables:
Table 1
itemID sale date
----------------------------
1 1/2015
1 3/2016
2 5/2016
2 1/2015
Table 2
itemID colorID price
--------------------------------------
1 1 23
1 2 10
1 3 13
2 1 11
2 2 14
2 3 18
Table 3
ColorID color
---------------------------------------
1 Red
2 Blue
3 Green
Table 4
SaleBegin SaleEnd ColorID
----------------------------------------
1/1/2014 12/31/2014 1
1/1/2015 12/31/2015 2
1/1/2016 12/31/2016 3
Now I need a query that essentially gets the price and color for both item ids in the first table. I'm not sure how to do it with this schema even just for one. I tried something like:
SELECT item_id, price, color FROM Table1 T1
INNER JOIN Table2 T2
ON T1.ItemID=T2.ItemID
INNER JOIN Table3 T3
ON T2.ColorID=T3.ColorID
INNER JOIN Table4 T4
ON T3.ColorID=T4.ColorID
WHERE T1.itemID between SaleBegin AND SaleEnd
But it's not working for me, instead giving me every possible color and price for each item. What am I doing wrong? How do I get those values? Expected results should look like:
itemID color price
------------------------------------
1 Blue 10
1 Green 13
2 Green 18
2 Blue 14
http://sqlfiddle.com/#!9/e6fac/4
SELECT t1.itemID
, t3.color
, t2.price
FROM table_1 t1
LEFT JOIN table_2 t2
ON t1.itemID = t2.itemID
LEFT JOIN table_3 t3
ON t2.colorID = t3.colorID
LEFT JOIN table_4 t4
ON t3.colorID = t4.colorID
WHERE t1.sale_date BETWEEN saleBegin and saleEnd;
I'm not sure this is what you're looking for, but this query will give you the itemID, color and price for each product during each sale
SELECT
t4.SaleBegin
,t4.SaleEnd
,t1.itemID
,t3.color
,t2.price
FROM Table4 t4
INNER JOIN Table1 t1
ON t4.SaleBegin = CAST(REPLACE(t1.[sale date], '/', '/1/') AS DATE)
INNER JOIN Table2 t2
ON t1.itemID = t2.itemID
AND t4.ColorID = t2.ColorID
INNER JOIN Table3 t3
ON t4.ColorID = t3.colorID
Here is the SQL Fiddle