Как исключить строки с совпадающим соединением в дереве SQL - PullRequest
1 голос
/ 10 марта 2010

Обновление: Поработав с этим в течение нескольких часов, выбрал решение для нескольких запросов и использовал таблицу, которая содержала только родительские атрибуты, чтобы определить, какие элементы необходимо обновить.


Извините за плохое название, я не мог придумать, как кратко описать эту проблему.

У меня есть набор предметов, которые должны иметь отношение 1-к-1 с атрибутом.

У меня есть запрос, чтобы вернуть те строки, где данные неверны, и это отношение было нарушено (1-ко-многим). Я собираю эти строки, чтобы исправить их и восстановить отношения 1: 1.

Это теоретическое упрощение моей реальной проблемы, но я опубликую пример схемы таблицы здесь, как это было запрошено.

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 может содержать идентификатор другого атрибута. У этого дерева только один уровень. Пример:

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

Я не хочу получить элементы в моем исходном запросе, где для пары связанных атрибутов они связаны как атрибуты 20 и 21.

Я делаю хочу получить элементы, где:

  • атрибуты не имеют родителя
  • для двух или более атрибутов, которые они не связаны (например, атрибуты 23 и 34)

Пример желаемого результата, просто ID товара:

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

Как я могу присоединиться к attributes из items и исключить эти строки?

Использую ли я временную таблицу или могу ли я добиться этого одним запросом?

Спасибо.

Ответы [ 4 ]

0 голосов
/ 11 марта 2010

Чтобы упростить это, я обновил все строки в item, указав идентификатор родительского атрибута, где он доступен.

Итак, в моем примере таблица item с обновленными идентификаторами атрибутов выглядит так:

+------------+------------+-----------+
| 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

Мне не удалось заставить работать один запрос.

0 голосов
/ 11 марта 2010

Что ж, кажется, это невозможно с одним запросом, так как нам нечего группировать или нечего сортировать. Осталось сделать рекурсивный вызов, но так как в mysql нет рекурсивного SQL или если в данных вашего атрибута есть правило для всех связанных атрибутов attr_id

0 голосов
/ 11 марта 2010

Следующий запрос извлечет только уникальные пары элемента и атрибута (или его родителя, если таковые имеются), тем самым удаляя дубликаты (согласно вашему запросу атрибут может иметь только одного родителя, а родитель не имеет родителей).

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 атрибутами каждый.

0 голосов
/ 10 марта 2010

Вы можете достичь этого с помощью одного запроса:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...