在同一个查询中多次连接表?

时间:2009-06-29 11:29:25

标签: sql mysql

我正在寻找一些构建查询的帮助。我有三张这样的表:

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

感谢您的任何帮助!

5 个答案:

答案 0 :(得分: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;

答案 1 :(得分:2)

要在表与自身之间进行连接,您必须使用自连接。这是通过使用别名来完成的,例如下面的“雇员”和“雇主”:

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

答案 2 :(得分:1)

您可以通过为其提供别名来多次添加表。例如

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

答案 3 :(得分: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 ;

答案 4 :(得分:0)

这非常棘手,因为您实际上是在转换cross_sell产品表。如果你有零个,一个或两个交叉销售产品,你可以修改你的表有三列:

id  cross_sell_product_id_1  cross_sell_product_id_2

然后,您可以按照其他答案中的描述进行更简单的连接。如果没有,那么进行查询有点棘手 - 我不是MySQL专家,但我知道它在Oracle上!