Pull to refresh

Comments 65

А почему не использовать комбинацию подходов? Данные структурировано хранятся в нескольких таблицах, а для поиска / вывода листинга использовать одну большую избыточную с кучей полей.
потому что тогда вам будет труднее добавить/удалить лишнее поле. Вот у меня задача где у объекта 40 характеристик, есть задачи, когда у объекта 400 характеристик и добавляются/удаляются
Я нередко использую одну таблицу значений(плюс, естественно, таблица описаний полей), имеющих поля intValue, varcharValue, datetimeValue и т.п. для соответствующих значений
если честно, то я тоже сейчас так делаю, но по науке нужно всеже в разные.
Универсальность зло. Вы не только в скорости проигрываете, но и в «простоте» приложения.

Что может быть «с переменным кол-вом свойств»? Например, добавление нового товара в каталог Интернет-магазина, где он добавляется/редактируется через панель. Думаю у вас что-то похожее.

Так вот, я бы сделал на каждый товар по таблице. А в админке запросы пользователя переводил на alter'ы (редко товар меняет свойство, соврее добавляет новые);
Так скрость возрастет, база станет более «понятной», упрастяться sql;

А если идти вашим подходом, то в конечном итоге так всю базу можно запихать в одну таблицу (id, var_name, var_value)
неправильно вы думаете. Да и говорите вы неверно: Во первых если вы заранее не знаете по каким полям будет идти поиск, то вы не только проиграете по скорости с таблицей в 30 столбцов, но еще и отгребете проблему построения индексов. в вашем примере на товары, забавно будет посмотреть на то как вы будите по 1500 или 5к (это количество разных видов товаров) делать поиск по ограничивающему параметру. Вот у меня например автомобили, так там в комплектации постоянно что-то дабаляется, а что-то из списков выбора становится текстовым значением, что-то просто есть/нет (кондицианер, люк и пр.) Второй момент такой, что по скорости вы резко получаете выйгрыш после того как набрав статистику вы перенесете часть параметров в первычные. В конце концов убеждать я вас не собираюсь, я ж делюсь решением, а не навязываю его.
Я тоже предлагаю решение. Мне главное понять как лучше. Поехали.

1. Не знаем по каким полям будет идти поиск -> По всем
2. "… забавно будет посмотреть на то как вы будите по 1500 или 5к (это количество разных видов товаров) делать поиск по ограничивающему параметру..."
Стоп! Одинаковые свойства у разных товаров? Это типа: «Найти бампер и фару стоимосью до $30», так чтоли? Я ищу либо бампер либо фару. Или я Вас не понял.
3. Про «второй момент» не понял, можете пояснить, кто получает выйгрышь?

И еще. Сравните два sql. Необходимо найти Масло до $30 и либо Mobil либо Castrol.
SELECT * FROM objects t
  INNER JOIN int_property t2 ON t.id = t2.object_id AND t2.prop_id = 101 /* это цена */
  INNER JOIN string_property t3 ON t.id = t3.object_id AND t3.prop_id = 102 /* тип масла */
  INNER JOIN string_property t4 ON t.id = t4.object_id AND t4.prop_id = 102 /* тип масла */
  WHERE t2.value < 30
       AND (t3.value = 'Castrol' OR t4.value = 'Mobile')
====
SELECT * FROM oil
WHERE price < 30
     AND (type = 'Castrol' OR type = 'Mobile')

Что быстрее?
Взглянем немного с другой стороны на те же автомобили
Огромное количество производителей, серий и т.д. Отсюда вытекает, что необходима какая-то классификация, а не просто одна табличка под все автомобили, и мы имеем до 1000 таблиц на всё это хозяйство. И вот в таком случае, если понадобится найти все автомобили, имеющие люк, то ваша архитектура проигрывает. Я думаю это имелось ввиду при упоминании общего ограничивающего параметра.
вот я напишу, а вы не поверите опять…
ваш SELECT * FROM oil будет использовать максимум один индекс, а значит если в базе 1024 товаров, из них половина в price < 30, и 1/4 в AND type in ('Castrol','Mobile'), то будет использован индекс на поле type (у вас ведь нет индекса на price+type) собственно выделив 1/4 вы дальше по базе результату пойдете простым перебором.

в случае полной нормализации ВСЕ ждойны происходят с оператором on, который задает связь по первичному ключу + если весит индекс на value то поиск по всем полям будет вестись по бинарному дереву ибо на все эти джойны будут наложен один индекс а не несколько.

а ведь есть и поля которые чисто технические (кол-во на складе) и тогда запрос: все товары, которых менее 100 на складе и размером не более 100см будут у вас бегать по разным таблицам и гемору вы отгребете не менее чем на написание конструктора SQL запроса (что я сделал за вас)
я Вам верю, но проверю :)

Смотрите ниже, для «общих свойств» одна отдельная таблица.
так вот вы усложняете не меньше чем я. Общие свойства в одном месте. а другие в другом. а теперь просто представьте что у вас оказалось что поле цвет было частным, а стало общим. Как вы будите вводить изменение. внизу правильно сказали, что если сущностей не много и не предполагается «гуляние» структуры, то пользуйтесь обычным классическим решением, вот я знал заранее что параметров много, какие то будут удалены, какие-то добавлены и не ясно какие самые популярные для поиска
Каждое решение имеет право на существование. Гораздо ценее было бы описать плюсы и минусы всех подходов, чтобы читатель мог применить то решение, которое ему более подходит.
а вот я против сравнительного анализа в одной статье. Ибо как писал ниже: выбирать все равно вы будите и вы сами можете прочесть одно, прочесть другое и решить себе. Я ж не нанимался репортером чтоб все писать. и денег я з аэто не получу, а так просто делаю доброе дело — рассказываю чтоб и критику послушать и просто провести вечер в форуме.
Все зависит от задачи. Что если в нашем магазине тысячи товаров (например, канцелярский магазин, одних только авторучек с пол тыщи наименований)? Долбануться потом можно, лазия среди тысяч таблиц только описаний товаров.

Можно объединить таблицы по типу товара: для пол тыщи авторучек своя таблица, для сотни карандашей другая, для десятка стирательных резинок — третья. Но сколько же незаполненных полей останется: поле «понтовость» заполняется только для ручек Паркер и т.д.

А если захочется сравнить, что дороже: ручка, карандаш или тетрадка — это же какой громоздкий запрос с UNIONами получится.
Я имел виду, что для ручек одна таблица, для карандашей своя, для резинок третья.

А если захочется сравнить, что дороже: ручка, карандаш или тетрадка — это же какой громоздкий запрос с UNIONами получится.

Надо смотреть глубже в архитектуру приложения. Если такое подразумевается (сравнивать карандаши и ручки), тогда бы я вынес общие свойства в отдельную таблицу «товар» (с ценой и цветом, напрмер). Получается некое наследование свойств.
Не буду спорить, вы правильно говорите: надо смотреть в архитектуру приложения. Разработчик БД обязан знать какие данные будут храниться в БД, сколько их, какова интенсивность использования (select, update, insert). Абстрактно рассуждать какой метод лучше — все равно что вычислять площадь поверхности сферического коня в вакууме.
нуу если честно то как правило мы реально узнаем задачу только сделав половину решения
developer не надо в общении категорично все воспринимать, надо вести конструктивную беседу, а не резать оппонента на полуслове и говорить, что он неправ.

Вы оба говорите правильные вещи, но о том как вы это говорите и как воспринимаете друг друга зависит ход решения и конечная истина общения.
Вобщем за общение:
ArtemS +
developer — (в целом за статью большой ПЛЮС)

вот 5 человек заминусовало топик… Зачем спрашивается? что тут плохого именно? бывают топики какие-то хорошие — им плюс ставят, бывают плохие — им минус ставят. Ну ставишь минус — утруди себя работой объясни, а тупо пробигая "-" лепи. Такое ощушение что все индусы, которые не врубаются лепят минусы
С моей точки зрения, топик написан довольно сумбурно. Много опечаток и пропущенных запятых. Сразу видно, вы очень торопились поведать миру о своем изобретении :-)

Кроме того, этот метод далеко не нов, хотя наверняка кому-нибудь будет полезен. К тому же он довольно спорен. Тут уже предлагали использовать широкую таблицу. Если объектов не слишком много, лучше предпочесть именно её. Чтобы быть объективным, не помешало бы рассмотреть проблему со всех сторон.

Мне лично статья понравилась — мне интересна тема SQL и хотелось бы чтобы на Хабре чаще обсуждали базы данных.
вообще то да лучше рассмотреть несколько вариантов, тока выбирать все равно вы будите и вы сами можете прочесть одно, прочесть другое и решить себе
Это особенность EVA =)

Убогие, которые понимают, что они убогие со своими нормализациями, не хотят люто признавать.
Сорри: признавать, что они убогие, и что классические реляции давно отжили своё.
Вот и придираются.
Я с таким «решением» сталкиваюсь каждый день на работе.
При очевидных плюсах (добавление/удаление свойств) я получил кучу минусов.

Первый затык встал на проверке входных данных. Пришлось писать «общую» форму для редактирования стойств объектов (здесь я имею ввиду указать длину, выставить цвет), для того чтобы проверки свести в одно место.
Но потом выяснилось что, товар «А» может быть зеленый и черный, а товар «B» черный, розовый и синий. Проверка превратилось в «непереводимую игру букв»

Второй затык встал при поиске. На каджое условие пользователя приходилось делать inner join, а кол-во условий доходило до 20 (значение как правило %Mobile%, которые не позволяли использовать индексы)

Вот таких «затыков» еще было много, но мне их здесь не описать, они достаточно часные.

Все это объясняется «универсальностью» предложенного метода, а если возникает какое-нибудь часное правило, можно смело иди вешаться.
а вы пытаетесь применить метод к не той проблеме. Вот посмотрите мою задачу (80к автомобилей, 47 параметров) и вы поймете, преимущества.
а кстати у вас все ограничения в таблицу класть. id нименования, поле, возможные переходы.
Спасибо за напоминание об интересной схеме…
Но, думается мне, как бы ни была хороша подобная схема — желание к ней прибегнуть говорит о недостатке приложенных усилий на этапе проектирования.

На практике, при решении подобных задач всегда находится набор свойств (и как правило, доминирующий по своему количеству), общий для всех обрабатываемых объектах твоей системы. и этот набор всегда целесообразно вынести в отдельную таблицу (или две, три… возможно с использованием справочников).
Если речь о том, что на этапе проектирования у тебя уже есть рабочая модель, а тебе нужно менять базовые свойства — так используй ALTER TABLE.
Попытка написать универсальный велосипед для езды под водой и для езды по горам — это всегда попытка портатить излишние силы на разработку. Конкретная задача — конкретное решение.

Известно: 15 минут с карандашом экономят 1 час кодинга.

Конечно, это высказывание к языку SQL, или его конструкциям не имеет отношения… но может быть полезно тем, кто им пользуется.
=) уж тебе ли не знать, сколько времени я отвожу проектированию?
вот видно — не внимательно читал:
>а практике, при решении подобных задач всегда находится набор свойств…
они попадают/качуют в первичные, написано же:
«выделить первичные (наиболее используемые в поиске) и вторичные свойства сущности, по первичным свойствам создаем таблицу»
еще раз повторюсь: если ты например осваиваешь новый рынок, то не знаешь какое поле будет наиболее употребительно для поиска.
второе: уневивирсальное решение — хороший метод для анализа.

вот сделаешь ты мега читаемую таблицу на 40 столбцов, а окажется она заполена на 20%, будут ли у тебя индексы работать? нет.
Про первичные свойства — меня сбила с мысли следующая фраза
> |id| meta data — все первичные сущности|

По делу — я согласен, что для описания остальных свойств подход удобный, хотел лишь обратить внимание, что если это касается всего набора свойств, то желание использовать универсальный метод говорит о недостатке проработанности задачи, и следует на это обратить внимание.
Впрочем каждый имеет право на свое мнение. Некоторые до сих пор ООП не признают, и с радостью вам докажут чем ООП плох.
главное понимать что ООП это не серебряная пуля :)
Увы, НО при разработки больших систем и часто модифицируемых — ООП уже давно стало серебренной пулей.
отчего же увы? имхо очень даже приятно писать и выражать мысли в абстракциях
UFO just landed and posted this here
ну вот я например сталкнулся с автомабилями, я заранее не знал какие там выделить классы и тд и тп.
Чувствую, будет холивар и много минусов %)

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

Вот интересная статья на эту тему steve-yegge.blogspot.com/2008/10/universal-design-pattern.html

Кстати у нас, такая таблица(за десять лет) уже переросла за миллиард строк, в каждом объекте от 20 до 100 параметров, 100 000 операций в день, 400+ пользователей и это на сервере всего за 10 килобаксов.

Так что возможные проблемы с производительностью не стоит преувеличивать раньше времени.

hint: взять по индексу object_id 20 записей и выбрать из них 3, может быть гораздо быстрее, чем сбегать за ними три раза используя индекс в трех разных таблицах
100 000 операций в день, имеется ввиду, новых объектов
в общем то у меня в сутки происходит заливка 10к новых обектов, а вся база крутится на предмет поиска и все летает. вы совершенно верно подметили, что производительность на уровне.
Опаньки, каталог auto.ru сливаешь? ;)
За сколько ты продашь такую базу с рабочими скриптами, сумму в личку.
А для какого SQL, если не секрет? Я понимаю, что здесь универсально, я имею ввиду на каком испытывалос Вами?

У меня была задача сделать универсальное хранение сущностей — сделал аналогично. Потом на хранимках написал какое-то подобие API -т.е. addProp / remProp и т.д. В том числе и поиск. Делал на PgSQL.
Работало очень шустро, просто интересно как на других SQL-ях?

Кстати говоря на практике потом, как выяснилось — не очень нужно. Гораздо более простой и удобной оказалось использование вот такой штуки — каждый объект имел свою собственную таблицу со своими свойствами, была таблица, представлявшая собой связь «тип объекта» => «таблица объекта» ну и остальное все от этого плясало.

Возможно что изменение такой структуры более долгое, т.к. требует alter, но на практике, во всяком случае в моем варианте, это требовалось крайне редко.
На МуSQL я тестировал на таблице в 200 000 сущностей, по 40 параметров у каждой случайным образом заданных. поиск по любой комбинации параметров производится в пределах 0.0001-0.2 сек
Вполне. а тесты хранимками были или внешкой?
тесты конечно скриптами внешними. Ну как я описал генератор SQL был, но время генерации SQL ничтожно. 0.2 сек это при обширных запросах, когда в реззультате слишком много записей получается + какой-нибудь group by.

если же у вас ограничения по поиску выделяют всего 1/100 или даже 1/20 таблицы, то все летает ибо используется праймари индекс
Можно добавить еще плюсов:

Полиморфизм for free. Разные объекты имеющие набор одинаковых свойств, могут обрабатываться одним и тем же запросом. Иногда это бывает вобще одно свойство. Наследование таблицами в этом случае солидный оверхед.

Поддержка истории атрибутов Классический пример — национальность, раньше это было параметром паспорта, теперь нет. В старых объектах, мы это поле сохраняем, в новых оно просто отсутствует. В обычных таблицах, приходится либо игнорировать еще одну ставшую ненужной колонку, либо заморачиваться с выносом ее в отдельную таблицу и переделывать код.
Удобство автогенерации формы для ввода параметров: т.е. создается описание, о том какие параметры для конкретной категории объектов используются. И когда оператор хочет добавить новый объект в базу, то форму параметров уже генерит сама админка исходя из описаний для конкретной категории.
django и rails прекрасно генерят формы для моделей мапящихся на нормальные таблицы
Не самое удачное решение. Практика показала, что наиболее простым и удобным для разработки является схема 1 сущность — 1 таблица. Сравнительный анализ приводить не буду — на работе нет времени:)
А Постгрес кроме многомерных массивов умеет хранить хеши… и делать поиск по ним, вроде как с индексами…
Кто нибудь сталкивался?
А цифры результатов тестирования в сравнении с альтернативными вариантами посмотреть можно? А то пустой холивар получается — я бы на сложности синтаксиса запросов вообще бы не заморачивосля, потому что обычно это все обернуто в API и генерируется автоматом (по крайней мере я так обычно делаю). А вот скорость — это тема для обсуждения.
вот о сравнении не скажу, потому что мне нужно было именно это решение, потому как я плохо заранее представлял хранимые сущности, а не из за выигрыша в скорости. Но думаю что если вы заранее знаете поля, которые используются в поиске, то на широкой таблице и суммарном индексе можно получить существенный выигрыш, но я не знал ни популярных полей для поиска, ни собственно изначально полного их списка.
Подход нормальный и правильный, применительно к этой конкретной задаче.

Умников, говорящих про 1 сущность = 1 таблица не слушайте, они не понимают о чем говорят.
У них «автомобиль» «телевизор» — разные сущности, тогда как на самом деле это просто сущность «товар» с переменными параметрами.

Разные сущности — это например «пользователь» и «товар». Хотя, если пользователь и есть товар… ;)
Т.о. количество характеристик ограничено количеством JOIN'ов, которое возможно использовать в SQL-запросе (32?). Для большего количества параметров необходимо делать дополнительные запросы.
а можно поподробнее про ограничение на количество join'ов?
Увы, никаких подробностей, только сухие цифры:

  • MySQL 3.23 – 31 таблица;
  • MySQL 4.0 – 61 таблица;
  • MySQL 4.1 – 61 таблица;
  • MySQL 5.0 – 61 таблица;
  • MySQL 5.1 – 61 таблица;
  • MySQL 6.0 – 61 таблица.

Мануал.
а тут в sql джоинятся только 3 таблицы для 3х типов данных, каждая по многу раз.
Похоже, все в порядке, и такого ограничения на кол-во параметров не будет.
Если честно не скажу как на самом деле — не проверял и в потолок не упирался. обычно поиск идет у меня с указанием 5-9 параметров.
На сколько я в курсе, MySQL'у без разницы, одна и та же таблица джоинится или разные. Т.е. лимит в 61 таблицу будет работать и для одной таблицы, которую заджоинили 61 раз.
А что можно почитать на тему вот таких не стандартных схем хранения данных в БД?

У меня, впринципе, не сложная задача: есть несколько типов похожих объектов, но каждый из которых отличается двумя-тремя своими характеристиками (полями, которые есть лишь у данного типа объектов). Пока у меня только три типа объектов и я использую по таблице под каждый тип объектов. А в будущем, конечно, хотелось бы универсальности.
Замер данной методы на MySQL:

Метод замечательный и я его использовал 2-3 года назад на сайте mobi.kz, НО когда записей и параметров становиться реально очень много и они не помещаются в одной таблице на одном жестком диске, то тут начинаются бешенные проблемы, а как собственно масштабировать систему?

Т.е. при всем желании вы уже не сможете засунуть в предложенную архитектуру, например с market.yandex.ru все их товары с миллиардами атрибутов.

Таким образом, данный подход удобен, если известно, что кол-во данных не будет превышать размер свободного места на диске. Можно конечно воспользоваться Партишин'гом в MySql 5.1 но далеко не уплывем, производительность будет крайне низка, хотя на «костылях» сможем сохранить любой объем.

Поэтому, если вы точно уверены что данных будет, дофига (и на 1 винт не влезут) — начинайте смотреть в сторону MapReduce.
на самом деле все маштабируется: просто разбивается по пулам ID объектов
но вы правы потолок настает в N раз быстрее, где N — среднее число параметров у сущности, но если у вас не милионы таких сущностей, а сотни тысяч, то можно не париться
UFO just landed and posted this here
«Все уже украдено до нас» (с)
Для интересующихся — гуглить по строкам EAV и модель Тенцера.

Основной минус такой технологии — производительность. Падение производительности от порядка до 20, 30 раз из-за отсутсвия оптимизаций СУБД по такой модели хранения, отсутсвия индексов по двум и более полям, проблем чтения при количестве записей от ста тысяч и т.д.
Основной плюс — однотипность разработки для таких задач, как: переменное колиество атрибутов объекта, хранение истории объекта, однотипная обработка изменения объекта, однотипный подход к аудиту и т.д.

Для MSSQL и ORACLE рекомендую рассмотреть возможность хранения данных в XML полях.
верно:
Entity-Attribute-Value, Сущность-атрибут-значение (EAV)
хехе, надо срочно дописать свою статейку по поводу таких ворпосов новичков :)
Sign up to leave a comment.

Articles