MySQL - многоуровневая база данных продуктов - PullRequest
0 голосов
/ 23 сентября 2018

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

Это может быть радиостанция типа handheld или mobile .У нас есть радиостанции, перечисленные по следующему пути:

store -> radios -> brand -> mobile / handheld -> item page

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

store -> radios -> accessory -> item page

Это полная категория ниже радиоприемников, поэтому написание выберите должно быть разным для каждой группы товаров, что меньше, чем хотелось бы.Элементы также должны быть доступны из общей родительской категории, как если бы я посетил store -> radios, я должен увидеть все элементы в этих подкатегориях.Вот текущая структура базы данных, которую я запланировал, и именно здесь у меня больше всего проблем (PS: категория идентификаторов уникальна, первична и имеет автоинкремент):

+===================================================+
| ID | Category Name  | Path            | Parent ID |
+===================================================+
| 1  | radios         | radios          | 0         |
| 2  | accessory      | misc            | 1         |
| 3  | motorola       | motorola        | 1         |
| 4  | handheld       | handheld        | 3         |
| 5  | mobile         | mobile          | 3         |
+===================================================+

Затем я устанавливаюcategory_id о продукте, чтобы сказать .. 5 для моего мобильного радио, которое я добавляю на страницу продуктов.Если я захожу на страницу продукта, мне нужно получить путь для каждой из родительских категорий для отображения на странице, например

store/radios/motorola/mobile/product-page-here.html

. Мне нужно будет построить этот путь из вышеуказанной базы данных, используя путьполе и соединение для всех родительских классов, чтобы получить правильный путь.Прямо сейчас я делаю много select * FROM tblname WHERE id = parent, а затем в цикле while этой команды выполняю другой выбор таким же образом, пока не вернусь к родительской таблице.Должен быть лучший способ сделать это, особенно если учесть, что я углубился только в 3 уровня, а клиент добавил подкатегорию глубиной в 4 уровня, и он никогда не был бы на правильном пути.

Последняя проблема заключается в том, что при использовании приведенного выше примера, если бы я вместо этого пошел к store/radios/motorola, он попытался бы выбрать любые элементы, перечисленные в category_id для 3 вместо 5, иэлемент не будет отображаться на общей вкладке motorola, поскольку вместо этого он является дочерним элементом таблицы mobile, косвенным дочерним элементом таблицы motorola.Предоставив им более 1 опции подкатегории, это сделало планирование всей этой системы огромной болью.

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

Ответы [ 2 ]

0 голосов
/ 23 сентября 2018

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

Если вы не можете установить жесткий предел для глубины вложенных категорий, это проблемы, которые, вероятно, легче решитьв коде приложения, чем SQL.Легко написать запрос, который может вытащить до заданной глубины вложенных категорий из такой таблицы.Что-то вроде:

SELECT t1.name, t2.name, t3.name FROM accessory_categories AS t1
LEFT JOIN accessory_categories AS t2 ON t2.parent_id=t1.id
LEFT JOIN accessory_categories AS t3 ON t3.parent_id=t2.id
WHERE t1.parent_id!=1
AND t1.id!=1;

Но, как вы говорите, как только кто-то создаст четвертый уровень, у вас будут проблемы.

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

<?php
//Mock category records, would come from the DB in the real world
$categoryRecords = [
    ['id' => 1, 'title' => 'Radios', 'slug'=>'radios', 'parent_id' => 0],
    ['id' => 2, 'title' => 'Accessories', 'slug'=>'misc', 'parent_id' => 1],
    ['id' => 3, 'title' => 'Motorola', 'slug'=>'motorola', 'parent_id' => 1],
    ['id' => 4, 'title' => 'Handheld', 'slug'=>'handheld', 'parent_id' => 3],
    ['id' => 5, 'title' => 'Mobile', 'slug'=>'mobile', 'parent_id' => 3],
    ['id' => 6, 'title' => 'Level 3', 'slug'=>'level-3', 'parent_id' => 5],
    ['id' => 7, 'title' => 'Level 4', 'slug'=>'level-4', 'parent_id' => 6]
];

//Create an array that maps parent IDs to primary keys
$idToParentIdMap = [];
$parentIdToIdMap = [];

foreach($categoryRecords as $currRecord)
{
    $idToParentIdMap[$currRecord['id']] = $currRecord['parent_id'];
    $parentIdToIdMap[$currRecord['parent_id']][] = $currRecord['id'];
}

/*
 * Now would be a good time to cache these maps in something like Redis or Memcache so you don't have to pull
 * the whole category table during every request.
 */


/**
 * This function will traverse the category tree and return an array of IDs belonging to all categories below
 * the category identified by the $parentId
 * @param int $parentId Primary key of category to find children for
 * @param array $childIds Pass previously found IDs for recursion
 * @return array
 */
function findChildIds($parentId, $childIds=[])
{
    global $parentIdToIdMap;

    if(array_key_exists($parentId, $parentIdToIdMap))
    {
        $immediateChildIds = $parentIdToIdMap[$parentId];

        foreach($immediateChildIds as $currChildId)
        {
            $childIds[] = $currChildId;

            $childIds = findChildIds($currChildId, $childIds);
        }
    }

    return $childIds;
}

/**
 * Return an array of parent IDs for a given category ID
 * @param int $categoryId
 * @return array
 */
function findParentIds($categoryId)
{
    global $idToParentIdMap;

    $parentIds=[];

    while(array_key_exists($categoryId, $idToParentIdMap))
    {
        $currParentId = $idToParentIdMap[$categoryId];
        $parentIds[] = $currParentId;

        $categoryId = $currParentId;
    }

    $parentIds = array_reverse($parentIds);

    return $parentIds;
}

//ID of category requested
$requestedId = 3;

/*
 * Now you can use these IDs to find all of the sub categories and products under the requested category
 */
$categoryIds = findChildIds($requestedId);
$categoryIds[] = $requestedId;

/*
 * Now you can use these IDs to get your parent categories
 */
$parentIds = findParentIds($requestedId);
0 голосов
/ 23 сентября 2018

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

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

 radios/sku1
 motorola/sku1
 mobile/sku1
 radios/Motorola/sku1
 radios/mobile/sku1
 motorola/radios/sku1
 motorola/mobile/sku1
 mobile/radios/sku1
 mobile/Motorola/sku1
 radios/Motorola/mobile/sku1
 radios/mobile/motorola/sku1
 (etc)

Если вы храните , этот путь зависит от вас - как видно выше, количество записей может быстро возрасти, но вы можете просто сгенерировать его на лету. Например,

Create table userpath (
   request int, 
   word varchar(30),
   Primary key (request, word)
);
Create table product (
   SKU int auto-increment,
   ...make, model, price, description...
);
Create table labels (
   SKU int,
   Word varchar(20),
   Primary key (SKU, word),
   Uniques key (word, SKU)
);

Чтобы разрешить элементы в пути, разбить его на слова, поместить слова в userpath, затем:

Select p.*
From products p
Join labels l
On p.sku=l.sku
Join userpath u
On l.word=u.word
Where request = ?

И вы можете получить подкатегории текущего пути из:

Select l2.word, count(*)
From labels L2
join products p
on l2.sku=p.sku
Join labels l
On p.sku=l.sku
Join userpath u
On l.word=u.word
Left join userpath U2
On l2.word=u2.word
Where u2.word is null
And request=?
Order by count(*) desc

(это можно улучшить с помощью взвешивания и метаданных)

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