MySQL и PHP: подсчет количества товаров в категориях.

Есть каталог товаров: товары хранятся в одной таблице БД, категории - в другой. Каждый товар привязан к той или иной категории. Категории же имеют вложенную структуру - т.е. есть "корневые", имеющие parent_id = 0, и подчиненные. Уровень вложенности категории заведомо неизвестен. Как пересчитать товар в категориях, причем категория верхнего уровня должна учитывать весь товар в своих подчиненных категориях.
Наверное, проще показать что требуется схематически:

Категория 1 (parent=0) 20 товаров (сумма товаров в подчиненных категориях)
    Категория 55 (parent=1) - 15 товаров
    Категория 56 (parent=1) - 5 товаров (количество берет из подчиненной)
        Категория 100 (parent=56) - 5 товаров
            (тут каким то образом нужно пересчитать количество записей в таблице товаров)

Категория 3 (parent=0) - 22 товара (сумма товаров в подчиненных категориях)
    Категория 5 (parent=3) - 22 товара
    Категория 12 (parent=3) - товаров нет
 

Единственный вариант, который приходит в голову, чтобы не использовать запросы в цикле — добавить поле в таблицу категорий, и при добавлении/редактировании/удалении товаров и категорий производить пересчет: например добавили товар - прибавляем единичку категории, в которую положили товар, выбираем parent_id этой категории, обновляем количество уже этой категории и так до тех пока не достигнем parent_id=0 (корневая). С категориями при изменении "предка" делаем что то похожее - сначала ищем категорию самого низкого уровня, пересчитываем товар и рекурсивный обход до категории самого высокого уровня (parent=0).
Надеюсь не слишком сумбурно :)

1 ответов


Решил все таки сделать в админке волшебную кнопку "пересчитать товар" — добавить немного AJAX и операция не будет сильно затратной, более того эта операция снимается с пользовательской части, так как данные получается хранятся в готовом виде.
Теперь есть небольшой вопрос в знатокам Zend Framework — что то я немного не соображу с составлением запроса с помощью Zend_Db. На чистом SQL запрос выглядит так:


SELECT categories.title AS title, COUNT( * ) AS count_goods
FROM categories
LEFT JOIN goods ON goods.catId = categories.id
GROUP BY categories.id
 А как это расписать используя

select()->from()->joinLeft()->group()
 

В mysql у Вас небольшой выбор при данной структуре хранения данных :
1. Как вы написали, добавить тригеры и пересчитывать количество при изменении данных
2.Использовать рекурсию , т.е. выполнять несколько запросов
а) на стороне клиента
б) на стороне сервера в хранимой процедуре используя курсоры.

Ну или переделать таблицу (например, неплохая статья про то, как хранить иерархические данные в БД : http://www.alandelevie.com/2008/07/12/recursion-less-storage-of-hierarchical-data-in-a-relational-database/


Т.к. дерево категорий небольшое его можно подгружать в массив и делать запрос вида:


SELECT  COUNT( * ) AS count_goods FROM goods WHERE goods.category IN (1,23,52,78,199)
 

Т.е. в

goods.category IN (1,23,52,78,199)
  вписывать все child_ID категории Parent_ID.

Кол-во запросов зависит от количества корневых котегорий.

Если заранее известна глубина вложенности, можно использовать ROLLUP. По ссылке довольно подробно все описано, есть примеры.

Если нет — только рекурсивно или циклом.