alexstuart
alexstuart

Reputation: 33

Joining a table to two one-to-many relationship tables in SQL Server

Happy Friday folks,

I'm trying to write an SSRS report displaying data from three (actually about 12, but only three relevant) tables that have akward relationships and the SQL query behind the data is proving difficult.

There are three entities involved - a Purchase Order, a Sales Order, and a Delivery. The problem is the a Purchase Order can have many sales orders, and also many deliveries which are NOT linked to the sales orders...that would be too easy.

Both the Sales Order and Delivery tables can be linked to the Purchase Order table by foreign keys and an intermediate table each.

I need to basically list Purchase Orders, a list of sales orders and a list of deliveries next to them, with NULLs for any fields that aren't valid so that'll give the required output in SSRS/when read by a human, ie, for a purchase order with 2 sales orders and 4 delivery dates;

PO    SO      Delivery
1234  ABC     05/10
1234  DEF     09/10
1234  NULL    10/12
1234  NULL    14/12

The above (when grouped by PO) will tell the users there are two sales orders and four (unlinked) delivery dates.

Likewise if there are more SOs than deliveries, we need NULLs in the Delivery column;

PO    SO      Delivery
1234  ABC     03/08
1234  DEF     NULL
1234  GHI     NULL
1234  JKL     NULL  

Above would be the case with 4 SOs and one delivery date.

Using Left Outer joins alone gives too much duplication - in this case 8 rows, as it gives 4 delivery dates for each match on the sales order;

PO    SO      Delivery
1234  ABC     05/10
1234  ABC     09/10
1234  ABC     10/12
1234  ABC     14/12    
1234  DEF     05/10
1234  DEF     09/10
1234  DEF     10/12
1234  DEF     14/12

It's fine that the PO column is duplicated as SSRS can visually group that - but the SO/Delivery fields can't be allowed to duplicate as this can't be got rid of in the report - if I group the column in SSRS by SO then it still spits out 4 delivery dates for each one.

The only situation our query works nice is when there is just one SO per PO. In that case the single PO and SO numbers are duplicated together for x deliveries and can both be neatly grouped in SSRS. Unfortunately this is a rare occurence in the data.

I've thought of trying to use some sort of windowing function or CROSS APPLY but both fall down as they will repeat for every PO number listed and end up spitting out too much data.

At the point of thinking this just isn't set-based enough to be doable in SQL, I know the data is horrible..

Any help much appreciated.

EDIT - basical sqlfiddle link to the table schemas. Omitted many columns which aren't relevant. http://sqlfiddle.com/#!2/5ba16

Example data...

Purchase Order
PO_Number Style
1001      Black work boots
1002      Green hat
1006      Red Scarf

Sales Order
Sales_order_number    PO_number    Qty   Retailer
A100-21               1001         15    Walmart
A100-22               1001         29    Walmart
A200-31               1006         1000  Asda

Delivery
Delivery_ID     Delivery_Date    PO_number
1543285         10/05/2014       1001
1543286         12/05/2014       1001
1543287         17/05/2014       1001
1543288         21/05/2014       1002

Upvotes: 3

Views: 958

Answers (1)

podiluska
podiluska

Reputation: 51504

If you assign row numbers to the elements in salesorders and deliveries, you can link on that.

Something like this

declare @salesorders table (po int, so varchar(10))
declare @deliveries table (po int, delivery date)
declare @purchaseorders table (po int)
insert @purchaseorders values (123),(456)

insert @salesorders values (123,'a'),(123,'b'),(456,'c')
insert @deliveries values (123,'2014-1-1'),(456,'2014-2-1'),(456,'2014-2-1')


select *
from
(   
    select numbers.number, p.po, so.so, d.delivery from @purchaseorders p
    cross join (Select number from master..spt_values where type='p') numbers           
    left join (select *,ROW_NUMBER() over (partition by po order by so) sor  from @salesorders ) so
        on p.po = so.po and numbers.number = so.sor
    left join (select * , ROW_NUMBER() over (partition by po order by delivery) dor from @deliveries) d
        on p.po = d.po and numbers.number = d.dor
) v
where so is not null or delivery is not null
order by po,number          

Upvotes: 1

Related Questions