6

OK, I have a system where orders get entered, and each order can have any number of bond, job and project numbers attached to it. The OrderDetails table contains an orderID used to go to the OrderNumbers table and get all attached "numbers" to this order, and then based on a numType column found there, it goes to one of the three tables (bond, job, project) and retrieves the actual number displayed and printed for the user.

This means that trying to query an order to search for a certain project number can return 3 rows for the same order if that order had 3 project numbers attached to it.

I am looking to be able to return my query results with only 1 row for the order, and a column which includes all 3 projects in delimited form, is this possible?

Here is my current query which returns multiple rows per order, when multiple numbers of the same type are attached to the order...

SELECT 
    ISNULL(b.Descr,'') as Bond, ISNULL(PO.Description,'') as PO, 
    ISNULL(j.Descr,'') as Job, ISNULL(Proj.Descr,'') as Project, 
    d.OrdNbr, d.BillofLadingNbr, d.TripAndDeliveryTicketNbr
FROM 
    OrderDetail d 
LEFT JOIN 
    OrderItemNumbers n ON d.OWID = n.LoadOWID 
LEFT JOIN 
    Bond b ON n.NumberOWID = b.OWID AND n.NumType = 0
LEFT JOIN 
    PONumbers PO ON n.NumberOWID = PO.OWID AND n.NumType = 1 
LEFT JOIN 
    Job j ON n.NumberOWID = j.OWID AND n.NumType = 2 
LEFT JOIN 
    Project Proj ON n.NumberOWID = Proj.OWID AND n.NumType = 3 
WHERE 
    d.OWID = 'BTBD1004'

and here are the results I get....

Bond    PO              Job Project         OrdNbr      BillofLadingNbr
                            82001-8177-44   BTBD000063  BTBD000072  
        4.158 Kingsport                     BTBD000063  BTBD000072  
                            IME-81-1(118)   BTBD000063  BTBD000072  

and here is what I am trying to get...(basically flattening those first 4 columns)

Bond    PO              Job Project                         OrdNbr      BillofLadingNbr
        4.158 Kingsport     82001-8177-44, IME-81-1(118)    BTBD000063  BTBD000072  

Any help is appreciated!!! Thanks, Doug

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
kingfenix
  • 63
  • 1
  • 6
  • One thing mysql has over sql-server is the jewel GROUP_CONCAT,AFAIK – Mihai Jan 08 '14 at 17:52
  • 1
    This answer might help you out: http://stackoverflow.com/questions/8005846/sql-server-combining-multiple-rows-into-one-row – DLeh Jan 08 '14 at 18:09
  • dont think that would help Dleh, because I originally need to be able to query for orders based on individual values, like where Project = 'IME-81-1(118)' and I have all this joined through the multiple tables unlike that example. This is hurting my head, lol – kingfenix Jan 08 '14 at 18:19
  • using stuff and for xml gets close to what I need, but then I lose my ability to specify where clause for the project numbers and many valued tables joined. grrrrr – kingfenix Jan 08 '14 at 19:33

1 Answers1

6

By your example, it seems that there are no matching records on Bond and Job tables. If there were, you would get something like this:

BOND        PO              JOB      PROJECT        ORDNBR     BILLOFLADINGNBR  
Some bond                                           BTBD000063 BTBD000072
            4.158 Kingsport                         BTBD000063 BTBD000072
                            Some job                BTBD000063 BTBD000072
                                     82001-8177-44  BTBD000063 BTBD000072
                                     IME-81-1(118)  BTBD000063 BTBD000072

That result set seems so "sparse" because you were joining OrderItemNumbers directly into your main query: this table has a distinct row for each child table (PONumbers, Job, Project), and so you get each join in a distinct, separated row. In order to avoid that, you could join OrderItemNumbers with each child table, and then join this subquery with OrderDetail (through the shared LoadOWID):

SELECT
    ISNULL(b.Descr, '') as Bond,
    ISNULL(PO.Description, '') as PO, 
    ISNULL(j.Descr, '') as Job,
    ISNULL(Proj.Descr, '') as Project, 
    d.OrdNbr,
    d.BillofLadingNbr,
    d.TripAndDeliveryTicketNbr
FROM 
    OrderDetail d 
    LEFT JOIN (
        SELECT aux.*, n.*
        FROM Bond aux INNER JOIN OrderItemNumbers n ON n.NumberOWID = aux.OWID AND n.NumType = 0
    ) AS b
        ON b.LoadOWID = d.OWID
    LEFT JOIN (
        SELECT aux.*, n.*
        FROM PONumbers aux INNER JOIN OrderItemNumbers n ON n.NumberOWID = aux.OWID AND n.NumType = 1
    ) AS PO
        ON PO.LoadOWID = d.OWID
    LEFT JOIN (
        SELECT aux.*, n.*
        FROM Job aux INNER JOIN OrderItemNumbers n ON n.NumberOWID = aux.OWID AND n.NumType = 2
    ) AS j
        ON j.LoadOWID = d.OWID
    LEFT JOIN (
        SELECT aux.*, n.*
        FROM Project aux INNER JOIN OrderItemNumbers n ON n.NumberOWID = aux.OWID AND n.NumType = 3
    ) AS Proj
        ON Proj.LoadOWID = d.OWID
WHERE 
    d.OWID = 'BTBD1004'

That way you will get the following result:

Bond       PO               Job       Project         OrdNbr      BillofLadingNbr
Some bond  4.158 Kingsport  Some job  82001-8177-44   BTBD000063  BTBD000072  
Some bond  4.158 Kingsport  Some job  IME-81-1(118)   BTBD000063  BTBD000072

I agree this is not exactly what you asked for, yet. You also seem to need some sort of "partial pivoting" over the Project table (at least).

That also would be possible, in theory, but I wouldn't go that way. This is clearly a presentation requirement, and the cost to implement it on the SQL layer is simply too high.

Edit

In order to get a complete flat result set, you can do that (based on this SO answer):

DECLARE @bonds   VARCHAR(MAX)
DECLARE @numbers VARCHAR(MAX)
DECLARE @jobs    VARCHAR(MAX)
DECLARE @projs   VARCHAR(MAX)

DECLARE @owid VARCHAR(10) = 'BTBD1004'

SELECT
    @bonds = COALESCE(@bonds + ', ', '') + aux.Descr
FROM
    Bond aux
    INNER JOIN OrderItemNumbers n
        ON n.NumberOWID = aux.OWID AND n.NumType = 0
WHERE
    n.LoadOWID = @owid

SELECT
    @numbers = COALESCE(@numbers + ', ', '') + aux.Description
FROM
    PONumbers aux
    INNER JOIN OrderItemNumbers n
        ON n.NumberOWID = aux.OWID AND n.NumType = 1
WHERE
    n.LoadOWID = @owid

SELECT
    @jobs = COALESCE(@jobs + ', ', '') + aux.Descr
FROM
    Job aux
    INNER JOIN OrderItemNumbers n
        ON n.NumberOWID = aux.OWID AND n.NumType = 2
WHERE
    n.LoadOWID = @owid

SELECT
    @projs = COALESCE(@projs + ', ', '') + aux.Descr
FROM
    Project aux
    INNER JOIN OrderItemNumbers n
        ON n.NumberOWID = aux.OWID AND n.NumType = 3
WHERE
    n.LoadOWID = @owid

SELECT
    @bonds,
    @numbers, 
    @jobs,
    @projs,
    d.OrdNbr,
    d.BillofLadingNbr,
    d.TripAndDeliveryTicketNbr
FROM 
    OrderDetail d 
WHERE 
    d.OWID = @owid

Which will result in this:

Bond       PO               Job       Project                        OrdNbr      BillofLadingNbr
Some bond  4.158 Kingsport  Some job  82001-8177-44, IME-81-1(118)   BTBD000063  BTBD000072  

It works if you get many bonds x many PO's x many jobs for the same OWID too.

IMHO that illustrates quite well what I tried to say: yes, it does what you need, but it is ugly as hell. Of course, if you don't care, go ahead (just add some triggers you'll feel like it is 1995 again :P ).

Hope it helps.

Community
  • 1
  • 1
rsenna
  • 11,775
  • 1
  • 54
  • 60
  • ok, I believe I understand everything, and thank you that all works exactly as you say. One last question before I close this questions, how would you recommend refactoring these tables so that its not "ugly as hell" which I agree with. – kingfenix Jan 08 '14 at 21:43
  • Be aware that when I said "ugly as hell" I was talking about the hack that I used to concatenate many rows into a single value. Your database is not *that* ugly. :) Now answering your question: I don't get why you need a `OrderItemNumbers` table at all. You could drop it, if each child table had a foreign key with a direct reference to `OrderDetail`. – rsenna Jan 08 '14 at 21:52
  • well the line of thinking is that each order can have multiple numbers, and that those numbers are also entities to themselves and can be attached to multiple orders... – kingfenix Jan 08 '14 at 21:56
  • this means that a number (job, bond, project or po) can be attached to any number of multiple orders, as well as any single order being able to have any combination of multiple numbers – kingfenix Jan 08 '14 at 21:57
  • the `OrderItemNumbers` table was the only way I could think to allow this freedom, is there a better way? – kingfenix Jan 08 '14 at 21:58
  • Guess I was distracted. Of course you are right, you need `OrderItemNumbers` for saving the n * n relationship. Naming style aside, you're database schema seems OK to me. – rsenna Jan 09 '14 at 11:46
  • hmmm, If I have a further question which stems from this one, do I post it as a new question? – kingfenix Jan 09 '14 at 21:09
  • Yes. You could add a link to this question, if you think the information here could be relevant to answer it. – rsenna Jan 10 '14 at 12:11