SQL - Tricky Merging - sql

I'm facing a tricky query to do. I hope your expertise will help me to sort it out.
There are 2 tables:
Table1 : Orders
Index ProductName OrderDate
0 a 03/03/1903
1 a 10/03/2014
2 b 01/01/2017
3 c 01/01/2019
Table2 : Product Specs
--> This table shows every change made in the Color of our products
Index ProductName Color ColorUpdatedOn
0 a Blue 01/01/1900
1 a Red 01/01/2014
2 a Yellow 01/01/2017
3 b Pink 01/01/2017
4 c Black 01/01/2018
5 c Black 31/12/2018
I would like to be able to retrieve all the data from Table1 with the Column Color et UpdatedOn
Index ProductName OrderDate Color ColorUpdatedOn
0 a 03/03/1903 Blue 01/01/1900
1 a 10/03/2014 Red 01/01/2014
2 a 01/01/2019 Yellow 01/01/2017
3 c 01/01/2019 Black 31/12/2018
Do you have any idea how I could do this ?
Thank you in advance for your help
Largo

Get the max() date of Product Specs table based on color,
then join it using year() function, applicable on mysql and mssql, not sure with other db.
select o.Index, o.ProdcutName, o.Date, t1.color, t1.ColorUpdatedOn
from Orders o
inner join
(select color, max(colorupdatedon) as ColorUpdatedOn
from productspecs
group by color) t1 on year(o.OrderDate) = year(t1.createdon)
but I would prefer using right() function since your year dates are at the end.
select o.Index, o.ProdcutName, o.Date, t1.color, t1.ColorUpdatedOn
from Orders o
inner join
(select color, max(colorupdatedon) as ColorUpdatedOn
from productspecs
group by color) t1 on right(o.OrderDate, 4) = right(t1.createdon, 4)

In a database that supports lateral joins (which is quite a few of them now), this is pretty easy:
select o.*, s.* -- select the columns you want
from orders o left join lateral
(select s.*
from specs s
where s.ProductName = o.ProductName and
s.ColorUpdatedOn <= o.OrderDate
order by s.ColorUpdatedOn desc
fetch first 1 row only
) s
on 1=1;
In SQL Server, this would use outer apply rather than left join lateral.
In other databases, I would use lead():
select o.*, s.* -- select the columns you want
from orders o left join
(select s.*,
lead(ColorUpdatedOn) over (partition by ProductName order by ColorUpdatedOn) as next_ColorUpdatedOn
from specs s
) s
on s.ProductName = o.ProductName and
o.OrderDate >= s.ColorUpdatedOn and
(o.OrderDate < s.next_ColorUpdatedOn or s.next_ColorUpdatedOn is null)

Assuming, the datatype for OrderDate and ColorUpdatedOn are both date, we can find the colors which was at the time of order.
For this I have used the anlytical/windowing function. The Hive query would look like this:
SELECT
y.ProductName, y.OrderDate, y.Color, y.ColorUpdatedOn
FROM (
SELECT
x.*,
DENSE_RANK() OVER(PARTITION BY x.ProductName, x.OrderDate ORDER BY x.recency ASC) AS relevance
FROM (
SELECT
a.*, b.color, b.ColorUpdatedOn, DATEDIFF(a.OrderDate, b.ColorUpdatedOn) AS recency
FROM
Order a
INNER JOIN
Product b
ON (
a.ProductName = b.ProductName
AND a.OrderDate >= b.ColorUpdatedOn
)
) x
) y
WHERE
y.relevance = 1;
The query could be made specific if you let me know the database you are using.
Let me know if it helps.

Related

How to join records from a group by

I don't know if the title is as descriptive as I wanted but I'll try to explain with real examples of what I want.
In my table 'Details' I have
Date | ProductId | Total
-------------------------------------
17/05/20 | 16788 | 62
--------------------------------------
19/05/20 | 3789 | 15
So I want the result be something like that:
17/05/20 - 16788 - 62
17/05/20 - 3789 - NULL (or 0)
19/05/20 - 16788 - NULL (or 0)
19/05/20 - 3789 - 15
I started doing RIGHT JOIN with a GROUP BY of the Dates, but didn't work.
I run out of ideas, can someone help me?
Thanks in advance
You can generate the rows with a cross join and then bring in the values using left join:
SELECT d.date,
p.productid,
t.total
FROM (
SELECT DISTINCT DATE
FROM details
) d
CROSS JOIN (
SELECT DISTINCT productid
FROM details
) p
LEFT JOIN details t
ON t.date = d.date
AND t.productid = p.productid
ORDER BY
d.date,
p.productid DESC;

Inner Join without repeating value, keeping the rows of the first table

I have a table with the discount for each invoice. For example:
Invoice Number|Discount
------------------------
1 | 3
2 | 5
3 | 6
I need to pull these discounts to the invoice lines table (because they only apply to the total of the invoice, not to a particular line). At the same time I cannot lose any line.
Example: If the invoice 1 has 5 lines, I need all lines to show up (the 5 lines of the invoice), but I want the discount only once (for example, the first line would be enough).
Expected:
Invoice Number|Discount
------------------------
1 | 3
1 | null
1 | null
1 | null
1 | null
If I have an Invoice table, and a InvoiceLines table that can be joined by the invoice number in both tables, how can I get the result that I need?
I tried this query without success:
Select
ROW_NUMBER() over(order by v.num_fra)as Rank,
l.*,
v.ctdrap_div as discount
from ffac_vta v --(invoicetable)
join ffac_hla l --(invoice lines table)
ON v.num_fra = l.num_fra
Can you help me?
Here is another way to do this...Basically, your sub query pulls the line item info - and gets the row number (partitioned by order number). Then, you LEFT OUTER JOIN that subset to the table with the discount value ONLY when the row number = 1. This approach doesn't require a CASE statement since the LEFT OUTER JOIN will give you NULL values for all row numbers above 1.
SELECT Sub.*,
V.ctdrap_div AS [Discount]
FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY v.num_fra ORDER BY v.num_fra) AS [Row Number]
FROM ffac_hla L
) Sub
LEFT OUTER JOIN ffac_vta V
ON v.num_fra = Sub.num_fra
AND Sub.[Row Number] = 1
It seems like you should be able to just change your join to a left join.
Select
ROW_NUMBER() over(order by v.num_fra)as Rank,
l.*,
v.ctdrap_div as discount
from ffac_vta v --(invoicetable)
left join ffac_hla l --(invoice lines table)
ON v.num_fra = l.num_fra
You will need to use the key of your lines table or something to order your rows. Something like this. Also, do we have ROW_NUMBER() in SQL-Server-2008?
SELECT T.num_fra,
CASE WHEN T.rank = 1 THEN T.Discount
ELSE NULL AS Discount
FROM
(
Select
ROW_NUMBER() over(PARTITION BY v.num_fra ORDER BY <<ADD THE KEY OF INVOICE LINES HERE>>)as Rank,
l.*,
v.ctdrap_div as discount
from ffac_vta v --(invoicetable)
join ffac_hla l --(invoice lines table)
ON v.num_fra = l.num_fra
) AS T
Change to
;WITH cte AS (SELECT DENSE_RANK() over(order by v.num_fra)as Rank,
num_fra,
l.*,
v.ctdrap_div as discount
FROM ffac_vta v --(invoicetable)
JOIN ffac_hla l --(invoice lines table)
ON v.num_fra = l.num_fra
)
SELECT num_fra
, CASE WHEN Rank = 1 THEN discount ELSE 0 END
, *
FROM cte;
I think you could use left join...
For more information about join, you can see this web site : https://www.quora.com/SQL-What-is-the-difference-between-various-types-of-joins
difference between join image
I hope this help you
Try this:
select i.InvoiceID,Discount
from invoicedetail i
left join invoicediscount id on i.invoiceID=id.invoiceid and i.linenumber=1

Last two row from multiple records assistance

I’m having a little problem that I’m not sure how to get a round and hoping someone here can assist. What I need to do is run a select on multiple records and retrieve the last two records of p.CustID. When enter in one p.CustID the code works fine however I need to remove the where clause and I need it to retrieve the last two records for each p.CustID (about 14,000 records in total) When I remove the where clause it only returns two records in total which are top two records in my from statement [DB_User].[dbo].[P1ASellers]. I tried using this in a CTE but still cannot get this to return
Code I’m using below:
SELECT TOP (2)
cbc.StorePartnerCustConfigID,
p.CustID,
cbc.ConfigID,
cbc.EffectiveDate,
ROW_NUMBER() OVER (ORDER BY cbc.StorePartnerID DESC) AS RowNum
FROM [DB_User].[dbo].[P1ASellers] p
INNER JOIN [ACA].dbo.tblConfig_StorePartnerConfig BP
ON BP.EntityID=CAST(p.CustID AS VARCHAR)
INNER JOIN [ACA].dbo.tblConfig_StorePartner CBP
ON CBP.StorePartnerID=BP.StorePartnerID
INNER JOIN [ACA].dbo.tblConfig_StorePartnerCustConfig CBC
ON CBP.StorePartnerID=CBC.StorePartnerID
AND cbc.ProcessConfigID IN (1,2,3,4)
INNER JOIN [ACA].dbo.tblConfig_StorePartnerCustConfig CBC2
ON CBC.StorePartnerID=CBC2.StorePartnerID
AND cbc2.ConfigID IN (1,2,3,4) where p.CustID=55555 <-need to remove the
where
ORDER BY cbc.StorePartnerID DESC
The results from the query
StorePartnerCustConfigID CustID ConfigID EffectiveDate RowNum
15031 55555 4 2015-06-25 1
15032 55555 1 2015-06-25 2
What I actually get after I remove the where clause:
StorePartnerCustConfigID CustID ConfigID EffectiveDate RowNum
68995 89566 2 2011-03-02 1
68996 89566 1 2011-03-02 2
what I expect after I remove the where clause:
StorePartnerCustConfigID CustID ConfigID EffectiveDate RowNum
15031 55555 4 2015-06-25 1
15032 55555 1 2015-06-25 2
64584 65486 2 2013-04-16 1
64585 65486 1 2013-04-16 2
So on and so on.......
Any input greatly appreciated, thanks!!
I think you are looking for top 2 records for each customer which you can get as below:
SELECT TOP (2) with ties
cbc.StorePartnerCustConfigID,
p.CustID,
cbc.ConfigID,
cbc.EffectiveDate,
ROW_NUMBER() OVER (ORDER BY cbc.StorePartnerID DESC) AS RowNum
FROM [DB_User].[dbo].[P1ASellers] p
INNER JOIN [ACA].dbo.tblConfig_StorePartnerConfig BP
ON BP.EntityID=CAST(p.CustID AS VARCHAR)
INNER JOIN [ACA].dbo.tblConfig_StorePartner CBP
ON CBP.StorePartnerID=BP.StorePartnerID
INNER JOIN [ACA].dbo.tblConfig_StorePartnerCustConfig CBC
ON CBP.StorePartnerID=CBC.StorePartnerID
AND cbc.ProcessConfigID IN (1,2,3,4)
INNER JOIN [ACA].dbo.tblConfig_StorePartnerCustConfig CBC2
ON CBC.StorePartnerID=CBC2.StorePartnerID
AND cbc2.ConfigID IN (1,2,3,4)
ORDER (row_number() over(partition by p.CustId Order BY cbc.StorePartnerID DESC-1)/2 +1 )

SQL - group in one table for a max in another

I'm trying to do a (Microsoft) sql query to get the most recent order from one table for each product in another. That is to say - the group by is in table B, while the max is in table A. I tried a number of things, but this is the final.
WITH max_date(maxid, maxdate) as (
SELECT inmost.PROD_ID as maxid
,MAX(inmost.ORDER_DATE) as maxdate
FROM(SELECT od2.PROD_ID
,o2.ORDER_DATE
FROM ORDERS o2
INNER JOIN ORDPRODS od2 ON o2.ORDER_ID = od2.ORDER_ID
) as inmost
GROUP BY inmost.PROD_ID
)
SELECT o.ORDER_DATE
,od.ORDER_QUANT
FROM ORDERS o
INNER JOIN ORDPRODS od ON o.ORDER_ID = od.ORDER_ID
LEFT JOIN max_date mxord ON od.PROD_ID = mxord.maxid
AND o.ORDER_DATE = mxord.maxdate
WHERE o.ORDERS_Canceled = '0'
At the end of things, it's still pulling multiple version of the each product and lots and lots of date for those products. For instance:
PROD_ID ORDER_DATE
111 1/1/2015
111 1/2/2015
112 1/2/2015
112 1/3/2015
112 1/4/2015
112 1/5/2015
What I WANT is:
PROD_ID ORDER_DATE
111 1/2/2015
112 1/5/2015
The join clause here means find matching Max_Date_mxord record on the ID and the date. Return all records found in od
LEFT JOIN max_date mxord ON od.PROD_ID = mxord.maxid
AND o.ORDER_DATE = mxord.maxdate
To visualize this you can add mxord.maxdate to your SELECT clause and you'd likely see many nulls
However you want to exclude records in OD that don't match maxdate. To do that you want an INNER join.
This is labeled as #mysql, but to my knowledge, MYSQL doesn't support the WITH clause, so I'm assuming this is SQL Server.
If that's the case, you could use the CROSS APPLY function, which would look something like this.
SELECT prd.PRODID, apl.ORDER_DATE
FROM ORDPRODS prd
CROSS APPLY (
SELECT TOP 1 ORDER_DATE
FROM ORDERS ord
WHERE ord.ORDER_ID = prd.ORDER_ID
AND ord.ORDERS_Canceled = '0'
ORDER BY ORDER_DATE DESC
) apl

SQL on joining tables (Month and subtextures that has no sales)

DBMS: Derby Embedded
Hello I wonder how I can make some outcome like
SubTextureID Year Month NetSales
1 2013 10 1000
2 2013 10 2000
3 2013 10 0
The third row never appears if that product
has no sales(no records) in the order detail table
Any help would be greatly appreciated!
Thanks
Jack
select s.TextureName, s.SubTextureId, sum(COALESCE(d.NetSales, 0)) NetSales
from (select SubTextureId, TextureName from subtexture) as s
join
(select SubTextureId, ProductCode from products) as p
on (p.SubTextureId = s.SubTextureId)
left outer join
(select ProductCode, OrderCode, NetSales from order_details) as d
on (d.ProductCode = p.ProductCode)
left outer join
( select YEAR(o.PurchaseDateTime) y,
MONTH(o.PurchaseDateTime) m,
OrderCode
from orders o
where o.PurchaseDateTime between '2013-11-01 00:00:00' and '2013-11-30 23:59:59' -- make use of an index if one exists
) as o
on (o.orderCode = d.orderCode)
group by s.TextureName, s.SubTextureId, o.y, o.m
because you used LEFT OUTER JOIN, try to use RIGHT OUTER JOIN, if you understand what's difference about LEFT and RIGHT OUTER JOIN, you will handle your problem