从mysql连接中的不同行中选择值

时间:2014-06-30 19:22:43

标签: mysql sql

我有两个表,productscategories,以及一个联接表products_categories

类别是嵌套的(通过categories.parent_id)。因此,对于任何给定的产品,它可以属于许多可能嵌套的类别。

类别的结构如下:

  • 部门(深度= 0)
    • 类别(深度= 1)
      • class(depth = 2)

每个产品都属于一个“部门”,一个“类别”和一个“类”。

因此,像“Rad Widget”这样的产品可能属于“电子”部门,“杂项”类别和“小工具”类。我的架构看起来像这样:

# products

id | name
---------------
1  | Rad Widget


# categories

id | parent_id | depth | name
--------------------------------------
1  | null      | 0     | Electronics
2  | 1         | 1     | Miscellaneous
3  | 2         | 2     | Widgets

# products_categories

product_id | category_id
------------------------
1          | 1
1          | 2
1          | 3

我想运行一个查询,将所有产品的部门列为一行,如下所示:

product.id | product.name | department  | category      | class
-----------------------------------------------------------------
1          | Rad Widget   | Electronics | Miscellaneous | Widgets

我无法想到这样做的方法,所以我正在考虑对数据进行非规范化,但我想确定我不会先错过一些东西。

2 个答案:

答案 0 :(得分:2)

由于每个类别(顺便说一下,你可能想要重命名表或级别以便“类别”并不意味着两个不同的东西)有一个单一的已知父级,但是不确定数量的未知子级,你需要从最具体的(depth = 2)“走向”到最常规的类别,在category表上为你要插入的每个附加值执行自联接。

如果您不耐烦,请跳至帖子底部的SQL Fiddle链接。如果您更愿意浏览它,继续阅读 - 它与您想要替换相应表中的数据的代理ID的任何其他情况完全没有区别。

您可以从查看所有信息开始:​​

SELECT * FROM products AS P
        JOIN
    products_categories AS PC ON P.id = PC.product_id
        JOIN
    categories AS C ON PC.category_id = C.id
WHERE P.id = 1 AN D C.depth = 2;

+----+------------+------------+-------------+----+-----------+-------+---------+
| id | name       | product_id | category_id | id | parent_id | depth | name    |
+----+------------+------------+-------------+----+-----------+-------+---------+
| 1  | Rad Widget | 1          | 3           | 3  | 2         | 2     | Widgets |
+----+------------+------------+-------------+----+-----------+-------+---------+

您要做的第一件事是识别哪些信息有用,哪些信息不有用。你不想在这里整天SELECT *。您有前两列,以及最后一列(将其识别为“类”);您需要parent_id才能找到您想要的下一列,让我们暂停depth只是为了说明。忘了剩下的,他们很杂乱。

请将*替换为特定的列名,别名为“class”,然后使用parent_id表示的数据。这些信息存储在category表中 - 您可能正在考虑,但我已经加入了该表!不在乎;再做一次,只给它一个新的别名。请记住,ON条件有点不同 - products_categories已完成其工作,现在您需要匹配C.parent_id的行 - 并且您只需要某些列来查找下一个父:

SELECT
    P.id,
    P.name,
    C1.parent_id,
    C1.depth,
    C1.name,
    C.name AS 'class'
FROM
    products AS P
        JOIN
    products_categories AS PC ON P.id = PC.product_id
        JOIN
    categories AS C ON PC.category_id = C.id
        JOIN
    categories AS C1 ON C.parent_id = C1.id
WHERE
    P.id = 1
        AND C.depth = 2;

+----+------------+-----------+---------------+---------+
| id | name       | parent_id | name          | class   |
+----+------------+-----------+---------------+---------+
| 1  | Rad Widget | 1         | Miscellaneous | Widgets |
+----+------------+-----------+---------------+---------+

再次重复此过程,为刚刚添加的列添加别名,并在下一个连接条件中使用新的C1.parent_id

SELECT
    P.id,
    P.name,
    PC.category_id,
    C2.parent_id,
    C2.depth,
    C2.name,
    C1.name AS 'category',
    C.name AS 'class'
FROM
    products AS P
        JOIN
    products_categories AS PC ON P.id = PC.product_id
        JOIN
    categories AS C ON PC.category_id = C.id
        JOIN
    categories AS C1 ON C.parent_id = C1.id
        JOIN
    categories AS C2 ON C1.parent_id = C2.id
WHERE
    P.id = 1
        AND C.depth = 2;

+----+------------+-----------+-------+-------------+---------------+---------+
| id | name       | parent_id | depth | name        | category      | class   |
+----+------------+-----------+-------+-------------+---------------+---------+
| 1  | Rad Widget | NULL      | 0     | Electronics | Miscellaneous | Widgets |
+----+------------+-----------+-------+-------------+---------------+---------+

现在我们已经完成了;我们无法在C2.parent_id = NULL上加入另一个副本,我们也会看到depth = 0,所以剩下要做的就是删除我们不想显示的列并仔细检查我们的别名。这是in action on SQL Fiddle

答案 1 :(得分:0)

如果您想要所有类别的列表,只需执行

即可
Select Distinct p.category_id, c.name
From products_categories p Join
     categories c On p.category_id = c.id
Where p.product_id = 1

问题是您将Classes和Departments放入Category表中。从技术上讲,您可以通过将每个数据移动到自己的表中来正确地规范化数据。我知道创建更多表的开销很痛苦,但它会简化您的查询(节省处理能力和潜在带宽)。