Strange join in query

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • prn
    Recognized Expert Contributor
    • Apr 2007
    • 254

    Strange join in query

    Hi Folks,

    Sorry if I have the forum wrong, It's more a SQL question than a SQL Server question, but it is intended to run on MS SQL Server at least. :)

    I'm trying to construct a query and am having trouble wrapping my head around this so I'm hoping that someone can help me get it straight.

    Here's a stripped-down version of the problem. There are 3 tables involved. (Actually 4, but I'm going to bypass one of them.)

    We have a table of people -- I'll call it CUSTOMER -- that has the usual sorts of fields, e.g., CUSTOMER_ID, LAST_NAME, FIRST_NAME, EMAIL_ADDRESS, etc. Then, for reasons that I don't pretend to understand, the guy who originally wrote the application decided to put two other descriptors into a separate table. (He called them "items".) So, we have a table ITEM_TYPE that contains the fields ITEM_TYPE_ID and NAME. This table has exactly two rows for the two item types. (For simplicity, I'm going to bypass this table in the current query.) There is then another table ITEM with the fields: ITEM_ID, ITEM_TYPE_ID and NAME. The ITEM table has about 20 rows which basically are divided between the two types and a bunch of descriptors of each type to be associated to the people in the CUSTOMER table. Then there is another table CUSTOMER_TO_ITE M to associate the customers with the descriptors in ITEM. This table has fields: CUSTOMER_TO_ITE M_ID (its primary key, which is not otherwise used), CUSTOMER_ID, ITEM_ID.

    The application that manages the data requires one descriptor from each item type for each customer. Therefore, each customer_id in the CUSTOMER_TO_ITE M table appears in two rows, once with an item of type 1 and once with an item of type 2. (Which, of course is why I would have made something like ITEM1_ID and ITEM2_ID fields in the CUSTOMER table, but that's neither here nor there since I don't really have a choice in the matter.)

    Now I need to create a report that contains, among other things, fields from the customer table itself and BOTH item descriptions (NAMEs) and here's where I'm floundering.

    Here's one version of what I've tried:
    [code=sql]
    SELECT c.CUSTOMER_ID,
    c.FIRST_NAME,
    c.LAST_NAME,
    c.EMAIL_ADDRESS ,
    i1.ITEM_ID,
    i1.ITEM_TYPE_ID ,
    i1.NAME,
    i2.ITEM_ID,
    i2.ITEM_TYPE_ID ,
    i2.NAME
    FROM CUSTOMER c inner join CUSTOMER_TO_ITE M ci
    on c.CUSTOMER_ID=c i.CUSTOMER_ID,
    ITEM i1,
    ITEM i2
    WHERE ci.item_id=i1.i tem_id
    AND ci.item_id=i2.i tem_id
    AND i1.item_type_id = 1
    AND i2.item_type_id = 2
    [/code]

    This version, as written, returns no data. If I were to remove line 16, for example, it would return thousands of rows, with a separate row for each customer with every type 2 item. If nothing else, this strongly suggests to me that my problem lies in how the joins are set up. I've tried several other ideas, trying to figure out how to inner join both i1 and i2 to ci, but I have not been able to think of one that is coherent. Everything seems to be nonsense.

    Any suggestions on how to construct this query?

    Thanks,
    Paul
  • code green
    Recognized Expert Top Contributor
    • Mar 2007
    • 1726

    #2
    You are employing a cartesian join on ITEM table twice
    Code:
    FROM    CUSTOMER c inner join CUSTOMER_TO_ITEM ci 
                on  c.CUSTOMER_ID=ci.CUSTOMER_ID,
            ITEM i1,
            ITEM i2
    I have not studied what you are trying to do but every record is being joined to ITEM twice over.
    Get rid of the commas and JOIN item ON a condition and then lets see what we have.
    By the way, it is best practice to use uppercase for SQL syntax and lower case for table and field names

    Comment

    • prn
      Recognized Expert Contributor
      • Apr 2007
      • 254

      #3
      Thanks, code_green,

      I think I'm getting closer now. If I try this query:
      [code=sql]
      SELECT customer_id, i1.item_id AS item_id_1, i2.item_id AS item_id_2
      FROM item i2
      INNER JOIN (item i1
      INNER JOIN customer_to_ite m ci
      ON i1.item_id = ci.item_id)
      ON i2.item_id = ci.item_id
      [/code]
      then I get something like this:
      Code:
      customer_id item_id_1  item_id_2  
      ----------- ---------- ---------- 
      98          1          1
      98          8          8
      120         5          5
      120         14         14
      where what I want as output is:
      Code:
      customer_id item_id_1  item_id_2  
      ----------- ---------- ---------- 
      98          1          8
      120         5          14
      What I think I need to do is to restrict the tables before the join, something like:
      [code=sql]
      SELECT customer_id, i1.item_id AS item_id_1, i2.item_id AS item_id_2
      FROM item i2 WHERE i2.item_type_id = 2
      INNER JOIN (item i1 WHERE i1.item_type_id = 1
      INNER JOIN customer_to_ite m ci
      ON i1.item_id = ci.item_id)
      ON i2.item_id = ci.item_id
      [/code]
      except, of course, that this latter is invalid SQL syntax, but it sort of reflects the semantics that I am looking for. Any suggestions?

      Thanks,
      Paul

      BTW, when I use code=sql, I'm still getting "Code: (text)" in the display. Am I doing something wrong? The Code tag FAQ says that "sql" should be a recognized code tag. ???

      Comment

      • prn
        Recognized Expert Contributor
        • Apr 2007
        • 254

        #4
        Hi again,

        I think I've finally got the right way to go about it.

        The answer appears to be to select from a couple of suitable subqueries, e.g.:
        [code=sql]
        SELECT c.customer_id,
        c.first_name,
        c.last_name,
        c.email_address ,
        i1.name AS d1,
        i2.name AS d2
        FROM customer c,
        (SELECT customer_id, name
        FROM item INNER JOIN customer_to_ite m ci ON item.item_id=ci .item_id
        WHERE item.item_type_ id = 1
        ) i1,
        (SELECT customer_id, name
        FROM item INNER JOIN customer_to_ite m ci ON item.item_id=ci .item_id
        WHERE item.item_type_ id = 2
        ) i2
        WHERE c.customer_id = i1.customer_id
        AND c.customer_id = i2.customer_id
        [/code]

        Thanks, code_green and anyone else who looked at this question.

        Paul

        Comment

        • code green
          Recognized Expert Top Contributor
          • Mar 2007
          • 1726

          #5
          I am sure this has a solution but I can't quite understand your DB structure.
          A bit too wordy.
          But I can do something with your 'illegal' query.
          Code:
          SELECT customer_id, i1.item_id AS item_id_1, i2.item_id AS item_id_2
          FROM item i2 WHERE i2.item_type_id = 2
              INNER JOIN (item i1 WHERE i1.item_type_id = 1
                  INNER JOIN customer_to_item ci 
                  ON i1.item_id = ci.item_id) 
              ON i2.item_id = ci.item_id
          What about something like
          Code:
          SELECT customer_id, i1.item_id AS item_id_1, i2.item_id AS item_id_2
          FROM item i2 
             INNER JOIN item i1 ON (i1.item_type_id = 1 AND i2.item_type_id = 2
          AND  i2.item_id = ci.item_id)
                  INNER JOIN customer_to_item ci 
                  ON i1.item_id = ci.item_id)
          Although this doesn't look like it will produce a sensible result.
          If you are joining to the same table twice 'item' it will probably need a GROUP BY

          Comment

          Working...