【问题标题】:How to exclude rows where matching join is in an SQL tree如何排除匹配连接在 SQL 树中的行
【发布时间】:2010-03-10 18:03:38
【问题描述】:

更新:在玩了几个小时后,使用多查询解决方案并使用仅包含父属性的表来确定哪些项目需要更新。


抱歉标题不好,我想不出如何简洁地描述这个问题。

我有一组应该与属性具有一对一关系的项目。

我有一个查询来返回那些数据错误并且这种关系已被破坏(一对多)的行。我正在收集这些行来修复它们并恢复这种一对一的关系。

这是对我的实际问题的理论上的简化,但我会按照要求在此处发布示例表架构。

item表:

+------------+------------+-----------+
| item_id    | name       | attr_id   |
+------------+------------+-----------+
| 1          | BMW 320d   | 20        |
| 1          | BMW 320d   | 21        |
| 2          | BMW 335i   | 23        |
| 2          | BMW 335i   | 34        |
+------------+------------+-----------+

attribute表:

+---------+-----------------+------------+
| attr_id | value           |  parent_id |
+---------+-----------------+------------+
|   20    | SE              |         21 | 
|   21    | M Sport         |          0 |
|   23    | AC              |         24 |
|   24    | Climate control |          0 |
              ....
|   34    | Leather seats   |          0 |
+---------+-----------------+------------+

返回具有多个属性的项目的简单查询。

SELECT item_id, COUNT(DISTINCT(attr_id)) AS attributes 
FROM item GROUP BY item_id HAVING attributes > 1

这让我得到这样的结果集:

+-----------+------------+
|   item_id | attributes |
+-----------+------------+
|    1      |          2 |
|    2      |          2 |
|    3      |          2 |
        -- etc. --

但是,有一个例外。 attribute 表可以通过表中的父链接保存树结构。对于某些行,parent_id 可以保存另一个属性的 ID。这棵树只有一层。示例:

+---------+-----------------+------------+
| attr_id | value           |  parent_id |
+---------+-----------------+------------+
|   20    | SE              |         21 |
|   21    | M Sport         |          0 |
              ....

不想在我的原始查询中检索项目,其中,对于一对关联的属性,它们的相关性类似于属性 20 和 21。

确实想要检索以下项目:

  • 属性没有父属性
  • 对于两个或多个不相关的属性(例如属性 23 和 34)

所需的示例结果,只是项目 ID:

+------------+
| item_id    |
+------------+
| 2          |
+------------+

如何从items 加入attributes 并排除这些行?

我是使用临时表还是可以通过单个查询来实现这一点?

谢谢。

【问题讨论】:

  • 您能否为“项目”添加示例数据和结构,以及它在所有 3 个场景中与“属性”的关系:干净数据(1 对 1)、脏数据(1 对很多),还有例外情况?
  • 这些都是对实际系统的理论上的简化。回家后我会添加一些示例。
  • 这棵树只有 1 级,还是 n?如果 n,我认为您需要一个递归解决方案 - 这使得它更难。
  • 你能把要求的结果和你放另一张桌子一样吗?
  • 是的,树只有 1 级。我添加了一个示例所需的结果集。问题已更新。

标签: sql mysql tree join


【解决方案1】:

以下查询将仅提取唯一的项目和属性对(或其父项,如果有的话),从而消除重复项(这是根据您的要求,属性只能有一个父项,而父项没有父项)。

SELECT DISTINCT I.item_id AS iid, A.par_id AS aid
FROM 
    items AS I, 
    (SELECT AA.attr_id, IF(AA.parent_id = 0, AA.attr_id, AA.parent_id) AS par_id 
     FROM attribute AS AA) AS A
WHERE I.attr_id = A.attr_id
ORDER BY I.item_id

因此,将上述查询用作计数查询的子表将起作用(与上面的 A 子表使用的方法相同):

SELECT SUB.iid, COUNT(DISTINCT(SUB.aid)) AS attributes
FROM
    (SELECT DISTINCT I.item_id AS iid, A.par_id AS aid
     FROM 
        items AS I, 
        (SELECT AA.attr_id, IF(AA.parent_id = 0, AA.attr_id, AA.parent_id) AS par_id 
         FROM attribute AS AA) AS A
     WHERE I.attr_id = A.attr_id
     ORDER BY I.item_id) AS SUB
GROUP BY SUB.iid
HAVING attributes > 1

我在您的示例项目表中添加了 3 行,以适应这种情况,其中项目只能链接到具有父项的属性,但不能链接到父项本身(即项目 3 -> 23 和 3 -> 20 ) 和 4 -> 23.

运行上述查询仅列出项目 2 和 3,每个项目有 2 个属性。

【讨论】:

    【解决方案2】:

    您可以通过单个查询来实现:

    SELECT
        i.item_id,
        COUNT(DISTINCT(i.attr_id)) AS attributes 
    FROM
        items i
    INNER JOIN
        attributes a
            ON i.attr_id = a.attr_id
    WHERE
        a.parent_id = 0
    GROUP BY
        i.item_id
    HAVING
        i.labels > 1
    

    【讨论】:

    • 我以为我也可以这样做,但是...排除了其中 1 个相关属性具有父级但...父级不在给定项目的关联属性集中的行。目前数据不是那么干净。
    【解决方案3】:

    好吧,一个查询似乎是不可能的,因为我们没有什么可以分组的,或者没有什么可以排序的。 剩下的一件事是进行一些递归调用,但由于 mysql 中没有递归 SQL,或者如果您的属性数据有一个规则,所有链接属性 attr_id

    【讨论】:

      【解决方案4】:

      为了简化这一点,我用父属性 ID 更新了 item 中的所有行,其中一个可用。

      所以在我的示例item 表中,属性 ID 更新后看起来像:

      +------------+------------+-----------+
      | item_id    | name       | attr_id   |
      +------------+------------+-----------+
      | 1          | BMW 320d   | 21        |
      | 1          | BMW 320d   | 21        |
      | 2          | BMW 335i   | 23        |
      | 2          | BMW 335i   | 34        |
      +------------+------------+-----------+
      

      首先我得到了一个属性关系列表(子到父):

      SELECT a.attr_id, a.parent_id FROM item i JOIN attribute a 
      USING (attr_id) WHERE parent_id > 0 GROUP BY a.attr_id
      

      我在代码中循环并更新了item 中引用子属性的行。

      $update = array();
      
      foreach ($relations as $child => $parent) {
          if (!isset($update[$parent]))
              $update[$parent] = array();
      
          $update[$parent][] = $child;
      }
      

      循环 $update 以更新 item。完成此操作后,我就可以使用我的原始查询:

      SELECT item_id, COUNT(DISTINCT(attr_id)) AS attributes 
      FROM item GROUP BY item_id HAVING attributes > 1
      

      我无法让一个查询工作。

      【讨论】:

      • 我刚刚编辑了我的答案并使用了一个(复杂的:))查询。看看吧:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-20
      • 2015-07-08
      • 1970-01-01
      相关资源
      最近更新 更多