Запатентованная мечта программиста — часть II

Краткая предыстория: моя прошлая заметка описывала подход к хранению и выборке данных, на котором можно построить конструктор приложений — альтернативу современным платформам разработки, но без необходимости программирования. Изобретение это потенциально может перевернуть весь мир IT, каким мы его знаем.


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


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




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


Есть только одна тонкость: в статье представлена не EAV как таковая


Самый популярный вопрос был: «чем это отличается от EAV, KV, Magento...».


Казалось бы, отличия заключаются во всем что не укладывается в перечисленные свойства:


  1. Структура — в таблице 5 колонок и 3 индекса
  2. Способ выборки — в одной таблице описываются типы данных и их взаимосвязи (мета-данные) и сами данные

Но такой ответ не устроил многих читателей, поэтому постараюсь объяснить более развернуто.


Описанная структура использует справочник EAV, дополненный атрибутом ID, так как любой атрибут также является самостоятельной сущностью, могущей иметь свои атрибуты, равно как и использоваться в качестве справочного значения. То есть, структура предназначена не только как EAV-справочник, почему, собственно, я и не могу назвать это EAV.


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


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



Нужно проверить выполнение следующих условий для сравниваемой системы:


1. Все данные хранятся в одной таблице (см. также п.3.), содержащей как минимум следующие поля: ID, Parent ID, Type ID, Value

2. Для таблицы построены как минимум следующие индексы: ID; Type ID + Value; Parent ID + Type ID.

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

4. Таблица содержит описание типов данных

5. Таблица содержит описание реквизитов типов данных (из набора описанных в ней же типов)

6. Таблица содержит объекты данных с сылкой на их тип (из набора описанных в ней же типов)

7. Таблица содержит реквизиты объектов с сылкой на родителя и тип

8. Выборка объектов производится с обязательным указанием типа объекта

9. Выборка реквизитов объекта производится с обязательным указанием родителя и типа

10. Выборка объекта по его реквизитам производится с обязательным указанием типа реквизита

11. (необязательно) Объекты связаны через реквизиты, содержащие в качестве типа ID связываемого объекта

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


Более существенные вопросы ставят под сомнение заявление о производительности системы с ростом объема


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


В комментариях к прошлой заметке была предложена простая структура связанных данных, пригодная для тестирования. Её я и взял за основу: это список из 1048552 книг 142654 авторов, сгенерированных из случайных данных.


Структура выглядит так (нажмите, чтобы посмотреть)
CREATE TABLE `author` (
  `id` int(11) NOT NULL,
  `author` varchar(128) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `books` (
  `id` int(8) NOT NULL,
  `name` varchar(256) NOT NULL,
  `author` int(8) NOT NULL,
  `pages` int(4) NOT NULL,
  `year` int(4) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `author`
  ADD PRIMARY KEY (`id`),
  ADD KEY `author` (`author`);

ALTER TABLE `books`
  ADD PRIMARY KEY (`id`),
  ADD KEY `name` (`name`(255)),
  ADD KEY `author` (`author`);

ALTER TABLE `books`
  ADD CONSTRAINT `books_ibfk_1` FOREIGN KEY (`author`) REFERENCES `author` (`id`);

В конструкторе эта схема данных выглядит так:



Тестирование проводилось на самом простом сервере: 1 ядро @2.4GHz, 1GB RAM, диск SSD.


Базы с индексами занимают 207 МБ в классической базе данных и 289 МБ в таблице конструктора.


Показать
Классика


Конструктор


Для справедливости я буду делать серию запросов, поочередно в одной и другой базе, и замерять время.


Первый предложенный оппонентом тест выглядел так: выбрать все книги, имя авторов которых содержит заданный текст. Для подобного запроса не может быть использован индекс по имени автора.


Запрос к базе получился такой:


SELECT author.author, books.name, books.pages, books.year 
FROM books, author 
WHERE author.author LIKE '%aro%' AND books.author=author.id 
LIMIT 1000

Этот же запрос в конструкторе выглядит так (указан фильтр по автору и задано подсчитать их количество):



Имя автора содержит символы «aro», и под это условие попадает 465 книг в нашей базе.


Запрос к таблицам вернул такой результат за 193 мс:



План выполнения запроса получился такой:



Конструктор вернул тот же набор за 266 мс:



Следует заметить, что за эти 266 мс конструктор выполнил 6 запросов к своей базе (проверил токен пользователя и его права, нашел мета-данные отчета, выполнил отчет). Собственно отчет выполнился за 264 мс.


В базу был отправлен такой запрос:


SELECT a225.val v1_225,a217.val v2_217,a223.val v3_217,a219.val v4_217
FROM test a225
 LEFT JOIN (test r217 JOIN test a217 USE INDEX (PRIMARY)) ON r217.up=a217.id AND a225.id=r217.t AND a217.t=217
 LEFT JOIN test a223 ON a223.up=a217.id AND a223.t=223
 LEFT JOIN test a219 ON a219.up=a217.id AND a219.t=219
WHERE a225.up!=0 AND a225.t=225 AND a225.val LIKE '%aro%'
LIMIT 1000

Такой был его план выполнения:



База, можно сказать, «холодная», поэтому время выполнения, должно быть, преувеличено.


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


Таблицы Конструктор Разница в разах
0,1927 0,2643 1,37
0,1175 0,1965 1,67
0,0777 0,1268 1,63
0,1178 0,0983 0,83
0,0626 0,1131 1,81
Среднее: 1,46

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


Главное, что следует уяснить: при обсуждаемом подходе ядро системы не будет производить полный скан таблицы


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


Проведем еще несколько тестов, учитывающие другие типовые случаи из жизни


Обычно поиск осуществляется с использованием индеска, например, по первым символам имени автора.


Перепишем запрос следующим образом:


SELECT author.author, books.name, books.pages, books.year 
FROM books, author 
WHERE author.author LIKE 'lac%' AND books.author=author.id 
LIMIT 1000

В традиционной базе этот запрос выполнился за 3.1 мс:



В конструкторе все запросы для генерации страницы выполнились за 8.4 мс, из которых запрос отчета занял ровно 6 мс:



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


Посмотреть планы запросов

В обычной базе данных:



В конструкторе:




Сделаем несколько аналогичных выборок наугад, занося результаты в таблицу (время в миллисекундах):


Условие Кол-во записей Таблицы Конструктор Разница в разах
LIKE'Le%' 1001 11,1 48,5 4,37
LIKE 'lac%' 108 3,1 6,0 1,94
LIKE 'Lean%' 66 2,7 8,2 3,04
LIKE 'dac%' 49 3,9 2,6 0,67
LIKE 'rac%' 30 2,5 3,2 1,28
LIKE 'nac%' 18 2,4 2,8 1,17
= 'Волков' 6 3,9 1,5 0,38
= 'John' 6 2,7 1,1 0,41
Среднее: 1,66

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


Также будет интересно протестировать выборку по более многочисленной категории — книгам


Применим фильтр к названию книги: поиск подстроки. Перепишем запрос так:


SELECT author.author, books.name, books.pages, books.year 
FROM books, author 
WHERE books.name LIKE '%лые пар%' AND books.author=author.id 
LIMIT 50

Аналогичный запрос создадим в конструкторе:



Запустив эти два отчета увидим большую разницу во времени их выполнения: 148 мс в обычной базе и 2490 мс в конструкторе. Разница почти в 17 раз!


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



Перезапустим оба отчета и опять увидим большую разницу во времени выполнения: 181 мс в обычной базе и 25 мс в конструкторе. Теперь, наоборот, конструктор отработал в 7 раз быстрее.


Для чистоты эксперимента зададим порядок выборки в обычной БД:


SELECT author.author, books.name, books.pages, books.year 
FROM books INNER JOIN author ON books.author=author.id
WHERE books.name LIKE '%лые пар%' 
LIMIT 50

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


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


Логика организации данных такова, что конструктор будет исполнять запросы так же, без полного сканирования и с использованием индексов, при любом объеме данных. На действительно больших данных понадобится принимать аналогичные меры, как в традиционной БД.


Напоследок сравним достаточно типичный случай: пользователь хочет найти книги определенного года и (или) с заданным количеством страниц. Вот наш запрос для этого:


SELECT author.author, books.name, books.pages, books.year 
FROM books, author 
WHERE books.pages=150 AND books.year=1972 AND books.author=author.id 
LIMIT 50

А так такой запрос выглядит в конструкторе:



Результаты различаются очень сильно, более чем в 30 раз в пользу конструктора:




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


Здесь мы плавно возвращаемся к вопросу, что это и зачем оно: это основа простого в освоении конструктора, позволяющего снизить риск лавинообразной деградации производительности.


Конструктор дает более удобное средство работы с данными: сравните обычные запросы DDL и DML в этой статье с тем как это делается в конструкторе.

Поделиться публикацией
Комментарии 158
    +3
    Затраты на обслуживание системы в зависимости от ее сложности.

    Сразу же вопрос — по каким данным построен этот график? Предоставьте данные исследований, пожалуйста. Графики, нарисованные от балды, не являются доказательством чего-либо, особенно в статье про отличие от существующих решений.


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

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


    Казалось бы, отличия заключаются во всем что не укладывается в перечисленные свойства:
    1. Структура — в таблице 5 колонок и 3 индекса
    2. Способ выборки — в одной таблице описываются типы данных и их взаимосвязи (мета-данные) и сами данные

    Ага, только я спрашивал об этом, и вы сказали, что дополнительные столбцы и таблицы являются модификацией вашего патента.


    — То есть если кто-то сделает аналогичную структуру, только будет хранить значения типов по разным таблицам (отдельно целые числа, отдельно строки, ...), это будет другая система, и он не нарушит патент?
    А если кто-то уже сделал структуру данных с аналогичными столбцами, только в разных таблицах, или с дополнительными столбцами, это другая система или ваш патент недействительный?
    — С точки зрения патентного права, оба случая закрыты этим патентом.

    Кроме того, вы не можете запрещать патентом ставить кому-то произвольные индексы. Потому что эта функциональность в СУБД разработана не вами. Вот если бы вы свой тип индекса написали, тогда могли бы его запатентовать.


    Описанная структура использует справочник EAV, дополненный атрибутом ID

    В любой реляционной базе данных есть столбец, аналогичный ID — первичный ключ сущности. Следовательно, в любой реализации EAV с хранением данных в РБД такой столбец есть. Значит описанная структура не может быть им "дополнена".


    так как любой атрибут также является самостоятельной сущностью, могущей иметь свои атрибуты

    Entity-Attribute-Value переводится как Сущность-Атрибут-Значение. В таком виде можно хранить любые сущности, в том числе и сущность "Тип данных".


    То есть, структура предназначена не только как EAV-справочник, почему, собственно, я и не могу назвать это EAV.

    Пока что никаких отличий от EAV нет.


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

    Вы написали веб-интерфейс для управления данными. Интерфейсы с таким назначением существовали и до вас. Например, та же Magento, или любой графический SQL-клиент.


    Нужно проверить выполнение следующих условий для сравниваемой системы

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


    Что насчет существующих систем с полями "ID, Parent ID, Type ID, Value", включая их переименование?
    В Magento можно найти такие поля:
    catalog_product_entity_varchar.entity_id — соответствует полю "Parent ID" в секции данных на схеме из вашего патента.
    catalog_product_entity_varchar.attribute_id — соответствует "Type ID"
    catalog_product_entity_varchar.value — соответствует "Value"
    catalog_product_entity_varchar.value_id — инкрементный идентификатор, соответствует "ID"

      0
      Сразу же вопрос — по каким данным построен этот график? Предоставьте данные исследований, пожалуйста. Графики, нарисованные от балды, не являются доказательством чего-либо, особенно в статье про отличие от существующих решений.

      Это иллюстрация, картинка. Там нет чисел, а только отражена гипотеза.
      В статье работа гипотезы рассматривается на примере: в каких-то случаях производительность обычной БД внезапно в 30 раз хуже, чем в конструкторе.

      Кроме того, вы не можете запрещать патентом ставить кому-то произвольные индексы. Потому что эта функциональность в СУБД разработана не вами. Вот если бы вы свой тип индекса написали, тогда могли бы его запатентовать.

      Кто вам чего запрещает? Цель этого всего — как раз сделать публичное свободное решение и развивать его, о чем я сейчас пытаюсь договориться с потенциальными партнерами.
      Патент защищает решение от того, чего вы опасаетесь, если я правильно вас понял.

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

      Вы опять рассуждаете о структуре, а это только 3 пункта из приведенных мной 10.
        0

        (здесь только про график, про остальное будет отдельный комментарий)


        Это иллюстрация, картинка.

        Иллюстрация чего? Ваших желаний?


        Там нет чисел, а только отражена гипотеза.

        Какая гипотеза? Как она формулируется? Каким формальным способом она проверяется?


        В статье работа гипотезы рассматривается на примере: в каких-то случаях производительность обычной БД ~~внезапно ~~в 30 раз хуже, чем в конструкторе.

        Вот только этот случай к вашей гипотезе отношения не имеет, потому что объем данных не изменился. Более того, немного выше по тексту есть случай, где производительность конструктора внезапно в 17 раз хуже. Этот пример в вашей гипотезе как отражен?

          0
          Вот только этот случай к вашей гипотезе отношения не имеет, потому что объем данных не изменился.

          Изменилась Сложность — добавился некий отчет, не повлиявший на объем, но увеличивший нагрузку на ресурсы.

          Более того, немного выше по тексту есть случай, где производительность конструктора внезапно в 17 раз хуже. Этот пример в вашей гипотезе как отражен?

          Этот пример отражен в начале графика — изначально конструктор требует в разы больше ресурсов.
          Причина конкретно этого случая известна и её легко устранить, как я показываю в примере.
            0
            Изменилась Сложность — добавился некий отчет, не повлиявший на объем, но увеличивший нагрузку на ресурсы.

            Какая у вас удобная "гипотеза" — можно трактовать ее параметры любым образом.


            Этот пример отражен в начале графика — изначально конструктор требует в разы больше ресурсов.

            … а где показано, что потом — не требует?


            Причина конкретно этого случая известна и её легко устранить, как я показываю в примере.

            Причину описанного вами проседания реляционной БД тоже легко устранить. Пример надо, или сам прекрасно понимаете?

              0
              Причину описанного вами проседания реляционной БД тоже легко устранить. Пример надо, или сам прекрасно понимаете?


              Да, давайте сравним легкость на примере. Я устранил причину в 1 клик, переместив колонку отчета.
                0
                Я устранил причину в 1 клик, переместив колонку отчета.

                Я устранил причину в один клик ("создать ключ" в SSMS), только при этом у меня еще и структура отчета никак не поменялась (т.е., эта оптимизация будет работать для любого отчета).

                  0
                  Это вы у себя дома в один клик. А в работающей системе вы сначала пройдете Change control и запланируете это в какой-то релиз.
                  То есть, кроме пользователя, которому это нужно, привлечете еще некоторое количество людей и времени.
                    0

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


                    Иными словами, я могу сделать это без управления изменениями, могу сделать с управлением, и это определяется только моими административными политиками, а не функциональностью системы.

                      0
                      Если вы хотите сказать, что у вас это можно сделать без change control, то вы показываете недостаток, а не достоинство вашей системы.

                      Что это?
                      У меня не нужно под каждый отчёт создавать индексы, в чем и есть суть.
                      Вы же предлагаете кликать в SSMS, значит, с политиками или без, вы рискуете накликать проблемы, и должны как минимум иметь доступ и разбираться в SSMS.
                        0
                        Что это?

                        Изменить характеристики используемого в продакшне решения.


                        У меня не нужно под каждый отчёт создавать индексы

                        Зато у вас нужно создавать отчет под каждый вид фильтрации… или нет?


                        Вы же предлагаете кликать в SSMS, значит, с политиками или без, вы рискуете накликать проблемы,

                        Вы так говорите, как если бы в вашем решении нельзя было "накликать проблемы". Что мешает пользователю поменять порядок колонок с оптимального на неоптимальный?


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

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

                          +1
                          Ничем не отличается от «должны иметь доступ и разбираться в конструкторе».

                          Отличается как раз уровнем риска. Сравните: «сделать неоптимальный запрос к базе» или «создать/удалить индекс в production системе».
                            0

                            Вы уже ввели формальное определение уровня риска?

                              +2

                              Просто для примера: мы с полгода назад разбирали кейс, когда интеграционное решение делало "неоптимальные запросы к базе" и тем самым полностью парализовало работу системы. Реально полностью, новые запросы вообще не обслуживались.

                                0
                                Да, можно сделать такую базу. Вы это к чему пример привели, вы ее сами делали?
                                  0

                                  Я этот пример привел к тому, что начиная с определенного момента "неоптимальные запросы к БД" — это проблема, и с ними приходится бороться. И, что характерно, это существенно большая проблема, чем неправильные индексы, потому что индексы-то мы ставим по результатам профилирования, а вот запрос пользователь придумывает сам, и все способы выстрелить в ногу у него так просто не отберешь.

            +1
            Там нет чисел, а только отражена гипотеза.
            Там есть поведение графиков. При гипотезу там ничего не написано, есть только утверждения «Затраты в случаях А и Б соотносятся так». Доказательств этого нет.

            Кто вам чего запрещает?
            Само существование патента. И ваше описание попадающих под него модификаций.

            Патент – это исключительное право на изобретение. Патент наделяет своего владельца правом решать, каким образом изобретение может – и может ли – использоваться другими людьми.
            Какого рода охрану обеспечивает патент?
            В принципе, патентовладелец имеет исключительное право препятствовать коммерческому использованию запатентованного изобретения либо удерживать других от такого использования. Иными словами, патентная охрана означает, что изобретение не может изготавливаться, использоваться, распространяться, ввозиться или продаваться другими лицами в коммерческих масштабах без согласия патентовладельца.

            Вы опять рассуждаете о структуре, а это только 3 пункта из приведенных мной 10.
            Так ответьте на вопрос. Если хоть одно условие не соответствует, значит ли это, что это уже другая система, а не модификация вашей?
            И нет, о структуре БД там все 11 пунктов. Везде присутствуют слова «таблица», «выборка», или «связь».

            Есть только одна тонкость: в статье представлена не EAV как таковая.
            Так собственно, в чем отличия от EAV? То, что вы написали в статье, присутствует в EAV в том или ином виде. У вас просто одна из возможных реализаций.
              0
              Само существование патента. И ваше описание попадающих под него модификаций.

              Поверьте, вас это никоим образом не затронет, так что нет причин переживать.

              Так собственно, в чем отличия от EAV? То, что вы написали в статье, присутствует в EAV в том или ином виде. У вас просто одна из возможных реализаций.

              У вас сейчас на руках столько же информации, сколько имею я.
              Да, EAV в решении присутствует. Все на этом?
                0
                Поверьте, вас это никоим образом не затронет, так что нет причин переживать.

                Извините, я вам не верю. Во-первых, потому что в прошлой статье всеми силами открещивались от модели EAV, а оказалось что она присутствует. А во-вторых, потому что патент это официальный документ, а комментарии в интернете юридической силы не имеют. В текущем виде он позволяет патентный троллинг. Это можете сделать вы, ваш коллега, или кто-то посторонний, кто сделает аналогичный патент, используя ваш как прецедент — "у меня тоже своя система, ему можно, а мне что, нельзя?"


                Да, EAV в решении присутствует. Все на этом?

                Как минимум надо явно обозначить модификации, которые не являются нарушением патента. Я вас между прочим 2 раза уже спросил, но вы почему-то игнорируете. Если учитывать то, что вы писали ранее, то этому соответствует много существующих систем и систем, которые может создать кто-то в будущем, но кто о вашей системе ни разу не слышал.

                  0
                  Извините, я вам не верю. Во-первых, потому что в прошлой статье всеми силами открещивались от модели EAV, а оказалось что она присутствует.

                  Я отрицал, что запатентована EAV. И сейчас придерживаюсь этого.

                  А во-вторых, потому что патент это официальный документ, а комментарии в интернете юридической силы не имеют.

                  Тут всё не так просто. Но в целом, совершенно не важно что я вам тут отвечу. Есть документ, объективная реальность, не зависящая от комментариев о нем в интернете.

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

                  Как же я игнорирую, если я вам ответил и своими словами, и предоставил первоисточник?
                  Также у вас есть чек-лист, и вы можете применить его к любой модификации и проверить её на совпадение.
                    0
                    Есть документ, объективная реальность, не зависящая от комментариев о нем в интернете.

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


                    Также у вас есть чек-лист, и вы можете применить его к любой модификации и проверить её на совпадение.

                    Этот чек-лист будет принят при рассмотрении патентного спора?

                      0
                      Проблема в том, что — для специалиста — трактовка этого документа совершенно неоднозначна. Я, если честно, вообще расстроен, что у нас можно такое патентовать.

                      Для специалиста? Давайте покажем специалисту, послушаем его.
                      Вы обвиняете в непрофессионализме людей и целые организации — ФИПС и канадскую компанию, проводившие патентный поиск и содействующие в составлении формулы. При этом вы же не стесняетесь заявить, что ничего не понимаете в патентном праве.

                      Хотя я, пожалуй, постою в сторонке в вашем споре с ними.
                        0
                        Для специалиста? Давайте покажем специалисту, послушаем его.

                        Вы как бы в профессиональном сообществе и находитесь.


                        Вы обвиняете в непрофессионализме людей и целые организации — ФИПС и канадскую компанию, проводившие патентный поиск и содействующие в составлении формулы.

                        Разве обвиняю?


                        При этом вы же не стесняетесь заявить, что ничего не понимаете в патентном праве.

                        В патентном праве — не понимаю. В разработке ПО, однако же, вполне понимаю.

                          0
                          Вы как бы в профессиональном сообществе и находитесь.

                          Спалились на «как бы».

                          Там вполне однозначная трактовка в формуле. Язык несколько необычен, что смущает по первости, и неудивительно. Но неоднозначностей там нет, если читать внимательно и аккуратно.
                            0

                            Ну, если вы предлагаете читать только формулу изобретения (в которой, кстати, ничего нет про индексы)… то выяснится, что вы запатентовали не готовое решение, а "способ формирования электронной базы данных" (я по формуле же и цитирую). Как это соотносится с вашим же "я же рассказываю о законченном, самодостаточном решении, которе не требует ничего дополнительного для создания структуры, данных и управления ими"?

                              0
                              Я вам предлагаю ответить за свои слова и указать на неоднозначно трактуемые места в тексте
                                –1

                                Во всем тексте патента или только в его формуле?

                                  0
                                  В неоднозначных местах, где вы их видите
                                    +1

                                    Srsly.


                                    Вот вам, например, неподтвержденное утверждение:


                                    Техническим результатом заявляемой группы изобретений является повышение надежности базы данных за счет исключения ошибок при добавлении новых данных и формировании запросов и отчетов, а также снижения риска лавинообразной деградации производительности из-за отсутствия необходимых индексов при работе с большими объемами данных (десятки и сотни миллионов записей).

                                    Туда же:


                                    Разработанная единожды заявляемая система [...] исключает стороннее вмешательство, сводя на нет ошибки, которые могут возникнуть при добавлении, изменении и удалении данных, что обеспечивает высокую надежность и стабильность системы.

                                    А вот типичный пример "implementation-defined":


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

                                    А вот фраза, которая неоднозначно соотносится с вашим чеклистом:


                                    Реализации заявляемой группы изобретений имеют по меньшей мере один из вышеупомянутых объектов и/или аспектов, но необязательно содержат все из них.

                                    Или вот прекрасные бланкетные нормы:


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

                                    Напоследок еще два голословных утверждения, и мне надоело:


                                    Основная проблема информационных систем — это растущая цена их поддержки при возрастании сложности системы, причем зачастую затраты на разработку и сопровождение растут экспоненциально [...] Заявляемая группа изобретений используют уже готовый программный код и архитектурное решение [...] обеспечивая универсальность системы, в которой зависимость затрат на сопровождение от сложности системы будет не хуже линейной.

                                    (дадада, это тот самый график сверху, он же "фиг 8")

                                      0
                                      Ага, понятно.
                                      То есть вас эти формулировки огорчают, а к описанию технического решения про поля и связи претензий нет. То есть, там всё однозначно.

                                      Только непонятно, чем вас вот это не устроило:
                                      А вот типичный пример «implementation-defined»:

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

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

                                        Мне, знаете ли, искренне лень разбираться, где в этом патенте описание технического решения, а где — что. Я изначально и написал, что меня расстраивают документ и факт патента при таком описании в документе.


                                        Ну да, порядок колонок и строк может быть любым. Что здесь не так?

                                        Формулировка "человеку… понятно" (да, я знаю, что я выше сам такую же употребил, но я тут все-таки в публичной беседе, а не в патентной заявке).

                                          0
                                          Мне, знаете ли, искренне лень разбираться, где в этом патенте описание технического решения, а где — что. Я изначально и написал, что меня расстраивают документ и факт патента при таком описании в документе.

                                          Это чистый слив. Как вы можете утверждать про неоднозначность, если вам лень разобраться?
                                          Так и пишите, что вам лень.
                                            0
                                            Как вы можете утверждать про неоднозначность, если вам лень разобраться?

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

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

                                                Я понимаю, что аргументы ad hominem использовать проще, чем отвечать по существу, но, повторюсь, я ничего не понимаю в теме патентного дела. А вот в разработке ПО — вполне. Или ваш патент не имеет к этому никакого отношения?

                      0
                      Я отрицал, что запатентована EAV

                      Вы отрицали, что ваше решение это EAV.
                      https://habr.com/post/358934/#comment_18709843
                      "Это не EAV, поэтому подразумеваемых вами недостатков тут нет."


                      Есть документ, объективная реальность, не зависящая от комментариев о нем в интернете.

                      Ага, я об этом и говорю. Документ этот описывает широкий круг существующих систем, и как следствие, их будущих аналогов. Значит создает возможности для патентного троллинга.


                      Например: "Понятно, что специалисты в данной области техники могут разработать различные устройства, которые, хотя они явно не описаны или показаны здесь, тем не менее, воплощают принципы заявляемой группы изобретений и включены в ее сущность и сферу применения."


                      Как же я игнорирую, если я вам ответил и своими словами, и предоставил первоисточник?

                      Я задал простой вопрос: "Если хоть одно условие не соответствует, значит ли это, что это уже другая система, а не модификация вашей?". Вариантов ответа всего 2 — да или нет (впрочем каждый из них порождает другие вопросы). Вы ни один не сказали.


                      Также у вас есть чек-лист, и вы можете применить его к любой модификации

                      Мне неясно, как трактовать результаты применения. Допустим, у меня поля называются не parent_id, type_id, а abc_id, def_id, а все остальное то же самое. В чек-листе нет таких названий. Значит ли это, что у меня другая система, не ваша?

                        0
                        Ага, я об этом и говорю. Документ этот описывает широкий круг существующих систем, и как следствие, их будущих аналогов. Значит создает возможности для патентного троллинга.

                        Укажите хоть одну систему, удовлетворяющую опроснику. Прямо по всем пунктам. Не по названиям полей, а по принципу хранения мета-данных и данных.

                        Я задал простой вопрос: «Если хоть одно условие не соответствует, значит ли это, что это уже другая система, а не модификация вашей?». Вариантов ответа всего 2 — да или нет (впрочем каждый из них порождает другие вопросы). Вы ни один не сказали.

                        Чек-лист сопровожден примечанием — должны выполняться 10 условий.

                        Мне неясно, как трактовать результаты применения. Допустим, у меня поля называются не parent_id, type_id, а abc_id, def_id, а все остальное то же самое. В чек-листе нет таких названий. Значит ли это, что у меня другая система, не ваша?

                        Имена полей не важны, важно их значение.
                          0
                          Укажите хоть одну систему, удовлетворяющую опроснику. Не по названиям полей, а по принципу хранения мета-данных и данных.
                          Имена полей не важны, важно их значение.
                          Я уже указал в прошлой статье — Magento. Раскидываем данные по системным таблицам, получаем рабочую систему. Семантика (значение) полей абсолютно аналогична. Перенос поля из одной таблицы в другую можно считать переименованием. Так как оно не считается, значит принцип хранения у Magento такой же.

                          По пунктам
                          1, 3. В Magento несколько таблиц. Семантика полей совпадает с «ID, Parent ID, Type ID, Value», примеры я написал в первом комментарии. Практически у всего есть «идентификатор, родительский элемент, значение», а «тип элемента» может задаваться вынесением в таблицу специально для этого типа. Ничего не мешает и столбец сделать с одинаковым значением.

                          2. ID это первичный ключ, индекс на него есть во всех системах.
                          Индекс Parent ID + Type ID есть (entity_id + attribute_id в catalog_product_entity_varchar, attribute_set_id + attribute_id в eav_entity_attribute).
                          Индекса Type ID + Value нет в таблицах для товаров, но есть в других (attribute_id + value в eav_entity_*, entity_id + attribute_id + value в customer_address_entity_*) (пруф), а так же его может добавить администратор системы после установки.

                          4. С учетом п.3 формулировка становится такой «Одна из нескольких таблиц сходной структуры содержит описание типов данных». Очевидно, это утверждение верно для любой EAV-системы, так как в ней описание типов хранится в таблицах.

                          5, 6, 7. С учетом п.3 аналогично — «одна из нескольких таблиц». В какой-то по-любому это хранится, а структуру любой можно подвести под семантику «ID, Parent ID, Type ID, Value».

                          8. Тип конечно надо в любой EAV указывать.
                          9. attribute_set_id — родитель, catalog_product_entity_int — тип в виде специальной таблицы, также хранится в eav_attribute.backend_type.
                          10. Так как значения атрибутов хранятся в специальных таблицах для типов, то естественно выборка объектов по значению атрибутов требует указывать тип, в виде названия этой таблицы.
                            0
                            Прокомментировал те пункты, где вы явно сами признаете несовпадение.
                            Это как если бы вы рассуждали, что поезд — это мотоцикл, потому что на колесах и ездит. Только нужно доработать немного.

                            1, 3. В Magento несколько таблиц. Семантика полей совпадает с «ID, Parent ID, Type ID, Value», примеры я написал в первом комментарии. Практически у всего есть «идентификатор, родительский элемент, значение», а «тип элемента» может задаваться вынесением в таблицу специально для этого типа. Ничего не мешает и столбец сделать с одинаковым значением.

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

                            2. ID это первичный ключ, индекс на него есть во всех системах.
                            Индекс Parent ID + Type ID есть (entity_id + attribute_id в catalog_product_entity_varchar, attribute_set_id + attribute_id в eav_entity_attribute).
                            Индекса Type ID + Value нет в таблицах для товаров, но есть в других (attribute_id + value в eav_entity_*, entity_id + attribute_id + value в customer_address_entity_*) (пруф), а так же его может добавить администратор системы после установки.

                            Так пусть добавит, что же он раньше не добавлял?

                            5, 6, 7. С учетом п.3 аналогично — «одна из нескольких таблиц». В какой-то по-любому это хранится, а структуру любой можно подвести под семантику «ID, Parent ID, Type ID, Value».

                            Сначала подведите так, чтобы заработало.

                            10. Так как значения атрибутов хранятся в специальных таблицах для типов, то естественно выборка объектов по значению атрибутов требует указывать тип, в виде названия этой таблицы.

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

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


                              а «тип элемента» может задаваться вынесением в таблицу специально для этого типа
                              Вот когда зададут и сделают, тогда и будет о чем говорить. Пока же эти пункты не выполняются.

                              Ну так уже задали. Вынесли в таблицы catalog_product_entity_*.


                              нет в таблицах для товаров, но есть в других

                              А слова есть в других вы типа не заметили? Там вообще-то тоже EAV. У вас нигде нет требования, чтобы это было только для товаров.


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

                              В смысле? Раньше (до установки) он добавить не мог, потому что системы у него не было.
                              Я вот например поставил и уже добавил.


                              Сначала подведите так, чтобы заработало.

                              Слова "подвести под смысл" не могут "работать". Результат может соответствовать смыслу слов в патенте или не соответствовать. В Magento можно найти поля структуры EAV, данные в которых имеют такой смысл.


                              И да, уже "подводил" и уже заработало. Я описывал это здесь.


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

                              Это вяжется с хранением его же в eav_attribute.backend_type, по нему и определяется, к какой таблице обращаться.
                              И если вы подумаете, как сделать п.3, то поймете, что у вас будет точно так же.

                                0
                                Уважаемый, вы же сами видите, что в Magento эти пункты могли бы выполниться, если что-то добавить и смотреть с некоего угла, хотя это никто не пытался проверить на практике.

                                Вот, например:
                                Только переименовать поля и разнести по таблицам

                                При принципиально идентичных структурах переносить ничего не нужно: вы просто обращаетесь к полям под другими именами в нужных таблицах и все. Грубо говоря, берем ядро Интеграла или Magento и переписываем одно под другое (переименовываем поля и таблицы).
                                Не получается? Значит структуры и подход разные.

                                И да, уже «подводил» и уже заработало. Я описывал это здесь.

                                Заработало? Там даже статическая картинка Договора не похожа на оригинал.

                                Возьмите простейший замусоленный здесь пример с книгами и авторами, загрузите в Magento и выполните серию запросов, которые здесь приведены. Сразу поймете, насколько далеки Magento и Интеграл в архитектурном плане.
                                  +1
                                  Уважаемый, вы же сами видите, что в Magento эти пункты могли бы выполниться, если что-то добавить и смотреть с некоего угла

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


                                  (переименовываем поля и таблицы). Не получается? Значит структуры и подход разные.

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


                                  Там даже статическая картинка Договора не похожа на оригинал.

                                  А в вашем чек-листе нет ни слова про интерфейс.
                                  Причем здесь вообще внешний вид? Его можно какой угодно сделать. Даже используя ваш код, с расширениями браузера типа Stylish. Если я скачаю движок Интеграла и слегка поменяю HTML+CSS, это не нарушит патент? Думаю, нарушит. Значит интерфейс в сравнении участвовать не должен.


                                  Возьмите простейший замусоленный здесь пример с книгами и авторами

                                  Я взял пример из патента, зачем мне брать другой пример? Но ок, позже попробую.

                                    0
                                    А в вашем чек-листе нет ни слова про интерфейс.
                                    Причем здесь вообще внешний вид? Его можно какой угодно сделать. Даже используя ваш код, с расширениями браузера типа Stylish. Если я скачаю движок Интеграла и слегка поменяю HTML+CSS, это не нарушит патент? Думаю, нарушит. Значит интерфейс в сравнении участвовать не должен.


                                    Я не про интерфейс говорю, а про содержание: значения полей Договор, Сумма, Предмет, Заказчик. Где у вас всё это?
                                      0

                                      В патенте есть данные только для одного договора, данных заказчика нет. Значения атрибутов "Предмет договора" и "Цена" есть на скриншоте.

                                        0
                                        Там каждый тип термина фигурирует в единственном экземпляре, поэтому Заказчик упомянут только ссылкой на него, так как это равноценный Договору тип.
                                        Я дал вам ниже картинку, чтобы проще было.
                                      0
                                      Возьмите простейший замусоленный здесь пример с книгами и авторами

                                      Я взял пример из патента, зачем мне брать другой пример? Но ок, позже попробую.

                                      Не забудьте здесь рассказать про результат, пожалуйста.
                                      Статья опубликована, в основном, для этого — подтверждение или опровержение наличия полных архитектурных аналогов.
                                        0
                                        Да, было некогда пока. Разобрался только с обработкой ссылок на объекты. Меня смущает запрос с JOIN в скобках.

                                        Подскажите, как у вас обрабатывается кейс, когда у сущности есть 2 ссылки на одну таблицу? Например, у книги есть автор и рецензент из числа других авторов, или оператор и менеджер у заказа из таблицы сотрудников. Какой запрос будет для примера из статьи с книгами? Попробовал сделать по аналогии, получаются дубликаты.
                                          0
                                          Если нужно присоединить одну сущность с разными целями, то можно использовать подобие ALIASа, как в обычном SELECT.
                                          Например, как это сделано в демо-кладре, где есть отчет с ссылкой на одну и ту же таблицу как на город, республику или регион (см.колонку Alias):


                                          Будет, к примеру, такой запрос:
                                          SELECT a171.val v1_171,a194.val v2_194,a202.val v3_202,a207.val v4_202,a197.val v5_194,a181.val v6_171,a171_region.val v9_171,a171_area.val v12_171 
                                          FROM kladr a171 
                                          LEFT JOIN (kladr r194 JOIN kladr a194 USE INDEX (PRIMARY)) ON r194.up=a194.id AND a171.id=r194.t AND a194.t=194 
                                          LEFT JOIN (kladr r202 JOIN kladr a202 USE INDEX (PRIMARY)) ON r202.up=a202.id AND a194.id=r202.t AND a202.t=202 
                                          LEFT JOIN kladr a207 ON a207.up=a202.id AND a207.t=207 
                                          LEFT JOIN kladr a197 ON a197.up=a194.id AND a197.t=197 
                                          LEFT JOIN kladr a181 ON a181.up=a171.id AND a181.t=181 
                                          LEFT JOIN (kladr r187 JOIN kladr a187 USE INDEX (PRIMARY)) ON r187.up=a171.id AND a187.id=r187.t AND a187.t=171 
                                          LEFT JOIN kladr a171_region ON a171_region.t=171 AND a171_region.id=a187.id 
                                          LEFT JOIN (kladr r187_region JOIN kladr a187_region USE INDEX (PRIMARY)) ON r187_region.up=a171_region.id AND a187_region.id=r187_region.t AND a187_region.t=171 
                                          LEFT JOIN kladr a171_area ON a171_area.t=171 AND a171_area.id=a187_region.id 
                                          WHERE a171.up!=0 AND length(a171.val) AND a171.t=171 AND a171_region.val LIKE 'Надым%' LIMIT 20;


                                          И такой результат:
                                          0
                                          Ответил ниже.
                                      0
                                      Не нужно ничего дорабатывать. Только переименовать поля и разнести по таблицам, а это, как вы сами сказали, не учитывается.


                                      Если ничего не нужно дорабатывать, давайте попробуем создать там такую схему данных и заполнить её:

                                      Описание сущностей:


                                      Экземпляры сущностей:


                                      Редактор экземпляра:
                                        0
                                        нет в таблицах для товаров, но есть в других
                                        А слова есть в других вы типа не заметили? Там вообще-то тоже EAV. У вас нигде нет требования, чтобы это было только для товаров.

                                        Давайте возьмем решение как есть, выберем одну таблицу и на ней всё проверим.
                                        Иначе получается «здесь читать, а там не читать».
                                          0

                                          Почему? Везде читать. Индекс есть, придуман не вами.

                                    –1
                                    Вот пример реализации, где всё в одной таблице: github.com/Boolive/Boolive/blob/develop/www/boolive/data/stores/MySQLStore2.php#L1605
                                    Можно пойти дальше — реализовать всё в двух колонках одной таблицы (id, json), или в одном поле одной записи (за счет JSON поля). И запатентовать ;)
                                      0
                                      Можно пойти дальше — реализовать всё в двух колонках одной таблицы (id, json), или в одном поле одной записи (за счет JSON поля).

                                      Это как раз будет KV (key-value)

                                      И запатентовать ;)

                                      Кстати, попробуйте!

                                      В комментариях здесь неоднократно выражалась печаль, что, якобы, можно что угодно запатентовать. Правда есть одна пикантная особенность — те же люди признаются, что не разбираются в теме патентования.
                                        0
                                        Можно запатентовать даже то, что уже было сделано до (в теории нельзя, но у вас вполне получилось).
                                          0
                                          Это практически нельзя.
                                          У меня получилось как раз потому, что это никто нигде пока не сделал именно так.
                                          Я не отрицаю, что схема необыкновенно проста и очевидна, и любой мог бы её запатентовать ранее, если бы построил пару индексов и запихнул всё в одну таблицу. Но почему-то никто этого не сделал ни в РФ, ни за рубежом. Допускаю, что это просто никому не было нужно.

                                          Должно быть, вы думаете, что легко получить такую вот бумажку

                                            0
                                            Допускаю, что это просто никому не было нужно.

                                            Так мне и сейчас не понятно, зачем это нужно.

                                              +1
                                              Чтобы закрыть тему построения конструкторов неким законченным решением.
                                              Вы брались дважды за это, как же вам не понять?
                                                0
                                                У других тоже были законченные решения, дело в нише, как иниверсальное решение — непригодно (поскольку на больших объёмах с интенсивным доступом непригодно).
                                                  0
                                                  на больших объёмах с интенсивным доступом непригодно

                                                  Вот на это я хочу ответить развернуто. Укажите, пожалуйста, какой минимальный объем можно уже считать большим?
                                                  0

                                                  Так мне именно поэтому и не понять: чтобы сделать законченное решение, мне не нужен патент.


                                                  Или вы имеете в виду "закрыть [...] для других людей"?

                          0
                          Сравнивать запросы, написанные без JOIN'ов на одних WHERE'ах, — это, конечно, в высшей степени справедливое сравнение. Впрочем, если предполагаемый пользователь продукта слабо владеет реляционной логикой, то, наверное, смысл от его использования есть.
                          В остальных случаях никакой «лавинообразной деградации» производительности при увеличении объёма данных случится не может.

                            0
                            Описанная структура использует справочник EAV, дополненный атрибутом ID, так как любой атрибут также является самостоятельной сущностью, могущей иметь свои атрибуты, равно как и использоваться в качестве справочного значения.

                            Давайте разберем это утверждение по частям. Начнем с конца:


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

                            Используется ли эта возможность в текущей реализации? Насколько я успел заметить, за пределами метаданных — нет. Иными словами, для прикладных данных у каждой сущности всегда есть ровно один уровень атрибутов, а все "вложенные" сущности (например, "книги автора") задаются через имитацию внешних ссылок (т.е., у сущности "книга" есть атрибут "author", в котором лежит идентификатор сущности "автор"). Если я не прав — покажите, как на уровне хранилища выглядят многоуровневые прикладные сущности.


                            Теперь перейдем к первой половине:


                            использует справочник EAV, дополненный атрибутом ID

                            Если многоуровневые бизнес-сущности, как предположено выше, не используются, то этот атрибут неотличим от атрибута entity_id в типовой реализации EAV (в запросах прекрасно видно, что он используется только для вытаскивания имен сущностей, что, в общем-то, тоже известная микрооптимизация — я такую делал лет пятнадцать назад).


                            Для иллюстрации этого предлагаю задуматься: какой оптимальный план запроса должен быть в этой структуре, если нас интересуют годы издания книг, соотнесенные с годами рождения авторов (и никакие другие атрибуты нам не нужны)?


                            То есть, структура предназначена не только как EAV-справочник, почему, собственно, я и не могу назвать это EAV.

                            EAV — это не предназначение, это способ организации хранилища, и пока что ваше хранилище неотличимо от EAV.


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

                            Нет, EAV — это способ хранения данных. Да, это один из элементов системы, и вам это и говорят: ваша система в качестве одного из элементов (определяющих) содержит EAV.


                            Тестирование проводилось на самом простом сервере: 1 ядро @2.4GHz, 1GB RAM, диск SSD.

                            Вы забыли два важных нюанса. Во-первых, вы не указали используемую СУБД (а она радикально влияет на некоторые ваши наблюдения). Ну а во-вторых, SSD — это как раз один из определяющих факторов быстродействия СУБД.


                            Видно, что разница по времени примерно в полтора раза,

                            QED 1: реляционная имплементация быстрее.


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

                            QED 2: реляционная имплементация быстрее и разрыв увеличивается с ростом объема возвращаемых данных.


                            Главное, что следует уяснить: при обсуждаемом подходе ядро системы не будет производить полный скан таблицы

                            Не "не будет", а "не должна". На ваших наблюдениях на вашей СУБД на ваших данных вы этого не обнаружили, однако нет гарантии, что это всегда будет выполняться: как я уже писал, на моих наблюдениях оптимизатор запросов MS SQL как минимум единожды решил, что скан таблицы выгоднее, чем проход по индексу.


                            Применим фильтр к названию книги: поиск подстроки. Перепишем запрос так:

                            А куда же пропали тексты и планы запросов?


                            Запустив эти два отчета увидим большую разницу во времени их выполнения: 148 мс в обычной базе и 2490 мс в конструкторе. Разница почти в 17 раз! К счастью, мы помним рекомендацию, данную в моей прошлой статье, которая велит нам переместить одну колонку отчета и начинать выборку с книги, а не с автора.

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


                            В отличие от конструктора, оптимизатор обычной БД вряд ли перестроил план выполнения в последней редакции запроса.

                            … потому что оптимизатор "обычной БД" (MS SQL же обычная БД?) строит его одинаково оптимально вне зависимости от порядка таблиц в джойне: если запрос выигрывает от индекса (LIKE 'smth%') получаем index seek (books) — key lookup (books) — clustered index seek (authors); если не выигрывает (LIKE '%smth%') получаем scan (books) — clustered index seek (authors). Еще раз повторюсь, это не зависит от порядка таблиц в запросе.


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

                            Так где же анализ производительности с ростом объема? Вы меняете критерии запросов, но не объем БД.


                            Более того, в реальности ваш "объем" имеет две метрики, которые по разному влияют на производительность (это, кстати, тоже я показывал): количество сущностей и количество атрибутов одной сущности. Этого анализа вы тоже не приводите.


                            Так что задача вашего эксперимента — как вы ее заявили — не выполнена.


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

                            А вот теперь давайте вернемся к отмеченной выше звездочки, и вспомним, что чтобы избежать падения производительности на конкретном запросе, автору пришлось этот запрос перестроить. Так чем же одна подстройка отличается от другой?


                            (и это мы еще не говорим о системах автоиндексации, которых тоже в природе есть)


                            основа простого в освоении конструктора, позволяющего снизить риск лавинообразной деградации производительности.

                            К сожалению, заявление о "снижении риска" так и не подтверждено. Хуже того, у вас даже нет объективной метрики для этого самого риска (или хотя бы для деградации).

                              0
                              Не «не будет», а «не должна». На ваших наблюдениях на вашей СУБД на ваших данных вы этого не обнаружили, однако нет гарантии, что это всегда будет выполняться: как я уже писал, на моих наблюдениях оптимизатор запросов MS SQL как минимум единожды решил, что скан таблицы выгоднее, чем проход по индексу.

                              Хорошо, «не должна». Задача — оптимизировать запрос, а не охотиться на ведьм в лице full scan.
                              Может быть ситуация, когда в базе (в таблице) только книги, у которых реквизиты заполнены процентов на 20. При этом, вероятно, будет full scan и это правильно.

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

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

                              К сожалению, заявление о «снижении риска» так и не подтверждено. Хуже того, у вас даже нет объективной метрики для этого самого риска (или хотя бы для деградации).

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

                                Тогда в чем же преимущество вашего решения (кроме визуального конструктора)? Ну и да, что же такое "большой объем данных"?


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

                                Я и говорю: у вас нет ни объективной метрики деградации, ни объективной метрики риска.

                                  0
                                  Тогда в чем же преимущество вашего решения (кроме визуального конструктора)?

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

                                  Ну и да, что же такое «большой объем данных»?

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

                                    Это как раз конструктор.


                                    Скажем так, от гигабайта и до бесконечности.

                                    Как же так получилось, что в вашем решении понадобилось двигать колонки уже на 300Мб?


                                    (я даже не буду спрашивать, понимаете ли вы объективную разницу между 10Гб и 10Тб данных)

                                      0
                                      Это как раз конструктор.

                                      Да, я об этом же и говорю. Конструктор. Мечта программиста.

                                      Как же так получилось, что в вашем решении понадобилось двигать колонки уже на 300Мб?

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

                                      (я даже не буду спрашивать, понимаете ли вы объективную разницу между 10Гб и 10Тб данных)

                                      В 2003 году у нас было 13.5Тб в базе, и я многое понял тогда.
                                        0
                                        Да, я об этом же и говорю.

                                        … а я ведь специально спрашивал " в чем же преимущество вашего решения (кроме визуального конструктора)? "


                                        Конструктор. Мечта программиста.

                                        Ну нет, спасибо. Я эту "мечту программиста" дважды писал, и, как программист, больше никогда не хочу ее видеть (в сколь-нибудь сложных LOB-решениях).


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

                                        А что тут "не забывать", если на современном SSD этот скан выполняется за полторы десятых? Все накладные расходы — от неправильно построенного вашим конструктором запроса.


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

                                        "Есть простые правила [индексации], которые лучше выполнять, пользуясь DDL"


                                        Можно было их и не выполнять, ничего страшного не случится.

                                        … можно было их (правила индексации) и не выполнять, ничего страшного не случится.

                                          0
                                          Ну нет, спасибо. Я эту «мечту программиста» дважды писал, и, как программист, больше никогда не хочу ее видеть (в сколь-нибудь сложных LOB-решениях).

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

                                          Я тоже поделюсь. Когда в 1998 году я пришел инженером на автозавод после института и армии, я загорелся сделать бесконтактный прерыватель указателя поворотов. И тогда взрослый инженер, под полтос, сказал мне не тратить время. В качестве доказательства он привел похожий аргумент: я пробовал, не получилось. Транзисторы горят, у тиристоров большие потери… Но у меня в итоге получилось, хотя я применил пару решений, за которые был критикуем. Например, я запараллелил пару транзисторов, а у них разный коэффициент передачи, плавающие характеристики, ну и прочие в общем случае правильные вещи услышал я от коллег. Тем не менее, в том конкретном случае такое решение было оправдано и мне удалось пройти все испытания: наработку на отказ, климатику (исполнение УХЛ: от -45 до +55 С) и прочие. Даже тогда я не слушал старпёров, теперь и подавно.
                                            0

                                            Это была бы прекрасная речь, если в ней не было одной мааааленькой ошибки.


                                            Мне не надо предсказывать будущее (может или не может получиться что-то путное из вашего решения), оно — решение — уже сейчас показывает все те признаки, которые безвозвратно лишают его возможности быть моей мечтой. И мы, что характерно, уже обсуждали эти признаки, просто вас они не волнуют.


                                            Что как бы показывает, что разные программисты мечтают совершенно о разном.

                                          +1
                                          Конструктор. Мечта программиста.

                                          Ровно до момента "а давайте..." и мечта становится кошмаром, не говоря уже о всяких конструкторах, где авторы решили не давать слишком много свободы и делают как им кажется удобным (те же индексы с просадкой производительности вместо роста — такое бывает, но не в ваших системах, потому как использовать их на терабайтных базах будет только безумец (сотни таблиц, миллионы записей при десятках полей десятка разных типов, часть из которых индексировать не надо в принципе и запрос выборки среднего значения поля с простой фильтрацией)).

                                            0
                                            Ровно до момента «а давайте...» и мечта становится кошмаром

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

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

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

                                              Я боюсь, критерии нормальности у людей различаются. Вы вот считаете свое хакатонное решение для онлайн-касс нормальным?

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

                                      … мне, кстати, нравится, как вы мило проигнорировали все остальные пункты. Я правильно понимаю, что вы с ними согласны?

                                        0
                                        Давайте начнем по порядку.

                                        Используется ли эта возможность в текущей реализации? Насколько я успел заметить, за пределами метаданных — нет. Иными словами, для прикладных данных у каждой сущности всегда есть ровно один уровень атрибутов, а все «вложенные» сущности (например, «книги автора») задаются через имитацию внешних ссылок (т.е., у сущности «книга» есть атрибут «author», в котором лежит идентификатор сущности «автор»). Если я не прав — покажите, как на уровне хранилища выглядят многоуровневые прикладные сущности.

                                        Пример приведите, о чем речь, я его сделаю и продемонстрирую здесь.
                                          0

                                          (а ведь типовая, казалось бы, вещь).


                                          Вот у нас есть заказ (order), у него есть номер (string), дата (datetime) и покупатель (string). У заказа есть строки (line), у каждой строки есть артикул (string) и количество (number). Еще у каждой строки заказа есть размещение (alloc), которое означает, с каких складов мы этот артикул повезем; у размещения есть склад (string) и количество (number).


                                          Собственно, всё.

                                            0
                                            Вот это имелось в виду?

                                            Описание типов:


                                            Объекты типов:
                                              0

                                              Да, это. А вот теперь давайте взглянем, как это хранится в БД.

                                                0
                                                Вот так
                                                  0

                                                  В приведенных данных одна "позиция" или две? Ноль размещений, одно, два или три?

                                                    0
                                                    В таблице выше видна одна позиция и её два размещения.
                                                    А вот вторая позиция с одним размещением:
                                                      0

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


                                                      Во-вторых, давайте попробуем разобраться.


                                                      Вот сам заказ:


                                                      | id              | t       | up | value |
                                                      |-----------------|---------|----|-------|
                                                      | 4338366         | 4338353 | 1  | 15а   |

                                                      Нас здесь интересует id=4338366.


                                                      Давайте найдем все дочерние строки:


                                                      | id      | t       | value    |
                                                      |---------|---------|----------|
                                                      | 4338429 | 4338355 | 1        |
                                                      | 4338428 | 4338421 | 20180619 |
                                                      | 4338372 | 4338368 | 4338367  |
                                                      | 4338448 | 4338355 | 2        |

                                                      Нас интересуют строки с t=4338355, это позиции. Возьмем первую из них (id=4338429) и снова найдем все дочерние:


                                                      | id      | t       | value   |
                                                      |---------|---------|---------|
                                                      | 4338437 | 4338422 | 2       |
                                                      | 4338433 | 4338422 | 1       |
                                                      | 4338432 | 4338365 | 5       |
                                                      | 4338431 | 4338430 | 4338358 |

                                                      Аналогично, нас интересуют строки с t=4338422, это размещения.


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


                                                      Давайте подумаем, можно ли это воспроизвести в "обычной" EAV… да, можно, с помощью все тех же foreign key:


                                                      | entity     | attribute | value      |
                                                      |------------|-----------|------------|
                                                      | order1     | nbr       | 15a        |
                                                      | order1     | line      | orderline1 |
                                                      | order1     | line      | orderline2 |
                                                      | orderline1 | item      | art1       |
                                                      | orderline1 | qty       | 10         |
                                                      | orderline2 | item      | art2       |
                                                      | orderline2 | qty       | 5          |

                                                      Является ли это изменение значимым? Я не знаю. Повышает ли оно производительность запросов или понижает? Надо мерять.

                                                        0
                                                        И это, собственно, ответ на мой вопрос: вы используете иерархические сущности (вопреки тому, что я предполагал по ранним примерам), поэтому мои вопросы к полю ID неуместны.

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

                                                        Суть в том что ядро не пробегается последовательно по иерархии при построении запроса (например, того, что описан в примере с книгами), а одним махом выбирает весь набор полей данных, которые используются в запросе, при любом количестве уровней иерархии.
                                                        В этом наборе есть вся информация для построения запроса с необходимыми JOIN, что отчетливо видно в тексте запроса.
                                                          0
                                                          Я не до конца понял в чем была задача этого упражнения

                                                          Задача этого упражнения была в том, чтобы понять, как вы используете поле ID. Я вот понял, что раньше понимал это (использование) неверно.


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

                                                          При любом? Вы уверены в этом утверждении?

                                                            0
                                                            Да.
                                                            Они все перечислены в таблице Колонки отчета в построителе запросов.

                                                            Если вы имеете в виду, что у баз данных есть ограничения по количеству JOIN, то тут — да (в том же MSSQL, вроде, не более 256 объединений), поэтому далее при построении запроса с большим количеством полей будет ошибка.
                                                              0
                                                              Да.
                                                              Они все перечислены в таблице Колонки отчета в построителе запросов.

                                                              Я боюсь, что мы с вами говорим о разных сценариях.


                                                              Меня-то интересует сценарий "у меня есть иерархия категорий, я хочу одним запросом выбрать все категории вниз от заданной".


                                                              Впрочем, то, что вы описываете:


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

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

                                                                0
                                                                Как работает построитель запросов:
                                                                1. Вы выбираете любую сущность и справочника
                                                                2. Система в один запрос выберет список всех ее полей, включая те, что являются связями
                                                                3. Вы можете выбрать любую сущность, которая связана с выбранными ранее (конструктор ограничит выбор)
                                                                4. Переходим к п.2 для выбора следующей сущности, пока не соберем весь набор полей для запроса
                                                                5. Теперь мы имеем список, по которому система в один запрос выберет список всех полей выбранных сущностей
                                                                  0

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


                                                                  Да, я понимаю, что данные самого отчета выберутся в один запрос — но, опять-таки ж, потому что вы принудительно развернули структуру в один уровень, вы не умеете выбирать данные рекурсивно.

                                                                    0
                                                                    вы не умеете выбирать данные рекурсивно

                                                                    Подскажите, пожалуйста, случай, в котором это может понадобиться? Просто интересно, чем будет ограничена выборка этой рекурсии.

                                                                    Например, в структуре рекрутерского проекта все данные взаимосвязаны, что неудивительно для реляционной базы. То есть, рекурсивно они выберутся практически все — все метаданные.
                                                                    Когда мне нужен некий фрагмент этой структуры, то я укажу явно, какие сущности нужны.
                                                                      0
                                                                      Подскажите, пожалуйста, случай, в котором это может понадобиться?

                                                                      Уже было выше: дерево категорий.


                                                                      Просто интересно, чем будет ограничена выборка этой рекурсии.

                                                                      Отсутствием дочерних элементов.

                                                                        0
                                                                        Уже было выше: дерево категорий.

                                                                        Так. И как это делается в реляционной БД так, чтобы я не мог то же самое повторить в конструкторе?
                                                                          0

                                                                          Не, неправильный вопрос. Правильный — это как это делается в вашем конструкторе?


                                                                          А то ведь понятно, что на EAV можно повторить любой трюк, используемый в РСУБД (если его знать), вопрос только производительности этого трюка.

                                                                            0
                                                                            Я пока показал здесь два примера: специализации в рекрутерском приложении и иерархия КЛАДР на стенде. Но там уровни фиксированные, а вы, вероятно, ждете нечто динамическое и неограниченной глубины.
                                                                            Давайте рассмотрим на примере — предложите его.
                                                                              0

                                                                              Мне несложно третий раз повторить: дерево категорий.


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

                                                                                0
                                                                                Уточню на всякий случай: вы тычете в любую категорию, и та раскрывается вниз до самых низших элементов, и при этом нужно выполнить только один запрос. Так?
                                                                                Или же количество запросов может быть больше одного, ибо
                                                                                вопрос только производительности этого трюка

                                                                                  0
                                                                                  Уточню на всякий случай: вы тычете в любую категорию, и та раскрывается вниз до самых низших элементов, и при этом нужно выполнить только один запрос. Так?

                                                                                  Да, именно так. Иначе не интересно.

                                                                                    0
                                                                                    Задача с подвохом?
                                                                                    Самый очевидный способ: хранить в коде иерархии весь путь наверх. Это компактно и быстро. Нужен один запрос на выборку. Также одним запросом делается изменение положения в иерархии, если, например, перенесли категорию в другое подчинение.
                                                                                      0
                                                                                      Задача с подвохом?

                                                                                      Конечно же.


                                                                                      Самый очевидный способ: хранить в коде иерархии весь путь наверх.

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

                                                                                        0
                                                                                        А в чем подвох?

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

                                                                                        Не понял. Есть код и есть название, это два атрибута, как ни крутите.


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

                                                                                        Вот, написал, руками. Не ногами же.

                                                                                        Запрос:


                                                                                        Отчет:
                                                                                          0
                                                                                          А в чем подвох?

                                                                                          Вот когда в реальной жизни попробуете применить, тогда и узнаете.


                                                                                          Есть код и есть название, это два атрибута, как ни крутите.

                                                                                          Это если у вас код — это атрибут. В моей задаче такого атрибута не было, вы его придумали.


                                                                                          Вот, написал, руками

                                                                                          Дадада, я помню, что ваш конструктор обещал решение типовых задач без программирования. Как видите, не справился.


                                                                                          И это вы еще не попробовали написать, собственно, запрос "все товары от этой категории". Или автогенерацию этих самых кодов при создании пользователем новой категории.

                                                                                            0
                                                                                            Вот когда в реальной жизни попробуете применить, тогда и узнаете.

                                                                                            Вообще-то, применял.

                                                                                            Дадада, я помню, что ваш конструктор обещал решение типовых задач без программирования. Как видите, не справился.

                                                                                            Что я тут программировал? Собрал ссылку для интерфейса?

                                                                                            Или автогенерацию этих самых кодов при создании пользователем новой категории.

                                                                                            Без проблем.

                                                                                            Но вы сначала главное скажите: дана задача, она выполнена. Подтверждаете?
                                                                                              0
                                                                                              Вообще-то, применял.

                                                                                              Значит и с подвохами без моей помощи знакомы.


                                                                                              Что я тут программировал? Собрал ссылку для интерфейса?

                                                                                              Да, вместе с зашитым в нее запросом.


                                                                                              Но вы сначала главное скажите: дана задача, она выполнена. Подтверждаете?

                                                                                              Нет. Продемонстрируйте, что это решение работает для неограниченной глубины вложенности.

                                                                                                0
                                                                                                Нет. Продемонстрируйте, что это решение работает для неограниченной глубины вложенности.

                                                                                                Впрочем, это не важно. Меня больше интересовало то, что хотя ваш конструктор и имеет поддержку иерархических структур (благодаря idparent_id), он при этом для них не оптимизирован (поэтому нельзя, скажем, сделать такое дерево категорий, где категории будут вложенными объектами). Более того, поддержки ссылочной целостности тоже нет.


                                                                                                Короче говоря, все эти решения — они будут построены поверх вашего конструктора, а не интегрированы в него. А встроенная иерархия работает для совершенно других целей.

                                                                                                  0
                                                                                                  Впрочем, это не важно. Меня больше интересовало то, что хотя ваш конструктор и имеет поддержку иерархических структур (благодаря id — parent_id), он при этом для них не оптимизирован (поэтому нельзя, скажем, сделать такое дерево категорий, где категории будут вложенными объектами).

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

                                                                                                  Более того, поддержки ссылочной целостности тоже нет.

                                                                                                  Кто вам такое сказал?
                                                                                                    0
                                                                                                    Не было задачи поддержки иерархических структур в вашем понимании.

                                                                                                    Ну да, не было. Не спорю с этим. Просто отмечаю для себя, что она не решена.


                                                                                                    Кто вам такое сказал?

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

                                                                                                  0
                                                                                                  Да, вместе с зашитым в нее запросом.

                                                                                                  Не запросом, а только фильтром имя=значение фильтра

                                                                                                  Нет. Продемонстрируйте, что это решение работает для неограниченной глубины вложенности.

                                                                                                  В каком смысле? Поле код не ограничено по длине, значит и вложенность тоже.
                                                                                                    0
                                                                                                    имя=значение фильтра

                                                                                                    … где значение фильтра вы собрали руками (я же вижу там процентик).


                                                                                                    Поле код не ограничено по длине

                                                                                                    … а у вас это правда так? И что же происходит с производительностью за ~900 байтами кода?

                                                                                                      0
                                                                                                      И что же происходит с производительностью за ~900 байтами кода?

                                                                                                      Вы не меньше трех раз пытались склонить меня всерьез рассматривать варианты с полем длиной более 900 байт, индексируемым полностью.
                                                                                                      Вероятно, вы даже делали какие-то решения, где код номенклатуры превышает эту длину, наверное, вот это одно из них, а здесь вы косвенно на него же намекаете.
                                                                                                      Это недостижимое ограничение в нормально спроектированной системе, потому что есть масса способов избежать полного индексирования таких полей, и особенно идентифицирующих полей.
                                                                                                        0
                                                                                                        Вероятно, вы даже делали какие-то решения, где код номенклатуры превышает эту длину

                                                                                                        Подождите, а откуда вдруг всплыл "код номенклатуры"? Речь идет о реализации вложенности в общем случае.


                                                                                                        наверное, вот это одно из них

                                                                                                        Нет, это не одно из них.


                                                                                                        Это недостижимое ограничение в нормально спроектированной системе

                                                                                                        (Аргумент истинного шотландца? Спасибо, но нет.)


                                                                                                        Как же вы "нормально спроектируете" систему, которая выполняет описанную выше задачу, и в которой минимум 1000 уровней вложенности?


                                                                                                        (заметим, что в реальности нужно намного меньше уровней, потому что вы тратите на уровень минимум два байта)

                                                                                                          0
                                                                                                          Подождите, а откуда вдруг всплыл «код номенклатуры»? Речь идет о реализации вложенности в общем случае.

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

                                                                                                          Как же вы «нормально спроектируете» систему, которая выполняет описанную выше задачу, и в которой минимум 1000 уровней вложенности?

                                                                                                          У номенклатуры нет 1000 уровней, а если будет, то будет выбрано иное решение. Поэтому я и просил конкретный пример.
                                                                                                            0
                                                                                                            У номенклатуры нет 1000 уровней, а если будет, то будет выбрано иное решение. Поэтому я и просил конкретный пример.

                                                                                                            Я вам и дал конкретный пример: номенклатура с большой вложенностью. То, что у вас ее не бывает — ваше дело; задачи конструктора вашими не ограничиваются.


                                                                                                            Задача изначально была поставлена именно для случая с неограниченной вложенностью. В поставленном виде она не выполнена.

                                                                                              0

                                                                                              … представьте, например, что я хочу "отчет" (на самом деле — просто таблицу), в которой в строчках — товары (категория-название-цена), а сверху — стандартный фильтр по каждой колонке отдельно. И вот фильтр по колонке "категория" должен работать по принципу "выбрал категорию — получил товары из этой категории и ее детей".

                                                                                                –1
                                                                                                Есть код и есть название, это два атрибута, как ни крутите.

                                                                                                … и именно поэтому в вашем конструкторе невозможен запрос "дай мне все значения всех атрибутов этого объекта и всех его вложенных объектов" (иными словами, получение агрегата в терминах DDD или документа в терминах ДОБД).

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

                                                                                                  Причем тут DDD и ДОБД?
                                                                                                  (название, конечно, то ещё)
                                                                                                  Не соскакивайте с обсуждения изначальной вашей же задачи.
                                                                                                    0
                                                                                                    В этом конструкторе реализуем любой запрос, возможный в реляционной базе данных

                                                                                                    А я этого и не оспаривал.


                                                                                                    Причем тут DDD и ДОБД?

                                                                                                    Для пояснения, какую функциональность я хочу получить.

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

                                                                                                      Я вот подумал, что зря это утверждение осталось без должного внимания. Только давайте сначала немножечко уточним: когда вы говорите "в конструкторе возможен запрос", вы имеете в виду, что сам запрос целиком и полностью будет выполнен на стороне БД (а конструктором только правильно сформирован), или же часть функциональности БД (например, какая-то фильтрация) все-таки будет выполнена за ее пределами (в php-коде вашей системы)?

                                                                                                        0
                                                                                                        Сам запрос целиком и полностью будет выполнен на стороне БД (а конструктором только правильно сформирован)
                                                                                                          0

                                                                                                          Ну тогда начнем с простого: есть чеки, у каждого чека есть дата/время и сумма (что важно, каждый чек заполнен в своем часовом поясе). Как получить среднюю сумму всех чеков в разбиении по часам дня в переводе на централизованное время?


                                                                                                          Ну то есть алгоритм вычисления приблизительно такой: приводим все чеки в один часовой пояс (например PST), затем получаем суммы чеков за каждый час в течении всего периода, затем усредняем по всем дням (чтобы получить отчет вида "между часом и двумя дня PST средняя общая сумма чеков составляет X").

                                                                                                            0
                                                                                                            Время хранится серверное для всех timestamp.
                                                                                                            У конструктора есть несколько внутренних контекстных значений, доступных по именам: пользователь, его ID, временной сдвиг относительно сервера и другие. Их можно использовать в построителе запросов или сохранять в базе при регистрации чеков, например.
                                                                                                            Если нужно, в запросе будет учтён сдвиг — прибавлен ко времени для приведения серверного времени к локальному пользовательскому. Сервер вычислит это, выполняя запрос.
                                                                                                            Вы про это спрашивали?
                                                                                                              0

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

                                                                                                                0
                                                                                                                Будет выглядеть следующим образом.

                                                                                                                Тип Чек в редакторе типов, для которого номер, локальное дата-время пользователя и сдвиг заполняются автоматически.


                                                                                                                Список чеков


                                                                                                                Создание чека — вносим только сумму, остальное заполнено


                                                                                                                Отчет по чекам


                                                                                                                Результат выполнения отчета


                                                                                                                Да, в запросе есть функции. Правило Интеграла: «Не сложнее Экселя», то есть не возбраняется использовать простейшие формулы и функции, если нужно, как большинство это делают в Экселе.

                                                                                                                UPD А, по дням еще усреднить, сделаю.
                                                                                                                  0

                                                                                                                  Особенно "простейшая формула" — это FROM_UNIXTIME, которая требует от пользователя знания деталей реализации, которые он не контролирует.


                                                                                                                  Ну и да, я все-таки хотел бы видеть запрос к БД, который это выполняет.

                                                                                                                    0

                                                                                                                    Да, кстати, а почему вы прибавляете пользовательское смещение к серверному времени (и тем самым получаете пользовательское время, в разных таймзонах), если нам надо получить все время в одной зоне (и это не зона сервера)?

                                                                0
                                                                Размещение второй позиции вот так выглядит в словаре, если что:
                                                        0
                                                        Давайте начнем по порядку.

                                                        Начать — начали, на первом же пункте и закончили. С остальными пунктами согласны?

                                                          0
                                                          Какими именно?
                                                          Что конструктор имеет накладные расходы и в простейших случаях производительность ниже, я заявляю на иллюстрации и в самой статье.

                                                          Тексты и планы всех-всех запросов нужны?

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

                                                          А вот теперь давайте вернемся к отмеченной выше звездочки, и вспомним, что чтобы избежать падения производительности на конкретном запросе, автору пришлось этот запрос перестроить. Так чем же одна подстройка отличается от другой?

                                                          На это я косвенно отвечал в другом комментарии, что создание индекса в базе после ковыряния в профайлере дороже обходится, чем передвинуть одним кликом колонку отчета.
                                                          При этом специально взят запрос по неиндексируемому значению. В более простом и привычном случае порядок колонок не важен, как в примере с КЛАДРом, где поиск идет по любому полю из восьми возможных.
                                                            0
                                                            Какими именно?

                                                            Всеми подряд. Даже в комментарии, на который я отвечаю, вы проигнорировали часть пунктов.

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

                                                              … кстати, на какой иллюстрации вы это заявляете?

                                                                0
                                                                Рисунок в начале статьи, где показано, что на начальном этапе развития системы, когда в ней немного данных, запросов и пользователей, накладные расходы в несколько раз превышают расходы в традиционно построенной базе данных
                                                                  0

                                                                  Какая у вас удобная универсальная иллюстрация. Я-то думал в ней идет речь о затратах (человеческих) на обслуживание, а она внезапно про накладные расходы при работе с БД. А откуда у вас в ней тогда экспоненциальный (я правильно на взгляд определил?) рост этих накладных расходов при "традиционной разработке"?

                                                    0
                                                    Не понятна новизна решения. Принципиальных отличий от EVA нет. Подобная идея скорее всего приходила в голову не один раз каждому архитектору и продвинутому разработчику.
                                                    Подход имеет как плюсы, так и минусы, из которых проистекает зона применимости.
                                                    Вопрос чего больше плюсов или минусов холиварный, но чтобы их обсуждать уровень инженерной аргументации в статье должен быть ОЧЕНЬ сильно выше( пока это выглядит как обсуждение строительства небоскребов с куличиками в песочнице ).

                                                      0
                                                      Не понятна новизна решения. Принципиальных отличий от EVA нет. Подобная идея скорее всего приходила в голову не один раз каждому архитектору и продвинутому разработчику.
                                                      Да нет там никакой новизны.
                                                      Как я уже комментировал в прошлой статье, но так и не дождался ответа от UltimaSol:
                                                      Достаточно погуглить по create table parent_id type_id и вы найдете кучу решений использующих этот подход задолго до вас! Он настолько очевиден, что люди даже используют такие же имена полей — вот парочка ссылок с первой же страницы поиска:
                                                      2013 CREATE TABLE myproducts (uid, parent_id, type_id, name ...
                                                      2007 CREATE TABLE menu_menuitem (id, parent_id, type_id, order, title ...
                                                        0
                                                        Как я уже комментировал в прошлой статье, но так и не дождался ответа

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

                                                      … но вообще, конечно, это исключительно мало данных для адекватного статанализа. Там по идее бы должно быть нормальное распределение, а его нет (p-value для критерия Шапиро-Уилкса 0.46 и 0.31). Ну и надо ли говорить, что доверительные интервалы (для 95%) перекрываются?

                                                        0
                                                        Я развернуто ответил на ваш вопрос о всего одной выборке, захватив несколько смежных вариантов.
                                                        По планам запроса и таймингам можно понять, чего ждать от этого подхода и жизнеспособен ли он вообще. Если сделать статистически значимое количество тестов, то принципиально ничего не поменяется, или вы это будете оспаривать?
                                                          0
                                                          Я развернуто ответил на ваш вопрос о всего одной выборке, захватив несколько смежных вариантов.

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


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

                                                          Только на этом объеме данных на этом сервере на этих выборках.


                                                          Если сделать статистически значимое количество тестов, то принципиально ничего не поменяется, или вы это будете оспаривать?

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

                                                        0
                                                        > Изобретение это потенциально может перевернуть весь мир IT, каким мы его знаем.

                                                        И что тут нового? Этому обещанию уже десятки лет (разве что IT тогда это не называлось у нас). Все эти дизайнеры форм и моделей без программирования… в середине 90х такое делали и даже похожие картинки были, которые к реальности (костыли в сложной системе с конструктором и большая часть трудозатрат — для обхода ограничений этого конструктора, когда какой-то фанат решил делать нечто сложнее прототипа на подобном).

                                                        Интересно, почему запрос по «традиционной схеме» написан без джоина? Проигрышь будет не в плтора раза? На игрушечных данных, конечно, 0.2с не время, но получается что для приличного размера системы оно не проверялось и навряд ли будет работать с приемлемой скоростью.
                                                          0
                                                          В предыдущей статье я рассказывал о сервисе, который сложнее прототипа, и он вполне себе нормально работает под типичной загрузкой 20-30 пользователей одновременно.
                                                        +1
                                                        Итак, выше было предложение перенести пример с книгами и авторами в Magento.
                                                        Я взял 10 авторов и 10 книг из дампа, указанного в этом комментарии. Там дамп не в SQL, видимо это формат выгрузки из приложения «Интеграл». Я перевел его в SQL и добавил недостающие идентификаторы по примеру из этой статьи.

                                                        SQL
                                                        CREATE TABLE IF NOT EXISTS `test_single_table` (
                                                          `id` int(11) NOT NULL AUTO_INCREMENT,
                                                          `parent_id` int(11) NOT NULL,
                                                          `type_id` int(11) NOT NULL,
                                                          `value` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
                                                          `order` int(11) NOT NULL DEFAULT '0',
                                                          `comment` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
                                                          PRIMARY KEY (`id`),
                                                          KEY `parent_id_type_id` (`parent_id`,`type_id`),
                                                          KEY `type_id_value` (`type_id`,`value`)
                                                        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
                                                        
                                                        INSERT INTO `test_single_table` (`id`, `parent_id`, `type_id`, `value`, `order`, `comment`) VALUES
                                                        	(1, 1, 1, 'Root', 0, 'Root'),
                                                        	(2, 0, 2, 'SHORT', 0, 'SHORT'),
                                                        	(3, 0, 3, 'NUMBER', 0, 'NUMBER'),
                                                        	(217, 0, 2, 'Книга', 0, 'Книга'),
                                                        	(219, 0, 3, 'Год выпуска', 0, 'Год выпуска'),
                                                        	(223, 0, 3, 'Число страниц', 0, 'Число страниц'),
                                                        	(225, 0, 2, 'Автор', 0, 'Автор'),
                                                        	(226, 0, 225, '', 0, '(ссылка на Автор)'),
                                                        	(231, 217, 219, '', 0, '(Книга.Год выпуска)'),
                                                        	(232, 217, 223, '', 0, '(Книга.Число страниц)'),
                                                        	(233, 217, 226, '', 0, '(Книга.ссылка на Автор)'),
                                                        	(236, 1, 225, 'Агафий', 0, ''),
                                                        	(237, 1, 225, 'Хикс Эдвард', 0, ''),
                                                        	(238, 1, 225, 'Aahan', 0, ''),
                                                        	(239, 1, 225, 'Aala', 0, ''),
                                                        	(240, 1, 225, 'Aaleahya', 0, ''),
                                                        	(241, 1, 225, 'Aaleyah', 0, ''),
                                                        	(242, 1, 225, 'Aalijah', 0, ''),
                                                        	(243, 1, 225, 'Aaliyah', 0, ''),
                                                        	(244, 1, 225, 'Aamori', 0, ''),
                                                        	(245, 1, 225, 'Aanjay', 0, ''),
                                                        	(1001, 1, 217, 'Сила молитвы и другие рассказы', 0, ''),
                                                        	(1002, 1001, 219, '1994', 0, ''),
                                                        	(1003, 1001, 223, '455', 0, ''),
                                                        	(1004, 1001, 236, '', 0, '(ссылка на Автор 236)'),
                                                        	(1005, 1, 217, '451 градус по Фаренгейту', 0, ''),
                                                        	(1006, 1005, 219, '1948', 0, ''),
                                                        	(1007, 1005, 223, '810', 0, ''),
                                                        	(1008, 1005, 237, '', 0, '(ссылка на Автор 237)'),
                                                        	(1009, 1, 217, 'Авторская исповедь', 0, ''),
                                                        	(1010, 1009, 219, '1941', 0, ''),
                                                        	(1011, 1009, 223, '813', 0, ''),
                                                        	(1012, 1009, 238, '', 0, ''),
                                                        	(1013, 1, 217, 'Авторство Откровенных рассказов странника духовному своему отцу', 0, ''),
                                                        	(1014, 1013, 219, '1992', 0, ''),
                                                        	(1015, 1013, 223, '812', 0, ''),
                                                        	(1016, 1013, 239, '', 0, ''),
                                                        	(1017, 1, 217, 'Агат – драгоценный камень', 0, ''),
                                                        	(1018, 1017, 219, '1972', 0, ''),
                                                        	(1019, 1017, 223, '121', 0, ''),
                                                        	(1020, 1017, 240, '', 0, ''),
                                                        	(1021, 1, 217, 'Аглая', 0, ''),
                                                        	(1022, 1021, 219, '1938', 0, ''),
                                                        	(1023, 1021, 223, '400', 0, ''),
                                                        	(1024, 1021, 241, '', 0, ''),
                                                        	(1025, 1, 217, 'Ад', 0, ''),
                                                        	(1026, 1025, 219, '1974', 0, ''),
                                                        	(1027, 1025, 223, '276', 0, ''),
                                                        	(1028, 1025, 242, '', 0, ''),
                                                        	(1029, 1, 217, 'Аксиомы религиозного опыта', 0, ''),
                                                        	(1030, 1029, 219, '2006', 0, ''),
                                                        	(1031, 1029, 223, '827', 0, ''),
                                                        	(1032, 1029, 243, '', 0, ''),
                                                        	(1033, 1, 217, 'Александр I', 0, ''),
                                                        	(1034, 1033, 219, '1939', 0, ''),
                                                        	(1035, 1033, 223, '224', 0, ''),
                                                        	(1036, 1033, 244, '', 0, ''),
                                                        	(1037, 1, 217, 'Александр Грин', 0, ''),
                                                        	(1038, 1037, 219, '1996', 0, ''),
                                                        	(1039, 1037, 223, '58', 0, ''),
                                                        	(1040, 1037, 245, '', 0, '');
                                                        



                                                        Эти данные можно перенести в систему EAV Magento без изменений, используя несложные запросы. Но есть нюансы, связанные с переносом ссылочных значений.

                                                        Запросы
                                                        /* id_offset is not required for empty tables, magento tables are not empty */
                                                        SET @id_offset = 10000;
                                                        
                                                        
                                                        /* entities */
                                                        
                                                        INSERT INTO eav_attribute_set (attribute_set_id, attribute_set_name, entity_type_id)
                                                        SELECT derived_types.id + @id_offset,
                                                            derived_types.value,
                                                            4 AS magento_product_entity_type
                                                        FROM test_single_table AS derived_types
                                                        WHERE derived_types.parent_id = 0 AND id != type_id AND type_id IN (
                                                          SELECT id
                                                          FROM test_single_table AS base_types
                                                          WHERE parent_id = 0 AND id = type_id
                                                        );
                                                        
                                                        
                                                        INSERT INTO eav_attribute_group (attribute_group_id, attribute_set_id, attribute_group_name, attribute_group_code)
                                                        SELECT attribute_set_id, attribute_set_id, 'General', 'general'
                                                        FROM eav_attribute_set
                                                        WHERE attribute_set_id > @id_offset;
                                                        
                                                        
                                                        
                                                        /* attributes */
                                                        
                                                        INSERT INTO eav_attribute (attribute_id, attribute_code, frontend_label, backend_table, backend_type, entity_type_id, frontend_input, is_user_defined)
                                                        SELECT data_attribute_names.id + @id_offset,
                                                            CONCAT('a_', data_attribute_names.id + @id_offset),
                                                            data_attribute_names.value,
                                                            NULL,
                                                            IF (data_attribute_names.type_id = 2, 'varchar', 'int'),
                                                            4 AS magento_product_entity_type, 'text', 1
                                                        FROM test_single_table AS data_attribute_names
                                                        WHERE data_attribute_names.parent_id = 0 AND id != type_id AND type_id IN (
                                                          SELECT id
                                                          FROM test_single_table AS base_types
                                                          WHERE parent_id = 0 AND id = type_id
                                                        );
                                                        
                                                        
                                                        INSERT INTO eav_attribute (attribute_id, attribute_code, frontend_label, backend_table, backend_type, entity_type_id, frontend_input, is_user_defined)
                                                        SELECT ref_attribute_names.id + @id_offset,
                                                            CONCAT('a_', ref_attribute_names.id + @id_offset),
                                                            ref_attribute_targets.value,
                                                            'catalog_product_entity_int',
                                                            ref_attribute_names.type_id + @id_offset,
                                                            4 AS magento_product_entity_type, 'text', 1
                                                        FROM test_single_table AS ref_attribute_names
                                                            INNER JOIN test_single_table AS ref_attribute_targets ON ref_attribute_targets.id = ref_attribute_names.type_id
                                                        WHERE ref_attribute_names.parent_id = 0 AND ref_attribute_names.id != ref_attribute_names.type_id AND ref_attribute_names.type_id NOT IN (
                                                            SELECT id
                                                            FROM test_single_table AS base_types
                                                            WHERE parent_id = 0 AND id = type_id
                                                        );
                                                        
                                                        
                                                        
                                                        INSERT INTO eav_entity_attribute (sort_order, attribute_set_id, attribute_group_id, attribute_id, entity_type_id)
                                                        SELECT attribute_structure.`order`,
                                                            attribute_structure.parent_id + @id_offset,
                                                            attribute_structure.parent_id + @id_offset,
                                                            attribute_structure.type_id + @id_offset,
                                                            4 AS magento_product_entity_type
                                                        FROM test_single_table AS attribute_structure
                                                        WHERE attribute_structure.parent_id IN (
                                                            SELECT id
                                                            FROM test_single_table AS possible_enitities
                                                            WHERE parent_id = 0 AND id != type_id AND type_id IN (
                                                              SELECT id
                                                              FROM test_single_table AS base_types
                                                              WHERE parent_id = 0 AND id = type_id
                                                            )
                                                        );
                                                        
                                                        
                                                        
                                                        INSERT INTO catalog_eav_attribute (attribute_id)
                                                        SELECT attribute_id
                                                        FROM eav_attribute
                                                        WHERE eav_attribute.attribute_id > @id_offset;
                                                        
                                                        
                                                        
                                                        
                                                        /* values */
                                                        
                                                        INSERT INTO catalog_product_entity (entity_id, attribute_set_id, sku)
                                                        SELECT objects.id + @id_offset, objects.type_id + @id_offset, objects.value
                                                        FROM test_single_table AS objects
                                                        WHERE objects.parent_id = 1 AND objects.id != 1;
                                                        
                                                        
                                                        INSERT INTO catalog_product_entity_varchar (value_id, attribute_id, entity_id, value)
                                                        SELECT data_attribute_values.id + @id_offset,
                                                            data_attribute_values.type_id + @id_offset,
                                                            data_attribute_values.parent_id + @id_offset,
                                                            data_attribute_values.value
                                                        FROM test_single_table AS data_attribute_values
                                                        WHERE parent_id IN (
                                                            SELECT id
                                                            FROM test_single_table AS objects
                                                            WHERE objects.parent_id = 1 AND objects.id != 1
                                                        )
                                                        AND type_id IN (
                                                            SELECT id
                                                            FROM test_single_table AS possible_attributes
                                                            WHERE parent_id = 0 AND id != type_id AND type_id = 2
                                                        );
                                                        
                                                        
                                                        INSERT INTO catalog_product_entity_int (value_id, attribute_id, entity_id, value)
                                                        SELECT data_attribute_values.id + @id_offset,
                                                            data_attribute_values.type_id + @id_offset,
                                                            data_attribute_values.parent_id + @id_offset,
                                                            data_attribute_values.value
                                                        FROM test_single_table AS data_attribute_values
                                                        WHERE parent_id IN (
                                                            SELECT id
                                                            FROM test_single_table AS objects
                                                            WHERE objects.parent_id = 1 AND objects.id != 1
                                                        )
                                                        AND type_id IN (
                                                            SELECT id
                                                            FROM test_single_table AS possible_attributes
                                                            WHERE parent_id = 0 AND id != type_id AND type_id = 3
                                                        );
                                                        
                                                        
                                                        INSERT INTO catalog_product_entity_int (value_id, attribute_id, entity_id, value)
                                                        SELECT ref_attribute_values.id + @id_offset,
                                                            type_ref_attributes.id + @id_offset,
                                                            ref_attribute_values.parent_id + @id_offset,
                                                            ref_attribute_values.type_id + @id_offset
                                                        FROM test_single_table AS ref_attribute_values
                                                            INNER JOIN test_single_table AS objects ON objects.id = ref_attribute_values.parent_id
                                                            INNER JOIN test_single_table AS ref_targets ON ref_targets.id = ref_attribute_values.type_id
                                                            INNER JOIN test_single_table AS type_attributes ON type_attributes.parent_id = objects.type_id
                                                            INNER JOIN test_single_table AS type_ref_attributes ON type_ref_attributes.id = type_attributes.type_id
                                                        	     AND type_ref_attributes.type_id = ref_targets.type_id
                                                        WHERE ref_attribute_values.parent_id IN (
                                                            SELECT id
                                                            FROM test_single_table AS objects
                                                            WHERE objects.parent_id = 1 AND objects.id != 1
                                                        )
                                                        AND ref_attribute_values.type_id IN (
                                                            SELECT id
                                                            FROM test_single_table AS objects
                                                            WHERE objects.parent_id = 1 AND objects.id != 1
                                                        );
                                                        



                                                        Результат:


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

                                                        Обратите внимание, в них практически нет джойнов. Данные переносятся «как есть», в том виде, в котором они находятся в одной таблице. Нельзя сказать, что джойны преобразую вертикальную структуру в горизонтальную. В секции WHERE указаны условия выборки секций, в патенте они обозначены в таблице 3 в столбце «Комментарий». Все константы нужны только для того, чтобы работал интерфейс Magento, так как он рассчитан на работу с этими значениями. На способ формирования базы данных они не влияют.

                                                        Джойны есть в 2 запросах, связанных со ссылками. В одном джойн нужен только для заполнения текстового названия поля у ссылочных атрибутов. В другом ситуация сложнее, это связано с неоптимальным хранением данных, способ не похож на хранение других атрибутов. Значения не-ссылочных атрибутов хранятся в поле «Value», а идентификаторы объектов для ссылочных в поле «Type ID», поле «Value» пустое. Но это не отмечено как отличительная особенность ни в патенте, ни в чек-листе здесь. Для не-ссылочных данных джойны не требуются, значит структуры хранения полностью аналогичные.

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

                                                        Кстати, запрос будет выглядеть так.
                                                        SQL
                                                        SELECT
                                                          a225_authors.value AS v1_225_author_name,
                                                          a217_books.value AS v2_217_book_name,
                                                          a223_attr_page_count.value AS v3_217_page_count,
                                                          a219_attr_year.value AS v4_217_year
                                                        FROM
                                                          test_single_table2 a217_books
                                                        
                                                          LEFT JOIN test_single_table2 a223_attr_author_ref
                                                            ON a223_attr_author_ref.parent_id = a217_books.id
                                                                AND a223_attr_author_ref.type_id = 226
                                                        
                                                          LEFT JOIN test_single_table2 a225_authors
                                                            ON a225_authors.id = a223_attr_author_ref.value
                                                                AND a225_authors.type_id = 225
                                                        
                                                          LEFT JOIN test_single_table2 a223_attr_page_count
                                                            ON a223_attr_page_count.parent_id = a217_books.id
                                                                AND a223_attr_page_count.type_id = 223
                                                        
                                                          LEFT JOIN test_single_table2 a219_attr_year
                                                            ON a219_attr_year.parent_id = a217_books.id
                                                                AND a219_attr_year.type_id = 219
                                                        WHERE
                                                          a217_books.parent_id != 0 AND a217_books.type_id = 217
                                                          AND a225_authors.value LIKE '%aro%'
                                                        LIMIT 1000;
                                                        



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

                                                        Но это исправляется очень простым кодом.

                                                        SQL, PHP
                                                        UPDATE `magento2`.`eav_attribute`
                                                        SET `frontend_input` = 'select', `source_model` = 'Magento\\Catalog\\Model\\Product\\Attribute\\Source\\Reference'
                                                        WHERE `attribute_id` = 10226;
                                                        


                                                        <?php
                                                        namespace Magento\Catalog\Model\Product\Attribute\Source;
                                                        
                                                        class Reference extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource
                                                        {
                                                            public $_productFactory;
                                                        
                                                            public function __construct(\Magento\Catalog\Model\ProductFactory $productFactory)
                                                            {
                                                                $this->_productFactory = $productFactory;
                                                            }
                                                        
                                                            public function getAllOptions()
                                                            {
                                                                if (!$this->_options) {
                                                                    $id = (int)$this->_attribute->getBackendType();
                                                        
                                                                    $collection = $this->_productFactory->create()->getCollection();
                                                                    $collection->addFieldToFilter('attribute_set_id', $id);
                                                                    $data = $collection->load()->toArray();
                                                        
                                                                    $this->_options = [];
                                                                    foreach ($data as $row) {
                                                                        $this->_options[] = ['value' => (int)$row['entity_id'], 'label' => $row['sku']];
                                                                    }
                                                                    array_unshift($this->_options, ['value' => '', 'label' => __('Not selected')]);
                                                                }
                                                        
                                                                return $this->_options;
                                                            }
                                                        }
                                                        



                                                        Также надо очистить кэш Magento. После этого появится выпадающий список. Хочу заметить, что для всего этого есть штатные механизмы, а значит ими кто-то пользуется и делает аналогичные действия.


                                                        Вывод.
                                                        Конкретно в части хранения ссылок на объекты подход нестандартный, в остальном структура данных аналогична. Считать ли это значимым изменением структуры? В патенте нет никаких указаний на это, зато есть размытые формулировки:

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

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


                                                        Также в чек-листе есть пункт 3, который разрешает несколько таблиц, которые отличаются типом поля «Value». Так как в такой таблице нельзя указать базовые типы (например если таблица для datetime), потому что их названия всегда строковые, то это означает, что в ней будет какое-то подмножество строк из одиночной таблицы. А это именно то, что делают приведенные запросы — разносят группы строк по разным таблицам.

                                                        Как IT-специалист считаю, что в текущем виде патент охватывает множество существующих систем.
                                                          0
                                                          Как IT-специалист считаю, что в текущем виде патент охватывает множество существующих систем.

                                                          Спасибо! Вы проделали большую работу, но давайте её закончим, прежде чем делать вывод. Вы поместили данные в таблицу и они даже отображаются в интерфейсе. Ок, но я вижу, что чтобы сделать из этого Интеграл, придется доработать Magento.

                                                          Пойдемте прямо по порядку, по пунктам:

                                                          1. Все данные хранятся в одной таблице (см. также п.3.), содержащей как минимум следующие поля: ID, Parent ID, Type ID, Value
                                                          Это есть
                                                          CREATE TABLE `catalog_product_entity_varchar` (
                                                          `value_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'Value ID',
                                                          `attribute_id` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Attribute ID',
                                                          `store_id` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Store ID',
                                                          `entity_id` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Entity ID',
                                                          `value` VARCHAR(255) NULL DEFAULT NULL COMMENT 'Value')


                                                          2. Для таблицы построены как минимум следующие индексы: ID; Type ID + Value; Parent ID + Type ID.

                                                          ID есть, это PRIMARY KEY (`value_id`)

                                                          Type ID + Value — не вижу

                                                          Parent ID + Type ID есть, это
                                                          UNIQUE INDEX `CATALOG_PRODUCT_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID` (`entity_id`, `attribute_id`, `store_id`)

                                                          Второй пункт не выполняется, что означает, что Magento как есть не конфликтует с патентом и не входит в упомянутое вами «множество существующих систем».

                                                          Если вы знаете какую-то реализацию Magento или нечто поверх неё, где этот индекс добавлен, то будет шанс, что и остальные пункты опросника тоже выполнятся. Давайте на них посмотрим.
                                                            +1
                                                            Ок, но я вижу, что чтобы сделать из этого Интеграл, придется доработать Magento.

                                                            Я говорил не про Интеграл, а про то, что написано в патенте. Там вообще слова "Интеграл" нет.


                                                            Type ID + Value — не вижу

                                                            Такой индекс есть в других таблицах, я об этом писал.


                                                            Второй пункт не выполняется, что означает, что Magento как есть не конфликтует с патентом

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


                                                            Если вы знаете какую-то реализацию Magento или нечто поверх неё

                                                            Знаю. Стоит у меня на компьютере. Там добавлен индекс на "Type ID + Value". По аналогии с другими таблицами.

                                                              0
                                                              Во-вторых, получается, что наличие патента запрещает создать такой индекс, так как это единственное отличие.

                                                              Я в некий момент даже полез искать, а можно ли вообще патентовать структуру индексов. Однозначного ответа не нашел, к сожалению. Но учитывая, что индексы могут создаваться ретроспективно, просто по анализу нагрузки на БД, получается редкостная фигня.

                                                                0
                                                                Такой индекс есть в других таблицах, я об этом писал.

                                                                Давайте вставим данные в другую таблицу и проверим еще раз опросником. Иначе условие не выполняется.

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

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

                                                                Даже если его нет в какой-то из таблиц, его может создать тот, что занимается поддержкой установленной системы, и кто вообще не в курсе про ваш патент.

                                                                Да, может. Это был я, например. Только я еще выкинул всё остальное ненужное и добавил что-то от себя. Получился совсем другой продукт.
                                                                Дело в том, что только добавление этого индекса ничего принципиально не решит и не является единственным отличием.
                                                                  0
                                                                  Давайте вставим данные в другую таблицу и проверим еще раз опросником. Иначе условие не выполняется.
                                                                  Почему не выполняется? Индекс в системе на аналогичных полях есть? Есть. В этих таблицах могут хранится значения атрибутов? Могут.

                                                                  Найдите систему, где этот индекс был добавлен хотя бы до публикации этой статьи
                                                                  Magento.

                                                                  git blame для customer_entity_int
                                                                  git blame для eav_entity_int
                                                                  MAGETWO-33889: Convert CE schema and data scripts to SPI classes
                                                                  3 years ago


                                                                  А вот еще пара ссылок, где рассматривается EAV и создание индексов на attribute_id + value, 2010 и 2017 год:
                                                                  http://qaru.site/questions/2674312/sql-optimization
                                                                  https://sqlinfo.ru/forum/viewtopic.php?id=7954

                                                                  Дело в том, что только добавление этого индекса ничего принципиально не решит и не является единственным отличием.

                                                                  Неправда, пока отличие только в индексе. Причем из десятка вообще возможных на 3 поля. Ладно бы миллионы вариантов были. А стандартный индекс новизной быть не может.

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

                                                                  и прогнать запросы, которые обсуждаются в статье, штатными средствами Magento.
                                                                  В патенте и чек-листе нет ничего про чьи-то штатные средства. Заявляемым изобретением является структура базы данных. Для сравнения структуры достаточно работы с SQL, правильная работа Magento вообще не требуется. Я это сделал просто чтобы показать, что структура настолько стандартная, что даже специальный веб-интерфейс писать не надо, достаточно правильно назвать поля.

                                                                  Давайте вставим данные в другую таблицу и проверим еще раз опросником. Иначе условие не выполняется.
                                                                  Ок, запросы вот.

                                                                  для customer_entity
                                                                  /* id_offset is not required for empty tables, magento tables are not empty */
                                                                  SET @id_offset = 10000;
                                                                  
                                                                  
                                                                  /* entities */
                                                                  
                                                                  INSERT INTO eav_attribute_set (attribute_set_id, attribute_set_name, entity_type_id)
                                                                  SELECT derived_types.id + @id_offset,
                                                                      derived_types.value,
                                                                      1 AS magento_product_entity_type
                                                                  FROM test_single_table AS derived_types
                                                                  WHERE derived_types.parent_id = 0 AND id != type_id AND type_id IN (
                                                                    SELECT id
                                                                    FROM test_single_table AS base_types
                                                                    WHERE parent_id = 0 AND id = type_id
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO eav_attribute_group (attribute_group_id, attribute_set_id, attribute_group_name, attribute_group_code)
                                                                  SELECT attribute_set_id, attribute_set_id, 'General', 'general'
                                                                  FROM eav_attribute_set
                                                                  WHERE attribute_set_id > @id_offset;
                                                                  
                                                                  
                                                                  /* attributes */
                                                                  
                                                                  INSERT INTO eav_attribute (attribute_id, attribute_code, frontend_label, backend_table, backend_type, entity_type_id, frontend_input, is_user_defined)
                                                                  SELECT data_attribute_names.id + @id_offset,
                                                                      CONCAT('a_', data_attribute_names.id + @id_offset),
                                                                      data_attribute_names.value,
                                                                      NULL,
                                                                      IF (data_attribute_names.type_id = 2, 'varchar', 'int'),
                                                                      1 AS magento_product_entity_type, 'text', 1
                                                                  FROM test_single_table AS data_attribute_names
                                                                  WHERE data_attribute_names.parent_id = 0 AND id != type_id AND type_id IN (
                                                                    SELECT id
                                                                    FROM test_single_table AS base_types
                                                                    WHERE parent_id = 0 AND id = type_id
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO eav_attribute (attribute_id, attribute_code, frontend_label, backend_table, backend_type, entity_type_id, frontend_input, is_user_defined)
                                                                  SELECT ref_attribute_names.id + @id_offset,
                                                                      CONCAT('a_', ref_attribute_names.id + @id_offset),
                                                                      ref_attribute_targets.value,
                                                                      'customer_entity_int',
                                                                      ref_attribute_names.type_id + @id_offset,
                                                                      1 AS magento_product_entity_type, 'text', 1
                                                                  FROM test_single_table AS ref_attribute_names
                                                                      INNER JOIN test_single_table AS ref_attribute_targets ON ref_attribute_targets.id = ref_attribute_names.type_id
                                                                  WHERE ref_attribute_names.parent_id = 0 AND ref_attribute_names.id != ref_attribute_names.type_id AND ref_attribute_names.type_id NOT IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS base_types
                                                                      WHERE parent_id = 0 AND id = type_id
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO eav_entity_attribute (sort_order, attribute_set_id, attribute_group_id, attribute_id, entity_type_id)
                                                                  SELECT attribute_structure.`order`,
                                                                      attribute_structure.parent_id + @id_offset,
                                                                      attribute_structure.parent_id + @id_offset,
                                                                      attribute_structure.type_id + @id_offset,
                                                                      1 AS magento_product_entity_type
                                                                  FROM test_single_table AS attribute_structure
                                                                  WHERE attribute_structure.parent_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS possible_enitities
                                                                      WHERE parent_id = 0 AND id != type_id AND type_id IN (
                                                                        SELECT id
                                                                        FROM test_single_table AS base_types
                                                                        WHERE parent_id = 0 AND id = type_id
                                                                      )
                                                                  );
                                                                  
                                                                  INSERT INTO customer_form_attribute (form_code, attribute_id)
                                                                  SELECT 'adminhtml_customer', attribute_id
                                                                  FROM eav_attribute
                                                                  WHERE eav_attribute.attribute_id > @id_offset
                                                                  UNION ALL
                                                                  SELECT 'customer_account_create', attribute_id
                                                                  FROM eav_attribute
                                                                  WHERE eav_attribute.attribute_id > @id_offset
                                                                  UNION ALL
                                                                  SELECT 'customer_account_edit', attribute_id
                                                                  FROM eav_attribute
                                                                  WHERE eav_attribute.attribute_id > @id_offset;
                                                                  
                                                                  
                                                                  INSERT INTO customer_eav_attribute (attribute_id)
                                                                  SELECT attribute_structure.type_id + @id_offset
                                                                  FROM test_single_table AS attribute_structure
                                                                  WHERE attribute_structure.parent_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS possible_enitities
                                                                      WHERE parent_id = 0 AND id != type_id AND type_id IN (
                                                                        SELECT id
                                                                        FROM test_single_table AS base_types
                                                                        WHERE parent_id = 0 AND id = type_id
                                                                      )
                                                                  );
                                                                  
                                                                  
                                                                  /* values */
                                                                  
                                                                  INSERT INTO customer_entity (entity_id, firstname)
                                                                  SELECT objects.id + @id_offset, objects.value
                                                                  FROM test_single_table AS objects
                                                                  WHERE objects.parent_id = 1 AND objects.id != 1;
                                                                  
                                                                  INSERT INTO customer_grid_flat (entity_id, name)
                                                                  SELECT objects.id + @id_offset, objects.value
                                                                  FROM test_single_table AS objects
                                                                  WHERE objects.parent_id = 1 AND objects.id != 1;
                                                                  
                                                                  
                                                                  INSERT INTO customer_entity_varchar (value_id, attribute_id, entity_id, value)
                                                                  SELECT data_attribute_values.id + @id_offset,
                                                                      data_attribute_values.type_id + @id_offset,
                                                                      data_attribute_values.parent_id + @id_offset,
                                                                      data_attribute_values.value
                                                                  FROM test_single_table AS data_attribute_values
                                                                  WHERE parent_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS objects
                                                                      WHERE objects.parent_id = 1 AND objects.id != 1
                                                                  )
                                                                  AND type_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS possible_attributes
                                                                      WHERE parent_id = 0 AND id != type_id AND type_id = 2
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO customer_entity_int (value_id, attribute_id, entity_id, value)
                                                                  SELECT data_attribute_values.id + @id_offset,
                                                                      data_attribute_values.type_id + @id_offset,
                                                                      data_attribute_values.parent_id + @id_offset,
                                                                      data_attribute_values.value
                                                                  FROM test_single_table AS data_attribute_values
                                                                  WHERE parent_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS objects
                                                                      WHERE objects.parent_id = 1 AND objects.id != 1
                                                                  )
                                                                  AND type_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS possible_attributes
                                                                      WHERE parent_id = 0 AND id != type_id AND type_id = 3
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO customer_entity_int (value_id, attribute_id, entity_id, value)
                                                                  SELECT ref_attribute_values.id + @id_offset,
                                                                      type_ref_attributes.id + @id_offset,
                                                                      ref_attribute_values.parent_id + @id_offset,
                                                                      ref_attribute_values.type_id + @id_offset
                                                                  FROM test_single_table AS ref_attribute_values
                                                                      INNER JOIN test_single_table AS objects ON objects.id = ref_attribute_values.parent_id
                                                                      INNER JOIN test_single_table AS ref_targets ON ref_targets.id = ref_attribute_values.type_id
                                                                      INNER JOIN test_single_table AS type_attributes ON type_attributes.parent_id = objects.type_id
                                                                      INNER JOIN test_single_table AS type_ref_attributes ON type_ref_attributes.id = type_attributes.type_id
                                                                  	     AND type_ref_attributes.type_id = ref_targets.type_id
                                                                  WHERE ref_attribute_values.parent_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS objects
                                                                      WHERE objects.parent_id = 1 AND objects.id != 1
                                                                  )
                                                                  AND ref_attribute_values.type_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS objects
                                                                      WHERE objects.parent_id = 1 AND objects.id != 1
                                                                  );
                                                                  


                                                                  для eav_entity
                                                                  /* id_offset is not required for empty tables, magento tables are not empty */
                                                                  SET @id_offset = 10000;
                                                                  
                                                                  
                                                                  /* entities */
                                                                  
                                                                  INSERT INTO eav_attribute_set (attribute_set_id, attribute_set_name, entity_type_id)
                                                                  SELECT derived_types.id + @id_offset,
                                                                      derived_types.value,
                                                                      4 AS magento_product_entity_type
                                                                  FROM test_single_table AS derived_types
                                                                  WHERE derived_types.parent_id = 0 AND id != type_id AND type_id IN (
                                                                    SELECT id
                                                                    FROM test_single_table AS base_types
                                                                    WHERE parent_id = 0 AND id = type_id
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO eav_attribute_group (attribute_group_id, attribute_set_id, attribute_group_name, attribute_group_code)
                                                                  SELECT attribute_set_id, attribute_set_id, 'General', 'general'
                                                                  FROM eav_attribute_set
                                                                  WHERE attribute_set_id > @id_offset;
                                                                  
                                                                  
                                                                  /* attributes */
                                                                  
                                                                  INSERT INTO eav_attribute (attribute_id, attribute_code, frontend_label, backend_table, backend_type, entity_type_id, frontend_input, is_user_defined)
                                                                  SELECT data_attribute_names.id + @id_offset,
                                                                      CONCAT('a_', data_attribute_names.id + @id_offset),
                                                                      data_attribute_names.value,
                                                                      NULL,
                                                                      IF (data_attribute_names.type_id = 2, 'varchar', 'int'),
                                                                      4 AS magento_product_entity_type, 'text', 1
                                                                  FROM test_single_table AS data_attribute_names
                                                                  WHERE data_attribute_names.parent_id = 0 AND id != type_id AND type_id IN (
                                                                    SELECT id
                                                                    FROM test_single_table AS base_types
                                                                    WHERE parent_id = 0 AND id = type_id
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO eav_attribute (attribute_id, attribute_code, frontend_label, backend_table, backend_type, entity_type_id, frontend_input, is_user_defined)
                                                                  SELECT ref_attribute_names.id + @id_offset,
                                                                      CONCAT('a_', ref_attribute_names.id + @id_offset),
                                                                      ref_attribute_targets.value,
                                                                      'catalog_product_entity_int',
                                                                      ref_attribute_names.type_id + @id_offset,
                                                                      4 AS magento_product_entity_type, 'text', 1
                                                                  FROM test_single_table AS ref_attribute_names
                                                                      INNER JOIN test_single_table AS ref_attribute_targets ON ref_attribute_targets.id = ref_attribute_names.type_id
                                                                  WHERE ref_attribute_names.parent_id = 0 AND ref_attribute_names.id != ref_attribute_names.type_id AND ref_attribute_names.type_id NOT IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS base_types
                                                                      WHERE parent_id = 0 AND id = type_id
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO eav_entity_attribute (sort_order, attribute_set_id, attribute_group_id, attribute_id, entity_type_id)
                                                                  SELECT attribute_structure.`order`,
                                                                      attribute_structure.parent_id + @id_offset,
                                                                      attribute_structure.parent_id + @id_offset,
                                                                      attribute_structure.type_id + @id_offset,
                                                                      4 AS magento_product_entity_type
                                                                  FROM test_single_table AS attribute_structure
                                                                  WHERE attribute_structure.parent_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS possible_enitities
                                                                      WHERE parent_id = 0 AND id != type_id AND type_id IN (
                                                                        SELECT id
                                                                        FROM test_single_table AS base_types
                                                                        WHERE parent_id = 0 AND id = type_id
                                                                      )
                                                                  );
                                                                  
                                                                  
                                                                  /* values */
                                                                  
                                                                  INSERT INTO eav_entity (entity_id, entity_type_id, attribute_set_id)
                                                                  SELECT objects.id + @id_offset, 4, objects.type_id + @id_offset
                                                                  FROM test_single_table AS objects
                                                                  WHERE objects.parent_id = 1 AND objects.id != 1;
                                                                  
                                                                  
                                                                  INSERT INTO eav_entity_varchar (value_id, attribute_id, entity_id, entity_type_id, value)
                                                                  SELECT data_attribute_values.id + @id_offset,
                                                                      data_attribute_values.type_id + @id_offset,
                                                                      data_attribute_values.parent_id + @id_offset,
                                                                      4,
                                                                      data_attribute_values.value
                                                                  FROM test_single_table AS data_attribute_values
                                                                  WHERE parent_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS objects
                                                                      WHERE objects.parent_id = 1 AND objects.id != 1
                                                                  )
                                                                  AND type_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS possible_attributes
                                                                      WHERE parent_id = 0 AND id != type_id AND type_id = 2
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO eav_entity_int (value_id, attribute_id, entity_id, entity_type_id, value)
                                                                  SELECT data_attribute_values.id + @id_offset,
                                                                      data_attribute_values.type_id + @id_offset,
                                                                      data_attribute_values.parent_id + @id_offset,
                                                                      4,
                                                                      data_attribute_values.value
                                                                  FROM test_single_table AS data_attribute_values
                                                                  WHERE parent_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS objects
                                                                      WHERE objects.parent_id = 1 AND objects.id != 1
                                                                  )
                                                                  AND type_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS possible_attributes
                                                                      WHERE parent_id = 0 AND id != type_id AND type_id = 3
                                                                  );
                                                                  
                                                                  
                                                                  INSERT INTO eav_entity_int (value_id, attribute_id, entity_id, entity_type_id, value)
                                                                  SELECT ref_attribute_values.id + @id_offset,
                                                                      type_ref_attributes.id + @id_offset,
                                                                      ref_attribute_values.parent_id + @id_offset,
                                                                      4,
                                                                      ref_attribute_values.type_id + @id_offset
                                                                  FROM test_single_table AS ref_attribute_values
                                                                      INNER JOIN test_single_table AS objects ON objects.id = ref_attribute_values.parent_id
                                                                      INNER JOIN test_single_table AS ref_targets ON ref_targets.id = ref_attribute_values.type_id
                                                                      INNER JOIN test_single_table AS type_attributes ON type_attributes.parent_id = objects.type_id
                                                                      INNER JOIN test_single_table AS type_ref_attributes ON type_ref_attributes.id = type_attributes.type_id
                                                                  	     AND type_ref_attributes.type_id = ref_targets.type_id
                                                                  WHERE ref_attribute_values.parent_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS objects
                                                                      WHERE objects.parent_id = 1 AND objects.id != 1
                                                                  )
                                                                  AND ref_attribute_values.type_id IN (
                                                                      SELECT id
                                                                      FROM test_single_table AS objects
                                                                      WHERE objects.parent_id = 1 AND objects.id != 1
                                                                  );
                                                                  





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

                                                                  Раз уж я разобрался в вашей системе, скажу пару слов в защиту. Умудриться засунуть все в одну таблицу это конечно интересное инженерное решение, но новизны в нем нет. Просто так никто не делает по причинам производительности. Если расширять признаки на несколько таблиц, то получаются аналоги существующих систем. Также есть нестандартная обработка ссылок, но технически это «костыль» — значение хранится в поле с другой семантикой и специальным образом обрабатывается. Индекс логически как бы разбивает строки на под-таблицы, но технически это обычное ускорение выборки по условию. Проблема в том, что вы заявляете что ничего подобного не существует и рассматриваете неопределенный круг модификаций.
                                                                    –2
                                                                    Почему не выполняется? Индекс в системе на аналогичных полях есть? Есть. В этих таблицах могут хранится значения атрибутов? Могут.

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

                                                                    Одна система — один проход опросника.

                                                                    Понятно, можно из кусочков насобирать: там один индекс берём, тут другой, из 2010 года тащим какой-то справочник. Только это не единая система, а набор кусочков, которые вместе не то что не обеспечат производительность, о которой я тут заявляю, а даже работать нормально не смогут вместе, без соответствующих доработок.

                                                                    Даже первые два пункта не пройдены: нет ссылки на систему, нет её структуры, нет её индексов. О чем говорить?

                                                                    И это мы еще не добрались, что в этой системе описаны типы, используемые для создания других типов и их реквизитов, которые описывают наборы связанных данных (связанных как раз через ссылки на тип или объект).
                                                                      0
                                                                      Индекс в системе на аналогичных полях есть? Есть. В этих таблицах могут хранится значения атрибутов? Могут.
                                                                      берете кусок скрипта или запроса и вставляете в качестве подтверждения.

                                                                      Ссылки на наличие индексов я привел. Запросы тоже. В чем проблема?


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

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


                                                                      Ведь это не отдельные системы, а куски систем

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


                                                                      Одна система — один проход опросника.

                                                                      Опросник работает как для отдельных подсистем Product EAV и Customer EAV, так и для Magento в целом.


                                                                      Понятно, можно из кусочков насобирать: там один индекс берём, тут другой

                                                                      Простите, где и что я собирал из кусочков? Вы попросили провести запросы на других таблицах, я это сделал.


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

                                                                      Что вас не устраивает в приведенных примерах? Это системы, и они были до публикации статьи. Индекс в них есть. Применяйте опросник к каждой из них, как собирались.


                                                                      а даже работать нормально не смогут вместе, без соответствующих доработок.

                                                                      Простите, вы утверждаете, что скриншоты работоспособной системы я подделал?


                                                                      Даже первые два пункта не пройдены: нет ссылки на систему, нет её структуры, нет её индексов.

                                                                      Система Magento находится на GitHub, ссылку я приводил.
                                                                      Если вы про примеры с индексами, то там тоже из запросов и обсуждений видно структуру таблиц и индексов.


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

                                                                      Это и есть EAV.

                                                                      0
                                                                      Раз уж я разобрался в вашей системе, скажу пару слов в защиту. Умудриться засунуть все в одну таблицу это конечно интересное инженерное решение, но новизны в нем нет. Просто так никто не делает по причинам производительности.

                                                                      Вот и славно. Никто так не делает, как вы сами подтвердили.
                                                                      А вся разница как раз в этом:
                                                                      Индекс логически как бы разбивает строки на под-таблицы, но технически это обычное ускорение выборки по условию.


                                                                      Если расширять признаки на несколько таблиц, то получаются аналоги существующих систем.

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

                                                                      Также есть нестандартная обработка ссылок, но технически это «костыль» — значение хранится в поле с другой семантикой и специальным образом обрабатывается.

                                                                      В чем проблема с семантикой? В любом случае ссылка делается по ID, а его запись всегда является типом.
                                                                      «костыль» — это StoreID в Magento, если я правильно понимаю это как ID магазина. Вот это действительно адский ад.

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

                                                                      Доказать вашу правоту очень просто: берете опросник, любой аналог и за 5 минут кровавого копи/пейста показываете идентичность.
                                                                        +1
                                                                        Никто так не делает, как вы сами подтвердили.

                                                                        Давайте вы не будете играть словами? В production-ready системах обычно так никто не делает по причинам производительности. Мои слова не означают, что никто так не делал для конретной задачи или для тестов.


                                                                        А вся разница как раз в этом:

                                                                        Если разница в этом, то Magento является аналогом вашей системы, потому что в ней точно то же самое. Такие же индексы с таким же эффектом.
                                                                        И нет, п.3 опросника явно разрешает разные таблицы.


                                                                        Вот здесь я не согласен, покуда типы не хранятся в таблице единой структуры, а хардкодятся в запросах

                                                                        Где там что хардкодится? Типы из вашей системы я нигде не хардкодил. Все они хранятся в таблице единой структуры.
                                                                        И да, возвращаемся к вопросу, что такое единая структура. Переименование полей и таблиц, как вы сами сказали, не считается изменением структуры.


                                                                        В чем проблема с семантикой?

                                                                        ID объекта это значение, а не тип. Объекты находятся в секции данных.


                                                                        «костыль» — это StoreID в Magento, если я правильно понимаю это как ID магазина. Вот это действительно адский ад.

                                                                        Причем здесь специфичные поля Magento? Разговор идет о вашей структуре.


                                                                        Доказать вашу правоту очень просто: берете опросник, любой аналог

                                                                        Давайте. Вы же сами предлагали. Вот есть система Magento с подсистемами EAV Product, Customer, EAV Entity.
                                                                        Возьмите EAV Entity и проверьте по опроснику.

                                                                          0
                                                                          Возьмите EAV Entity и проверьте по опроснику.

                                                                          Взял здесь, проверяю, несмотря на очевидно иное предназначение этой таблицы, нежели в описанном мной конструкторе.
                                                                          Итак,
                                                                          1. Все данные хранятся в одной таблице (см. также п.3.), содержащей как минимум следующие поля: ID, Parent ID, Type ID, Value
                                                                          Структура и индексы
                                                                                  /**
                                                                                   * Create table 'eav_entity'
                                                                                   */
                                                                                  $table = $installer->getConnection()->newTable(
                                                                                      $installer->getTable('eav_entity')
                                                                                  )->addColumn(
                                                                                      'entity_id</b>',
                                                                                      \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
                                                                                      null,
                                                                                      ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
                                                                                      'Entity Id'
                                                                                  )->addColumn(
                                                                                      'entity_type_id',
                                                                                      \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
                                                                                      null,
                                                                                      ['unsigned' => true, 'nullable' => false, 'default' => '0'],
                                                                                      'Entity Type Id'
                                                                                  )->addColumn(
                                                                                      'attribute_set_id',
                                                                                      \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
                                                                                      null,
                                                                                      ['unsigned' => true, 'nullable' => false, 'default' => '0'],
                                                                                      'Attribute Set Id'
                                                                                  )->addColumn(
                                                                                      'increment_id',
                                                                                      \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                                                                                      50,
                                                                                      ['nullable' => true, 'default' => null],
                                                                                      'Increment Id'
                                                                                  )->addColumn(
                                                                                      'parent_id',
                                                                                      \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
                                                                                      null,
                                                                                      ['unsigned' => true, 'nullable' => false, 'default' => '0'],
                                                                                      'Parent Id'
                                                                                  )->addColumn(
                                                                                      'store_id',
                                                                                      \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
                                                                                      null,
                                                                                      ['unsigned' => true, 'nullable' => false, 'default' => '0'],
                                                                                      'Store Id'
                                                                                  )->addColumn(
                                                                                      'created_at',
                                                                                      \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
                                                                                      null,
                                                                                      ['nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT],
                                                                                      'Created At'
                                                                                  )->addColumn(
                                                                                      'updated_at',
                                                                                      \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
                                                                                      null,
                                                                                      ['nullable' => false, 'default' => \Magento\Framework\DB\Ddl\Table::TIMESTAMP_INIT_UPDATE],
                                                                                      'Updated At'
                                                                                  )->addColumn(
                                                                                      'is_active',
                                                                                      \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
                                                                                      null,
                                                                                      ['unsigned' => true, 'nullable' => false, 'default' => '1'],
                                                                                      'Defines Is Entity Active'
                                                                                  )->addIndex(
                                                                                      $installer->getIdxName('eav_entity', ['entity_type_id']),
                                                                                      ['entity_type_id']
                                                                                  )->addIndex(
                                                                                      $installer->getIdxName('eav_entity', ['store_id']),
                                                                                      ['store_id']
                                                                                  )->addForeignKey(
                                                                                      $installer->getFkName('eav_entity', 'entity_type_id', 'eav_entity_type', 'entity_type_id'),
                                                                                      'entity_type_id',
                                                                                      $installer->getTable('eav_entity_type'),
                                                                                      'entity_type_id',
                                                                                      \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
                                                                                  )->addForeignKey(
                                                                                      $installer->getFkName('eav_entity', 'store_id', 'store', 'store_id'),
                                                                                      'store_id',
                                                                                      $installer->getTable('store'),
                                                                                      'store_id',
                                                                                      \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE
                                                                                  )->setComment(
                                                                                      'Eav Entity'
                                                                                  );
                                                                                  $installer->getConnection()->createTable($table);



                                                                          Вижу entity_id, entity_type_id, parent_id
                                                                          Упс… Value не нашел.
                                                                            0
                                                                            — Найдите систему, где этот индекс был добавлен хотя бы до публикации этой статьи, и мы проверим оставшиеся пункты (не забыв перепроверить и первые два).
                                                                            — Вот есть система Magento с подсистемами EAV Product, Customer, EAV Entity.
                                                                            — Взял здесь, проверяю, несмотря на очевидно иное предназначение этой таблицы

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


                                                                            eav_attribute
                                                                            eav_attribute_set
                                                                            eav_entity_attribute
                                                                            eav_entity
                                                                            eav_entity_varchar
                                                                            eav_entity_int

                                                                            Также в запросах есть eav_attribute_group, но там константы и ее можно не считать.

                                                                              0
                                                                              Хорошо, я нашел таблицу eav_entity_int, в которой есть все поля и индексы.
                                                                              Теперь проверим пункты 4 и 5 опросника для этой таблицы: покажите как в этой системе описаны типы, используемые для создания других типов и их реквизитов, которые описывают наборы связанных данных. В этой же таблице или любой другой, удовлетворяющей пунктам 1 и 2.
                                                                                0
                                                                                покажите как в этой системе описаны типы, используемые для создания других типов и их реквизитов, которые описывают наборы связанных данных

                                                                                Разумеется, штатными средствами Маженты или опубликованными до этой статьи. Ваши скрипты миграции мы в этом случае учитывать не можем.
                                                                                  0

                                                                                  Ну здрасьте-приехали, как это не можем? А как я туда данные из вашей таблицы помещать должен?


                                                                                  покажите как в этой системе описаны типы

                                                                                  Строки для введения базовых типов не нужны, так как используются специальные таблицы, разрешенные пунктом 3.
                                                                                  Тем не менее, их список присутствует в коде приложения. Значения из этого списка помещаются в eav_attribute.backend_type при задании типа атрибута.


                                                                                  Если придерживаться варианта "по возможности хранить все в одной таблице", то производные типы из вашей схемы помещаются в таблицу eav_attribute_set.
                                                                                  Если придерживаться семантики названий, то производные типы помещаются в таблицу eav_entity_type и для каждого будет один attribute set.
                                                                                  В запросах я придерживался первго варианта как более близкого к предмету обсуждения.


                                                                                  Названия и базовый тип реквизитов (атрибутов) производных типов находятся в eav_attribute.


                                                                                  Связь атрибутов с сущностями находится в eav_entity_attribute.


                                                                                  Объекты хранятся в eav_entity.


                                                                                  Значения атрибутов хранятся в eav_entity_int и аналогичных.

                                                                          0
                                                                          Вот здесь я не согласен, покуда типы не хранятся в таблице единой структуры, а хардкодятся в запросах — это уже не аналоги.

                                                                          У меня к вам есть вопрос, кстати. Вот у вас есть колонка value. Какого она типа?

                                                                      0
                                                                      Знаю. Стоит у меня на компьютере. Там добавлен индекс на «Type ID + Value». По аналогии с другими таблицами.

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

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

                                                                Самое читаемое