Вопрос по реализации т.н. "универсального каталога" (уже другой вопрос)

Anky

Новичок
Вопрос по реализации т.н. "универсального каталога" (уже другой вопрос)

Здравствуйте.

Поскольку тема по ссылке окончательно сдохла, а участники того топика ушлив творческий запой по поводу других проектов, всё же подниму вопрос ещё раз. К тому же, он слегка другой.

Задача:
Сделать каталог, колонки (со свойствами товаров) в котором можно изменять из админки (то есть они не привязаны к колонкам в БД).

Реализация на первый взгляд простая: (кто знает альтернативную - расскажите, не помешает)
Три таблицы:
items (с товарами: itemID, ...)
fields (со свойствами товаров: fieldID, ...)
values (со значениями свойст товаров для каждого из товаров: itemID, fieldID, value)

И собственно всё бы ничего, но возникло пару вопросов:
1) как реализовать выборку из values нужных значений, нужных свойств и сортировку по значению (или значениям!) какого-либо из этих свойств (то есть что-то типа WHERE ... ORDER BY ..., если бы таблица была "обычная") - желательно в один запрос в БД
2) реально ли на выходе получить "обычную" структуру - тогда и первый вопрос отпадёт
При этом под "обычной" подразумевается выдача в виде:
itemID | field1 | field2 | field3 ...
Товар1 | знач.1 | знач.2 | знач.3 ...

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

p.s.> bitrix'ы и иже с ними советовать не нужно, остальная часть "каталога" очень уж специфическая и битрикс меня явно не удовлетворит.
 

Gas

может по одной?
На вскидку: сначала запросом получить id-шники товаров, которые удовлетворяют условию, отсортированы и ограничены limit'ом, а потом их сджойнить с этими же таблицами. Скорость страдать не сильно не должна - так-как доступ к данным уже был прозведён в первом запросе и во втором они должны вытянуться из кешей, а не с hdd.

Сильно не думал, это то, что сразу пришло в голову :)

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

реально ли на выходе получить "обычную" структуру
а какая разница какая структруа, всё равно код получения этих данных будет в одном месте и там уже будет формироваться удобная для работы структура данных. Да и общие для всех товаров поля можно хранить в таблице items (тоже zerkms предлагал).
 

Anky

Новичок
На вскидку: сначала запросом получить id-шники товаров, которые удовлетворяют условию, отсортированы и ограничены limit'ом, а потом их сджойнить с этими же таблицами.
То есть я правильно понимаю, что сначала получаем в php первый массив id'ников упорядоченный. Потом вторым массивом получаем те же id'ники + данные. Крутим первый массив по порядку, выбирая из второго нужные id'ники.
Если так, что такая мысль уже в голову и мне приходила. Но хочется как-то в один запрос. Может зря хочется? :) Если же делать вложенный запрос, типа WHERE ( SELECT ... ), то само собой бы упорядочеваемость теряем, что плохо.
+ на каждое условие (field1 = val1, field2 = val2 ..) получает надо по подзапросу отдельному делаьт.

Да и общие для всех товаров поля можно хранить в таблице items.
Это понятно, но задача в том, чтобы выборку/сортировку можно было делать по любым полям.

а какая разница какая структруа
Если бы оно оно формировало нормальную таблицу, то по ней можно было бы делать ORDER BY и WHERE нормальные, не мучая php :)

В целом, спасибо за ответ, хотя высказанные мысли и меня уже посищали.
 

Gas

может по одной?
зачем в php, одним запросом.

SELECT *
FROM (SELECT id FROM _здесь_все_наши_условия_join'ы_group_order_limit) as t
JOIN t.id=items.id
LEFT JOIN остальные таблицы

Не уверен что в данном случае обязательно данные вернуться в порядке derived query, тогда можно в ней ещё добавить переменную @i:=@i+1 и отсортировать уже ограниченную в запросе выборку на уровне php.
 

Anky

Новичок
Хм.. да, так можно попробывать. Только не уверен, что он их прямо так по очереди и будет сравнивать-то.
Но в любом случае попробую, спасибо за совет.
 

cDLEON

Онанист РНРСlub
bitrix'ы и иже с ними советовать не нужно, остальная часть "каталога" очень уж специфическая и битрикс меня явно не удовлетворит.
Интересный коммент...
Не припомню я что то на форуме хоть одного человека, который хоть кому-нибудь посоветывал быдликс.
 

Gas

может по одной?
Только не уверен, что он их прямо так по очереди и будет сравнивать-то
я тоже не уверен, по-этому и предложил доп. параметр, который говорит о порядке в выборке. Всё равно, после того-как данные будут получены в php, нужно будет по ним пробежаться и создать нужные объекты или массив с "правильной" структурой, так что лишнего цикла для сортировки даже не понадобиться.
Но чтоб инициализировать @i нужно или доп.запрос сначала set @i=0; или (только-что придумалось) вставить контрукцию (@i:=if(@i is null,@i:=0, 1)+@i) AS ord;
 

Anky

Новичок
cDLEON, да многоуважаемый г-н Фaнaт кому-то и посоветовал, когда этот кто-то свой каталог (правда другой направленности) пытался делать :)

Gas, а можно тогда ещё один вопрос)
"_здесь_все_наши_условия_join'ы_group_order_limit" - вот здесь насчет условий.
Поскольку у нас таблица (itemID, fieldID, value), то нельзя же просто WHERE value=... , поэтому получается надо ещё один подзапром делать? То есть что-то типа:
SELECT * FROM
( SELECT itemID FROM values WHERE itemID IN
( SELECT itemID FROM values WHERE fieldID='field1' AND value='значение1'
)
)
JOIN t.id=items.id ....

Причет такой подзапрос для каждого параметра поиска же получается.
Или как-то это можно обойти по-хитрому?
 

Gas

может по одной?
ну, например, нужно выбрать items по 2-свойствам, отсортировать по дате добавления и вывести 10 самых последних, я себе вижу это так (сложно назвать красивым решением, главное чтоб работало, но я не тестил).

Код:
SELECT поля_какие_нужны_ 
FROM
 (
   SELECT v.itemID, (@i:=if(@i is null,@i:=0, 1)+@i) AS ord 
   FROM values AS v
   JOIN items AS i ON v.itemID=i.itemID
   WHERE (v.fieldID=1 AND v.value=5) OR (v.fieldID=2 AND v.value=10)
   GROUP BY v.itemID
   HAVING COUNT(*)=2
   ORDER BY i.date_add DESC
   LIMIT 10
  ) 
AS t
JOIN items ON items.itemID=t.itemID
JOIN values ON ...
LEFT JOIN fields ON ...
 

Anky

Новичок
мм.. не будет это работать имхо:
если у нас две записи с (i.fieldID=1 AND i.value=5) и, например, ни одной с (i.fieldID=2 AND i.value=10) - он выведет два itemID

В общем, ладно, ещё раз спасибо. Главное понял в каком направлении копать ))
 

Gas

может по одной?
согласен, но можно попытаться из*нуться

SELECT v.itemID, COUNT(DISTINCT CONCAT(v.fieldID,v.value)) AS cnt, (@i:=if ....

HAVING cnt=2

:)
 

Anky

Новичок
Воооо.. через CONCAT я тоже подумывал 8)
В общем, ладно, в руки лопату и....
 

Anky

Новичок
Гы.. обалдеть, какое оказывается модное название есть 8))
надо погуглить на эту тему
отдельное спасибо itprog :)

-~{}~ 22.05.08 22:29:

В общем, group_concat спасёт EAV.
 

Gas

может по одной?
В общем, group_concat спасёт EAV.
как group_concat спасёт при поиске по условиям и сортировке ?
имхо, свёртка параметров объекта (после поиска) в одну строку, для этой задачи, не принципиальна
 

Anky

Новичок
Как при сортировке ещё не придумал, а вот для условия - в самый раз. Если 50 условий, 50 раз таблицу join'ить не надо)
Пардон, если банальность сказал.

-~{}~ 23.05.08 03:07:

Не используйте в реляционных БД дизайн "сущность-атрибут-значение"

Дизайн "сущность-атрибут-значение" (Entity-Attribute-Value, EAV) особенно популярен среди "чайников", принадлежащих к экстремальной школе разработки ПО. Ее девиз "Сначала программируй, потом думай".
Методика эта вкратце состоит в создании одной огромной таблицы с тремя столбцами метаданных: название сущности, название атрибута, значение атрибута. Это позволяет пользователям в процессе работы с БД создавать новые сущности. Если один из них хочет внести в БД сущность "батон", а другой - "булка", они оба имеют возможность это сделать.
Значения должны храниться с использованием наиболее общего типа данных, поэтому в модели EAV широко используются столбцы VARCHAR(n). Попробуйте наложить на такой столбец какое-либо ограничение.

Исключения

Нет.

Имеются лучшие инструменты для сбора данных в свободном формате.
(c) http://www.sql.ru/articles/mssql/2006/080102DataDeclarationLanguage.shtml#25

))
 

zerkms

TDD infected
Команда форума
:))))))
начнём с того, что в предложенной схеме был не варчар, а под каждый тип своё поле...

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

-~{}~ 23.05.08 10:27:

а... там просто перевод Селко
 

Anky

Новичок
Да, там просто перевод.
Альтернатива - обычные таблицы, просто под каждый объект своя, как я понял.

+ почитал forum.sql.ru - там и альтернативы, там и почему EAV зло
и вообще они все там этот EAV ненавидят 8))))
Если коротко:
EAV - много, тормозно, но универсально и есть некая унификация в плане гуёв.
ROT - быстро, правильно, но типа как-то не комфортно 8)

+ как итог, group_concat решает всё. Причем как в плане where, так и order by. Кому надо покопают и разбирутся.

Тему можно оставить для потомков, но в целом можно закрывать.
 

Gas

может по одной?
почитал ссылку,
не увидел чтоб все ненавидели EAV, для задачи со 100M записей, это скорей всего не лучший вариант, но на меньшем количестве данных этот подход используют и участники того форума.

group_concat решает всё. Причем как в плане where, так и order by.
сказал A, говори и B, в смысле пример
 
Сверху