MySQL запрос для выбора детей - PullRequest
1 голос
/ 04 марта 2009

Я не уверен, возможно ли это в mySQL. Вот мои таблицы: -

Таблица категорий:

  • ID
  • имя
  • parent_id (который указывает на Categories.id)

Я использую приведенную выше таблицу для сопоставления всех категорий и подкатегорий.

Таблица продуктов:

  • ID
  • имя
  • category_id

category_id в таблице Products указывает на идентификатор подкатегории, к которой он относится.

например. Если у меня есть «Игрушки»> «Обучающие»> «ABC», где «ABC» - продукт, «Игрушки» - «Категория», а «Образование» - «Подкатегория», то для «ABC» будет category_id = 2.

Теперь проблема в том, что я хочу использовать SQL-запрос для отображения всех продуктов (во всех подкатегориях и их подкатегориях .. n уровне) для определенной категории.

например:.

select * from categories,products where  category.name = 'Toys' and ....

Приведенный выше запрос должен отображать продукты из Обучения также и всех других подкатегорий и их подкатегорий.

Возможно ли это с помощью запроса MySQL? Если нет, какие варианты у меня есть? Я хотел бы избежать рекурсии PHP.

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

Ответы [ 7 ]

5 голосов
/ 04 марта 2009

Я думаю, что самый простой способ добиться этого - использовать модель для вложенных множеств *1002*. Это немного сложно реализовать, но мощно использовать. В MySQL есть учебное пособие Управление иерархическими данными в MySQL . Один из крупных SQL-гуру Джо Селко писал о том же здесь . Если вам нужна дополнительная информация, посмотрите ссылки Troel о хранении иерархических данных .

В моем случае я бы отказался от использования СУБД для хранения данных такого рода и использовал бы вместо этого базу данных графов, поскольку данные в этом случае на самом деле являются ориентированными графами.

5 голосов
/ 04 марта 2009

То, что я делал в предыдущих проектах, где мне нужно было делать то же самое, я добавил две новые колонки.

  • i_depth: int значение глубины категории
  • nvc_breadcrumb: полный путь к категории в формате «хлебная крошка»

И затем я добавил триггер в таблицу, в которой хранится информация о категории, для выполнения следующих действий (все три обновления находятся в одном триггере) ...

-- Reset all branches
UPDATE t_org_branches
    SET nvc_breadcrumb = NULL,
    i_depth = NULL

-- Update the root branches first
UPDATE t_org_branches 
    SET nvc_breadcrumb = '/', 
        i_depth = 0 
    WHERE guid_branch_parent_id IS NULL

-- Update the child branches on a loop
WHILE EXISTS (SELECT * FROM t_branches WHERE i_depth IS NULL) 
    UPDATE tobA 
        SET tobA.i_depth = tobB.i_depth + 1, 
            tobA.nvc_breadcrumb = tobB.nvc_breadcrumb + Ltrim(tobA.guid_branch_parent_id) + '/' 
        FROM t_org_branches AS tobA
            INNER JOIN t_org_branches AS tobB ON (tobA.guid_branch_parent_id = tobB.guid_branch_id) 
        WHERE tobB.i_depth >= 0 
            AND tobB.nvc_breadcrumb IS NOT NULL 
            AND tobA.i_depth IS NULL

А затем просто выполните объединение со своей таблицей продуктов по идентификатору категории и выполните команду "LIKE"% / [CATEGORYID] /% '". Имейте в виду, что это было сделано в MS SQL, но это должно быть достаточно легко перевести на версию MySQL.

Возможно, он достаточно совместим для вырезания и вставки (после изменения имени таблицы и столбца).


Расширение объяснения ...

t_categories (в нынешнем виде) ...

Cat Parent  CategoryName
1   NULL    MyStore
2   1       Electronics
3   1       Clothing
4   1       Books
5   2       Televisions
6   2       Stereos
7   5       Plasma
8   5       LCD

t_categories (после модификации) ...

Cat  Parent  CategoryName   Depth   Breadcrumb
1   NULL    MyStore         NULL    NULL    
2   1       Electronics     NULL    NULL
3   1       Clothing        NULL    NULL
4   1       Books           NULL    NULL
5   2       Televisions     NULL    NULL
6   2       Stereos         NULL    NULL
7   5       Plasma          NULL    NULL
8   5       LCD             NULL    NULL

t_categories (после использования скрипта, который я дал)

Cat  Parent  CategoryName   Depth   Breadcrumb
1   NULL    MyStore         0       /   
2   1       Electronics     1       /1/
3   1       Clothing        1       /1/
4   1       Books           1       /1/
5   2       Televisions     2       /1/2/
6   2       Stereos         2       /1/2/
7   5       LCD             3       /1/2/5/
8   7       Samsung         4       /1/2/5/7/

t_products (как у вас сейчас, без изменений) ...

ID   Cat Name
1   8   Samsung LNT5271F
2   7   LCD TV mount, up to 36"
3   7   LCD TV mount, up to 52"
4   5   HDMI Cable, 6ft

Объединение категорий и товаров (где категории - C, товары - P)

C.Cat Parent CategoryName   Depth   Breadcrumb  ID   p.Cat  Name
1    NULL   MyStore         0       /           NULL NULL   NULL
2    1      Electronics     1       /1/         NULL NULL   NULL
3    1      Clothing        1       /1/         NULL NULL   NULL
4    1      Books           1       /1/         NULL NULL   NULL
5    2      Televisions     2       /1/2/       4    5      HDMI Cable, 6ft
6    2      Stereos         2       /1/2/       NULL NULL   NULL
7    5      LCD             3       /1/2/5/     2    7      LCD TV mount, up to 36"
7    5      LCD             3       /1/2/5/     3    7      LCD TV mount, up to 52"
8    7      Samsung         4       /1/2/5/7/   1    8      Samsung LNT5271F

Теперь, предполагая, что таблица продуктов была более полной, чтобы в каждой категории было что-то, а не NULL, вы можете выполнить "Breadcrumb LIKE"% / 5 /% ", чтобы получить последние три элемента последней таблицы предоставлена. Обратите внимание, что он включает в себя прямые предметы и детей из категории (например, телевизор Samsung). Если вы хотите ТОЛЬКО элементы определенной категории, просто сделайте «c.cat = 5».

1 голос
/ 05 марта 2009

Предполагая MySQL, будет трудно избежать рекурсии в PHP.

Ваш вопрос, по сути, как имитировать синтаксис Oracle CONNECT BY PRIOR в MySQL. Люди задают этот вопрос несколько раз , но это особенность, которая никогда не включалась в MySQL, и реализация с помощью хранимых процедур, вероятно, не будет работать, потому что (сейчас) хранимые функции не могут быть рекурсивный .

Остерегайтесь предлагаемых к настоящему времени кладжей базы данных.

Лучшая информация на данный момент - это три ссылки из nawroth :

1 голос
/ 04 марта 2009

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

1 голос
/ 04 марта 2009

Добавьте столбец в таблицу категорий, который будет содержать полное дерево с разделителями-запятыми для каждой группы. Используя ваш пример, подкатегория «Обучающие» будет иметь это дерево «1,2», где 1 = Игрушки, 2 = Обучающие (включая себя). Следующий вложенный уровень категорий будет продолжать добавляться в дерево.

Чтобы получить все продукты в группе, вы используете функцию MySQL FIND_IN_SET, например,

SELECT p.ID 
FROM Products p INNER JOIN Categories c ON p.category_ID = c.ID
WHERE FIND_IN_SET(your_category_id, c.tree)

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

0 голосов
/ 04 марта 2009

Терпите меня, потому что я никогда не делал ничего подобного.

BEGIN
  SET cat = "5";
  SET temp = "";

  WHILE STRCMP(temp, cat) != 0 DO
    SET temp = cat;
    SET cat = SELECT CONCAT_WS(GROUP_CONCAT(id), cat) FROM Categories GROUP BY (parent_id) HAVING FIND_IN_SET(parent_id, cat);
  END LOOP;
END;

SELECT * FROM products WHERE FIND_IN_SET(category_id, cat)

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

0 голосов
/ 04 марта 2009

Насколько велика таблица категорий? Возможно, вам придется кэшировать это на уровне приложения и создать соответствующий запрос: ... где id в (2, 3, 6, 7)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...