Sql-server – Join based on date

join;sql serversql-server-2008-r2

(SQL Server 2008 R2) I need to get a result set the link between two tables linked by key items and a special link to date. I have a couple of troubles to know how to get this link and I can't use this a stored procedure, so it must be a view. Here is my case:

Table Materials:
You will see how an item is composed of materials but these materials were purchased in different dates in 2016 and 2017.

Item    BOM Date    Warehouse   Material ID BOM #
CDIL-PTU208XXX-00   2016-12-08 00:00:00.000 MAIN    SDCGF-208L-0250-001 B000001211
CDIL-PTU208XXX-00   2016-12-08 00:00:00.000 MAIN    SDCGF-208L-0250-002 B000001211
CDIL-PTU208XXX-00   2016-12-08 00:00:00.000 MAIN    SDCGF-208L-0300-001 B000001211
CDIL-PTU208XXX-00   2016-12-08 00:00:00.000 MAIN    SDCXX-PTU-0208-001  B000001211
CDIL-PTU208XXX-00   2017-06-28 00:00:00.000 MAIN    SDCGF-208L-0250-001 B000001358
CDIL-PTU208XXX-00   2017-06-28 00:00:00.000 MAIN    SDCGF-208L-0250-002 B000001358
CDIL-PTU208XXX-00   2017-06-28 00:00:00.000 MAIN    SDCGF-208L-0300-001 B000001358
CDIL-PTU208XXX-00   2017-06-28 00:00:00.000 MAIN    SDCGF-208L-0300-003 B000001358
CDIL-PTU208XXX-00   2017-06-28 00:00:00.000 MAIN    SDCXX-PTU-0208-001  B000001358

Table Invoices:

Invoice Date    Item
2017-09-25 00:00:00.000 CDIL-PTU208XXX-00
2017-09-22 00:00:00.000 CDIL-PTU208XXX-00
2017-09-20 00:00:00.000 CDIL-PTU208XXX-00
2017-09-20 00:00:00.000 CDIL-PTU208XXX-00
2017-09-15 00:00:00.000 CDIL-PTU208XXX-00
2017-09-08 00:00:00.000 CDIL-PTU208XXX-00
2017-09-08 00:00:00.000 CDIL-PTU208XXX-00
2016-12-29 00:00:00.000 CDIL-PTU208XXX-00
2016-12-28 00:00:00.000 CDIL-PTU208XXX-00
2016-12-21 00:00:00.000 CDIL-PTU208XXX-00
2016-12-16 00:00:00.000 CDIL-PTU208XXX-00
2016-12-15 00:00:00.000 CDIL-PTU208XXX-00
2016-12-14 00:00:00.000 CDIL-PTU208XXX-00
2016-12-13 00:00:00.000 CDIL-PTU208XXX-00
2016-12-13 00:00:00.000 CDIL-PTU208XXX-00
2016-12-12 00:00:00.000 CDIL-PTU208XXX-00
2016-12-09 00:00:00.000 CDIL-PTU208XXX-00

I need a result set the materials that composed the item sold in the most closed date to the invoice date. so for invoices in 2017 get in the output items in 2017 and in 2016 just the components in 2016. I know the join must be in invoice date but I don't know how to create it.

My tries:

SELECT month([Invoice Date]),bom.[Material ID]--,bom.[BOM #]
FROM [dbo].[INVOICES] inv
LEFT JOIN (select Item, Warehouse,[Material ID],[BOM Date]
            from [dbo].[BOM]) bom
ON inv.Item = bom.Item
AND [Invoice Date]>= MAX([BOM Date])

required output

We see how the components for the Item in 2016 were 4 but for 2017 are 5, So my count must be based in the closest date coming from materials with invoice date to get something like:

In Sept 2017 we sold 7 products so that means that I needed 7 materials per id material and the same logic for 2016.

year    month   material_id qty
2017    Sept    SDCGF-208L-0250-001 7
2017    Sept    SDCGF-208L-0250-002 7
2017    Sept    SDCGF-208L-0300-001 7
2017    Sept    SDCGF-208L-0300-003 7
2017    Sept    SDCXX-PTU-0208-001  7
2016    Dec SDCGF-208L-0250-001 10
2016    Dec SDCGF-208L-0250-002 10
2016    Dec SDCGF-208L-0300-001 10
2016    Dec SDCXX-PTU-0208-001  10

Editing to add insert scripts:

USE tempdb
SET NOCOUNT ON 


CREATE TABLE #Materials
(
    Item VARCHAR(20),
    BOMDate DATETIME,
    Warehouse VARCHAR(5),
    MaterialId VARCHAR(30),
    BomNo VARCHAR(20)
);

INSERT #Materials ( Item, BOMDate, Warehouse, MaterialId, BomNo )
SELECT *
FROM   ( VALUES ( 'CDIL-PTU208XXX-00', '2016-12-08 00:00:00.000', 'MAIN', 'SDCGF-208L-0250-001', 'B000001211' ),
                ( 'CDIL-PTU208XXX-00', '2016-12-08 00:00:00.000', 'MAIN', 'SDCGF-208L-0250-002', 'B000001211' ),
                ( 'CDIL-PTU208XXX-00', '2016-12-08 00:00:00.000', 'MAIN', 'SDCGF-208L-0300-001', 'B000001211' ),
                ( 'CDIL-PTU208XXX-00', '2016-12-08 00:00:00.000', 'MAIN', 'SDCXX-PTU-0208-001 ', 'B000001211' ),
                ( 'CDIL-PTU208XXX-00', '2017-06-28 00:00:00.000', 'MAIN', 'SDCGF-208L-0250-001', 'B000001358' ),
                ( 'CDIL-PTU208XXX-00', '2017-06-28 00:00:00.000', 'MAIN', 'SDCGF-208L-0250-002', 'B000001358' ),
                ( 'CDIL-PTU208XXX-00', '2017-06-28 00:00:00.000', 'MAIN', 'SDCGF-208L-0300-001', 'B000001358' ),
                ( 'CDIL-PTU208XXX-00', '2017-06-28 00:00:00.000', 'MAIN', 'SDCGF-208L-0300-003', 'B000001358' ),
                ( 'CDIL-PTU208XXX-00', '2017-06-28 00:00:00.000', 'MAIN', 'SDCXX-PTU-0208-001 ', 'B000001358' )) AS x (
Item, BOMDate, Warehouse, MaterialId, BomNo );

CREATE TABLE #Invoices
(
    InvoiceDate DATETIME,
    Item VARCHAR(20)
);
INSERT #Invoices ( InvoiceDate, Item )
SELECT *
FROM   ( VALUES ( '2017-09-25 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2017-09-22 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2017-09-20 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2017-09-20 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2017-09-15 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2017-09-08 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2017-09-08 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-29 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-28 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-21 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-16 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-15 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-14 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-13 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-13 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-12 00:00:00.000', 'CDIL-PTU208XXX-00' ),
                ( '2016-12-09 00:00:00.000', 'CDIL-PTU208XXX-00' )) AS X ( InvoiceDate, Item );

Update

I have done something like that:

SELECT month([Invoice Date]),bom.[Material ID],count(*)
FROM [dbo].[INVOICES_VIEW] inv
LEFT JOIN (select Division, Item,[Material ID],[BOM Date]
            from [dbo].[BOM_VIEW]
            where [Work Center]= 'PREPA') bom
ON inv.Item = bom.Item
AND inv.division = bom.division
AND bom.[BOM Date] = (SELECT MAX(bom2.[BOM Date])
                        FROM [dbo].[BOM_VIEW] bom2
                        WHERE inv.Item = bom2.Item
                           AND inv.division = bom2.division 
                           AND bom2.[BOM Date] <= inv.[Invoice Date])
WHERE inv.Item='CDIL-PTU208XXX-00'
AND year([Invoice Date])=2017 and month([Invoice Date])=9
GROUP BY    month([Invoice Date]),bom.[Material ID]     

I cant use CTE. Insights?

Best Answer

Try this

select 
t1.Year, 
t1.Month, 
m.materialid,
t1.[RowCount]
from (
select Item, YEAR(Invoicedate) as [Year]
, MONTH(Invoicedate) as [MonthNum]
, DateName( month , DateAdd( month , MONTH(Invoicedate) , 0 ) - 1 ) as [Month], COUNT(*) as [RowCount]
from [dbo].[Invoices]
group by YEAR(Invoicedate), MONTH(Invoicedate), Item
) T1
join [dbo].[Materials] m 
on 
T1.Year = year(m.[BOMDate]) 
--and t1.[MonthNum] = month(m.[BOMDate])
and t1.item = m.item

This query returns exactly the same result that you are expecting, but I am not sure about "So my count must be based in the closest date coming from materials ".

Please check it out.