views:

77

answers:

2

I have three tables

Prospect -- holds prospect information

id
name
projectID

Sample data for Prospect

id | name | projectID
1  |  p1  |  1
2  |  p2  |  1
3  |  p3  |  1
4  |  p4  |  2
5  |  p5  |  2
6  |  p6  |  2

Conjoint -- holds conjoint information

id
title
projectID

Sample data

id | title  | projectID
1  | color  |  1
2  | size   |  1
3  | qual   |  1
4  | color  |  2
5  | price  |  2
6  | weight |  2

There is an associative table that holds the conjoint values for the prospects:

ConjointProspect
id
prospectID
conjointID
value

Sample Data

id | prospectID | conjointID | value
1  |      1     |      1     |   20
2  |      1     |      2     |   30
3  |      1     |      3     |   50
4  |      2     |      1     |   10
5  |      2     |      3     |   40

There are one or more prospects and one or more conjoints in their respective tables. A prospect may or may not have a value for each conjoint.

I'd like to have an SQL statement that will extract all conjoint values for each prospect of a given project, displaying NULL where there is no value for a value that is not present in the ConjointProspect table for a given conjoint and prospect.

Something along the lines of this for projectID = 1

prospectID  | conjoint ID  | value
    1       |      1       |   20
    1       |      2       |   30
    1       |      3       |   50
    2       |      1       |   10
    2       |      2       |   NULL
    2       |      3       |   40
    3       |      1       |   NULL
    3       |      2       |   NULL
    3       |      3       |   NULL

I've tried using an inner join on the prospect and conjoint tables and then a left join on the ConjointProspect, but somewhere I'm getting a cartesian products for prospect/conjoint pairs that don't make any sense (to me)

SELECT p.id, p.name, c.id, c.title, cp.value
FROM prospect p
INNER JOIN  conjoint c ON p.projectID = c.projectid
LEFT JOIN conjointProspect cp ON cp.prospectID = p.id
WHERE  p.projectID = 2
ORDER BY p.id, c.id

prospectID  | conjoint ID  | value
    1       |      1       |   20
    1       |      2       |   30
    1       |      3       |   50
    1       |      1       |   20
    1       |      2       |   30
    1       |      3       |   50
    1       |      1       |   20
    1       |      2       |   30
    1       |      3       |   50
    2       |      1       |   10
    2       |      2       |   40
    2       |      1       |   10
    2       |      2       |   40
    2       |      1       |   10
    2       |      2       |   40
    3       |      1       |   NULL
    3       |      2       |   NULL
    3       |      3       |   NULL

Guidance is very much appreciated!!

A: 

Your cartesian product is a result of joining by project Id - in your sample data there are 3 prospects with a project id of 1 and 3 conjoints with a project id of 1. Joining based on project id should then result in 9 rows of data, which is what you're getting. It looks like you really need to join via the conjointprospects table as that it what holds the mapping between prospects and conjoint.

What if you try something like:

SELECT p.id, p.name, c.id, c.title, cp.value
FROM prospect p
LEFT JOIN conjointProspect cp ON cp.prospectID = p.id
RIGHT JOIN conjoint c ON cp.conjointID = c.id
WHERE  p.projectID = 2
ORDER BY p.id, c.id

Not sure if that will work, but it seems like conjointprospects needs to be at the center of your join in order to correctly map prospects to conjoints.

Ryan Elkins
Thanks Ryan -- I had thought the same thing -- put the ConjointProspect table in the "middle". I've tried your suggestion but it unfortunately only returns the rows that exist in the ConjointProspect table. It appears to be the equivalent of "SELECT p.id prospectID, p.name, c.id conjointID, c.title, cp.valueFROM prospect p, conjoint c, conjointprospect cpWHERE c.id = cp.conjointID AND p.id = cp.prospectID AND p.projectID = 2ORDER BY p.id, c.id"thanks for taking the time to respond
TomH
+1  A: 

Then this will work for you... Prejoin a Cartesian against all prospects and elements within that project via a select as your first FROM table. Then, left join to the conjoinprospect. You can obviously change / eliminate certain columns from result, but at least all is there, in the join you want with exact results you are expecting...

SELECT 
       PJ.*,
       CJP.Value
    FROM 
       (   SELECT 
                   P.ID ProspectID,
                   P.Name,
                   P.ProjectID,
                   CJ.Title,
                   CJ.ID ConJointID
               FROM 
                   Prospect P,
                   ConJoint CJ
               where 
                       P.ProjectID = 1
                   AND P.ProjectID = CJ.ProjectID
               ORDER BY 
                   1, 4
         ) PJ
         LEFT JOIN conjointProspect cjp 
             ON PJ.ProspectID = cjp.prospectID 
            AND PJ.ConjointID = cjp.conjointid
     ORDER BY 
        PJ.ProspectID,
        PJ.ConJointID
DRapp
Excellent! Works as advertised. I wasn't aware that there could be more than one condition in the ON clause. That being the case, I can also extract the desired records with an inner join on prospect and conjoint and then left join the conjointprospect using the same ON clause used in your solution. "SELECT p.id prospectID, p.name ProspectName, c.id conjointID, c.title conjointTitle, cp.valueFROM prospect pINNER JOIN conjoint c ON p.projectID = c.projectIDLEFT JOIN conjointprospect cpON cp.conjointID = c.id AND cp.prospectID = p.idWHERE p.projectID = 2ORDER BY p.id"Thanks DRapp!!
TomH