Как ВЫБРАТЬ непосредственных детей и предков в одном запросе - PullRequest
6 голосов
/ 23 февраля 2010

Я работаю с древовидной структурой в MySQL, которая представлена ​​с использованием модели вложенных множеств.

Я надеюсь, что некоторые из вас, эксперты sql, могут помочь мне с построением запроса SELECT.

Я бы хотел иметь возможность сопоставлять набор узлов, используя LIKE. Для каждого сопоставляемого узла мне также нужен список предков этого узла, разделенный запятыми, и список ближайших потомков этого узла, разделенный запятыми.

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

Начиная со строки "qu" и обращаясь к таблице "Body", я получаю ...

Node      | Parent Nodes               | Immediate Children
Quads       Leg, Lower Body, Muslces     Vastus Lateralus, Vastus Medialis, Rectus Femoris
Obliques    Core, Trunk, Muscles         Inner obliques, outer obliques

Будем весьма благодарны за любые предложения о том, как сделать это без зацикливания запросов.

Ответы [ 4 ]

0 голосов
/ 23 февраля 2010

Этот вопрос намного сложнее, чем я ожидал в вашем другом посте, но я не согласен с первым постером.

Я вполне уверен, что это возможно с помощью одного запроса.

Вам нужно использовать ПОДПИСКИ и выбирать. Вы когда-нибудь смотрели действительно хорошую демонстрацию на веб-сайте mySQL о модели списка смежности.

Поскольку вы можете НРАВИТСЯ для «Узла», вы можете использовать Подзапросы для своего SQL-запроса, чтобы получить всех родителей и родителей. Один из моих запросов, по которому я сделал нечто подобное, был абсолютно массовым! Но это сработало.

Посмотрите: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

Это всего лишь небольшой фрагмент кода, который показывает, как найти непосредственных детей.

SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
    nested_category AS parent,
    nested_category AS sub_parent,
    (
        SELECT node.name, (COUNT(parent.name) - 1) AS depth
        FROM nested_category AS node,
        nested_category AS parent
        WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.name = 'PORTABLE ELECTRONICS'
        GROUP BY node.name
        ORDER BY node.lft
    )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
    AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
    AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING depth <= 1
ORDER BY node.lft
0 голосов
/ 23 февраля 2010

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

Например:

Таблица "узлы" Таблица "node_parents"

В таблице «узлы» будут храниться все узлы, а в «узле-узле» будут отображаться отношения между различными узлами.

Поэтому, когда вы выбираете LIKE для определенного узла, вы можете получить все его родители и дочерние элементы из node_parents.

Вы можете получить дополнительную информацию, используя соединения или подзапросы.

0 голосов
/ 23 февраля 2010

Хотя я согласен с Nickf, что это плохо и грязно, но все равно весело, так что вот так:

SELECT     base.left_id, base.ancestors, 
           GROUP_CONCAT(children.left_id) children
FROM       (
            SELECT     base.left_id
            ,          GROUP_CONCAT(ancestors.left_id) ancestors
            FROM       nested_set   base
            LEFT JOIN  nested_set   ancestors
            ON         base.left_id     BETWEEN ancestors.left_id 
                                            AND ancestors.right_id
            WHERE      base.name  LIKE '%criteria%'
            GROUP BY   base.left_id
           ) base                                    
LEFT JOIN  nested_set   children
ON         children.left_id BETWEEN base.left_id 
                                AND base.right_id
LEFT JOIN  nested_set   inbetween
ON         inbetween.left_id BETWEEN base.left_id 
                                AND base.right_id
AND        children.left_id  BETWEEN inbetween.left_id 
                                AND inbetween.right_id     
WHERE      inbetween.left_id IS NULL
GROUP BY   base.left_id

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

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

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

0 голосов
/ 23 февраля 2010

В одном запросе? Я бы не стал беспокоиться. SQL был бы ужасным, и, вероятно, даже не таким эффективным. Разделите каждый бит на логические меньшие запросы: сначала найдите все соответствующие узлы, затем для каждого из них дополнительную информацию, которая вам нужна.

...