2009-06-29 79 views
2

我正在尋找在構造查詢一些幫助。我有三個表是這樣的:在同一查詢中多次連接表?

Products 

id | (20 other columns I don't need) 
1 

product_names 

product_id | product_name 
1     my product 
2     my second product 
3     my third product 

cross_sell_products (where each product may have one, two, or no cross sell products) 

product_id | cross_sell_product_id 
1     2 
1     3 

我期待使用,這將使我的全部產品清單(他們的名字)單查詢,他們的每一個交叉銷售產品的名稱。

的問題是,所有這些信息從product_names表來了,我不知道如何執行加入不止一次在同一個表,在同一查詢中。 (希望這有意義!)

所以輸出會是這樣的:

id | product_name | cross_sell_product_1 | cross_sell_product_2 
1   my product   my second product   my third product 

欣賞任何幫助!

+0

當然 - 這是一個購物車,每個產品可以有相關的產品(有點像「誰購買此產品的客戶也對這些項目感興趣」) – 2009-06-29 11:36:10

+0

如果我按照正確的,當我們有id爲1,我們檢查cross_sell_products表並使用product_id = id(在本例中爲1)查找該表中的所有行。然後,我們要列corss_sell_product_1以具有對應於第一行的corss_sell_product_id(2在這種情況下=>我的第二個產品)的PRODUCT_NAME。然後我們繼續對第二行做同樣的事情(如果存在的話)? – 2009-06-29 11:48:52

回答

3

您別名表。你可以這樣做:

SELECT p.product_name, x.product_name 
from products p 
inner join cross_sell_products c 
    on c.product_id = c.product_id 
inner join products x 
    on c.product_id = x.product_id; 
2

要做一個表與自身之間的聯接,你必須使用自聯接。這是通過使用別名來完成的,例如,下面的「僱員」和「僱主」:

select employee.name employee, 
     employer.name manager 
    from employees employee, 
    join employees employer on employer.name = employee.manager 
; 
1

您可以通過爲其提供別名來多次添加表。例如,

SELECT 
    * 
FROM 
    MyTable AS MyTable1 
INNER JOIN 
    MyTable AS MyTable2 
ON 
    MyTable1.Id = MyTable2.Id 
0

這是非常棘手的,因爲您基本上是在旋轉cross_sell產品表。如果你有零個,一個或兩個交叉銷售的產品,你可以修改你的表有三列:

id cross_sell_product_id_1 cross_sell_product_id_2 

然後你就可以做在其他的答案中描述的更簡單連接。如果沒有,那麼做一個查詢會有點棘手 - 我不是MySQL專家,但我知道它在Oracle上!

1

我相信,我想出了一個解決問題的方法,但我不是100%肯定它的工作原理,只要你想。無論如何,這很複雜。這是一個存儲過程,它使用臨時表來翻轉記錄。它有一個我還無法克服的缺陷,它緩存臨時表的結果。我試着在SELECT語句中添加SQL_NO_CACHE,但沒有效果。下面是程序,這可能不會出現好這裏SO,這樣你就可以this GitHub gist它看一看了。要點也包含我測試表的結構和數據。任何錯誤報告或反饋非常感謝。

DELIMITER $$ 

CREATE PROCEDURE `report`() 
BEGIN 
    DECLARE col_number  INT(2)   DEFAULT 0; 
    DECLARE counter   INT(2)   DEFAULT 0; 
    DECLARE done    INT(1)   DEFAULT 0; 
    DECLARE last_prod  VARCHAR(128) DEFAULT ""; 
    DECLARE prod_name  VARCHAR(128); 
    DECLARE cross_prod_name VARCHAR(128); 
    DECLARE col_name   VARCHAR(32); 
    DECLARE create_temp_tbl TEXT; 

    -- ------------------------------------------------------------------------ 
    -- Query for fetching products and associated cross products. 
    -- ------------------------------------------------------------------------ 
    DECLARE cross_products CURSOR FOR 
     SELECT SQL_NO_CACHE 
      b.product_name, 
      c.product_name 
     FROM cross_sell_products AS a 
     INNER JOIN product_names AS b ON 
      a.product_id = b.product_id 
     INNER JOIN product_names AS c ON 
      a.cross_sell_product_id = c.product_id; 

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 


    -- ------------------------------------------------------------------------ 
    -- Find the largest number of cross products for a single product 
    -- ------------------------------------------------------------------------ 
    SELECT SQL_NO_CACHE 
     COUNT(*) AS total INTO col_number 
    FROM cross_sell_products 
    GROUP BY product_id 
    ORDER BY total DESC 
    LIMIT 1; 


    -- ------------------------------------------------------------------------ 
    -- Get rid of any instance of report_tmp. Given its structure is changing 
    -- from procedure call to procedure call, it might cause problems because 
    -- of the different number of columns it has versus the ones that we want 
    -- to insert. 
    -- ------------------------------------------------------------------------ 
    DROP TABLE IF EXISTS report_temp; 


    -- ------------------------------------------------------------------------ 
    -- Create a table with as many fields for cross products as the number 
    -- stored in col_number (which is the maximum number of cross products for 
    -- a single product). 
    -- Also, make product_name a primary key. We'll need this later in the 
    -- insertion phase. 
    -- ------------------------------------------------------------------------ 
    SET create_temp_tbl = "CREATE TEMPORARY TABLE report_temp (product_name VARCHAR(128) PRIMARY KEY, "; 

    WHILE counter < col_number DO 
     SET col_name = CONCAT("cross_sel_product_", counter); 
     SET create_temp_tbl = CONCAT(create_temp_tbl, CONCAT(col_name, " VARCHAR(128)")); 

     IF counter != col_number - 1 THEN 
      SET create_temp_tbl = CONCAT(create_temp_tbl, ", "); 
     END IF; 

     SET counter = counter + 1; 
    END WHILE; 

    SET @x = CONCAT(create_temp_tbl, ");"); 

    PREPARE stmt FROM @x; 
    EXECUTE stmt; 
    DEALLOCATE PREPARE stmt; 
    TRUNCATE TABLE report_temp; 


    -- ------------------------------------------------------------------------ 
    -- Begin fetch of products and cross products 
    -- ------------------------------------------------------------------------ 
    OPEN cross_products; 

    REPEAT 
     FETCH cross_products INTO prod_name, cross_prod_name; 

     IF NOT done THEN 
      -- ---------------------------------------------------------------- 
      -- Be sure to reset the counter every time the product group is 
      -- changing, so that we don't attempt to use more fields than 
      -- there are in the temporary table. 
      -- ---------------------------------------------------------------- 
      IF NOT prod_name = last_prod THEN 
       SET counter = 0; 
       SET last_prod = prod_name; 
      END IF; 

      -- ---------------------------------------------------------------- 
      -- For each cross product of a product, try to insert it, in case 
      -- it's not the first one in the group a key duplication error will 
      -- be reported. In this case, update the entry with a new cross 
      -- product. 
      -- ---------------------------------------------------------------- 
      SET col_name  = CONCAT("cross_sel_product_", counter); 
      SET @insert_stmt = CONCAT("INSERT INTO report_temp SET" 
            ," product_name = ?, " 
            , col_name ," = ? " 
            ,"ON DUPLICATE KEY UPDATE " 
            , col_name ," = ?"); 

      SET @prod_name  = prod_name; 
      SET @cross_prod_name = cross_prod_name; 

      PREPARE stmt_ins FROM @insert_stmt; 
      EXECUTE stmt_ins USING @prod_name, @cross_prod_name, @cross_prod_name; 
      DEALLOCATE PREPARE stmt_ins; 

      -- Go to next field 
      SET counter = counter + 1; 
     END IF; 
    UNTIL done END REPEAT; 

    CLOSE cross_products; 

    -- ------------------------------------------------------------------------ 
    -- Return desired result 
    -- ------------------------------------------------------------------------ 
    SELECT SQL_NO_CACHE * FROM report_temp; 
END $$ 

DELIMITER ;