Так представление это и есть функция, точнее набор функций. Но в данном случае так как представление из одной функции, можно считать, что это и есть функция.
который обозначает конкретную бизнес-сущность
Тут вопрос что такое сущность. Если вы имеете ввиду под этим класс (для которого создаются объекты), то последняя цена — не класс.
Foreign key логически и есть join, то есть таблица, ключевое поле и поле связывания.
Еще раз, PHP это не хранилище данных, и без обращений к хранилищу данных работать с данными принципиально нельзя. Если бы не SQL, писали бы на каком-нибудь другом синтаксисе, начиная с банального fread/fwrite по нужным смещениям.
Это SQL замаскированный под PHP. А тощая корова — не газель.
хотя эта связь многие-ко-многим явно присутствует в бизнес-логике
Я вообще не понимаю этих суррогатов многие-ко-многим. А если четыре ключа в таблице, то это многие-ко-многим-ко-многим-ко-многим?
где никаких представлений нет оно не может быть в виде класса, как и любая другая таблица
Потому что логически такого объекта как последняя цена нет. Есть функция последняя цена от двух объектов (склада и товара).
обычно можно сделать по-другому
Это всегда можно было делать по другому. С тех пор как появились If'ы. Но если все делать If'ами получается адский лапше-код.
решается нормализацией данных — имеем отдельно таблицы Location и Airport, и в Airport также имеем foreign key к Location. Просто, оптимизируемо, и — внимание! — логически верно, так как
Так Airport это не Location. А вот Capital это City. А Роза — цветок.
Я не совсем понял, вы считаете что наследование не нужно и всегда можно обойтись композицией? Очень смелое утверждение.
То почему произвольными объектами плохо я писал в статье в разделе про наследование и полиморфизм. И как бы я хотел тоже там писал. И уже несколько раз тут отвечал, почему наследование таблиц не подходит.
Сужу исключительно по наличию нескольких типов с одинаковым названием.
В lsFusion просто множественное наследование есть. Это одинаковые классы. Просто так граф в виде дерева выглядит.
Затем же зачем и в Java. Модульность и расширяемость. То есть нужен новый функционал, добавляем новые классы, наследуем, добавляем в абстрактные представления реализацию для этих классов. Иначе спагетти-код как в 1С.
А можно все сделать нормально классами, наследованием и полиморфизмом. Вот пример кусочка дерева классов например:
Но так делать можно только на очень низконагруженых системах. Даже на системе со средней нагрузкой это может породить проблемы.
А что в вашем понятии низконагруженные системы? Системы класса ERP с террабайтными базами и почти тысячей одновременных пользователей это какие системы? Вот у меня сейчас перед глазами с пяток таких систем, использующих описанный в статье подход.
Но тут вопрос то в другом. Остаток вам все равно надо хранить и обновлять инкрементально. Иначе система просто ляжет из-за нарушения баланса чтения и записи. Весь вопрос, как это делать. Задать декларативно представление и сказать серверу — теперь это твои проблемы, крутись как хочешь. Или самому разработчику императивно руками обновлять соответствующую таблицу. Не говоря о том что поддержать все случаи изменений очень тяжело, а значит нужно прибегать к разным хакам вроде запретов и перепроведений, о чем я и писал в статье.
При это умалчивается, что в принципе в задаче пересчета остатков заложен конфликт, который приходится разрешать в любой системе
Вы про update conflict'ы? Ну так с ними то как раз SQL сервера более менее сами справляются. От разработчика нужно только перестарт транзакции поддержать (хотя это тоже не такая тривиальная задача). Но это перпендикулярно задаче материализации представлений.
Или мы строго учитываем остатки, или быстро, при этом в быстро еще заложено или быстро пишем, или быстро читаем.
Проблема в том что пользователю надо и то и другое. А дальше вопрос балансов. Грубо говоря пользователь обычно согласен лишние 500мс подождать на проведении документа, но чтобы все остальное летало.
т.е. одну проблему, мы поменяли на другую
Почему поменяли? Решили одну. Ну а вторую значительно упростили. Так, достаточно обновлять поле «остаток у поставщика» как первичные данные в своей таблице, а дальше уже внутри сервер включает свою магию с материализациями, событиями и т.п.
Это обычно если логика достаточно простая (то есть немного сложнее CRUD). Если вам нужно делать группировки, композиции, разбиения, упорядочивания и у вас будут десятки таких показателей на каждой форме, на noSQL это будет очень тяжело сделать.
Собственно в свое время было много холиваров на тему SQL vs noSQL, и, соответственно, много историй, когда людям с noSQL на SQL приходилось переходить из-за невозможности сделать простейший JOIN.
Я имел ввиду, что ок наследование первичных данных (таблиц) вы сделаете, а что с наследованием вычислений (когда не просто return field) предполагается надо делать?
1) Большой кэш для предвычисленных затратных вещей на Redis. Обслуживание кэша не на SQL, а на прикладном языке.
ACID'а не будет, то есть подойдет только для аналитики по большому счету, во всяком случае в бизнес-приложениях.
2) Запуск длительных запросов в виде отдельных скриптов на других языках. Добавление прогрессбаров в конце концов.
Тоже самое что и в пункте 1). Хотя во многих случаях действительно помогает (в lsFusion это асинхронные события).
3) Решение проблемы наследования на уровне ORM — в PHP в Doctrine, точно можно делать красивое наследование. Затем же из PHP работать с сущностями и построитель запросов ORM сам соберет данные из нужных таблиц.
Там в ORM те же и проблемы что в Oracle будут (в статье есть), то есть N+1 и непонятно что делать с таблицами скажем с 3 ключами.
SQL не идеален, но он уже давно под капотом у других языков. И в целом системы, где используется деревянный SQL, сгенерированный ORM или неискушенными в ORM программистами легче поддерживать, чем базу со вьюшками, хранимками и прочими радостями.
Он все же обычно не под капотом, а сбоку. То есть у большинства других языков есть трансляторы в SQL, типа как в LINQ, Hibernate(HQL) или 1С.
Но и на голом SQL где очень важна производительность (банки, финансы, ритейл), тоже много чего пишут. Откуда по вашему у Oracle оборот 10млрд, не как у подложки же под SQL.
Вот и надо говорить о проблемах и альтернативах. Возможно для кого-то некоторое снижение производительности будет приемлемо для уменьшения вероятности ошибки и увеличения удобности разработки.
Проблемы и альтернативы это разные вещи. Хотя в статье как раз есть много и про альтернативы (и как это сделано в lsFusion).
Но я категорически не согласен с выводами статьи про то, что коммерческие системы не соответствуют чему-то там. Нельзя обобщать два неудачных случайных примера до всех коммерческих систем.
Там в том то и дело что их больше чем 2. Я специально все собрал в одну статью, чтобы было видно что их гораздо больше, и что производители СУБД везде смогли накосячить.
Ну и я не говорил, что они чему-то не соответствуют. Тут вот какая ситуация. Смотрите мы когда делали платформу над SQL и компилировали запросы, к нам постоянно приходили разработчики и говорили вот мы тут такую логику нахерачили и у нас что-то тормозит. Ты им отвечаешь, смотрите вот видите запрос, видите план, видите как СУБД косячит. Знаете что они отвечали, а нам какая разница, чьи это проблемы, формируйте такие запросы, чтобы они выполнялись быстро. Понятно что это вопрос бренда. Будь у нас бренд Oracle или MS SQL мы бы всех посылали на три буквы и говорили бы, что «просто вы не так держите», крутитесь сами как хотите. Но нет, нам приходилось решать проблемы СУБД, мы конечно в конце концов все их решили, но сам факт почему это приходилось делать нам, хотя мы могли бы сфокусироваться именно на верхнем уровне, конечно дико раздражал. Собственно так и родилась эта статья.
Разработчик только указывает что-то типа Entity::find()->where(...)->with('relationship').
Ну так я и говорю разработчик делает, а не ORM.
Вызываем логику для каждой сущности
Ну так вы когда вызываете логику каждой сущности у вас N+1 и будет.
Или еще хуже вам эту логику дублировать придется. Сначала чтобы избежать проблемы N+1 докинуть в первый запрос все нужные данные, а потом еще раз повторить эту логику в ORM с обработкой этих данных (дублирование конечно не полное будет, но процентов на 80). Ну и вариантов выстрелить в ногу, если забыли, не те данные считали и т.п.
Но вообще конкретно эта статья не про ORM, тут как раз про то когда бизнес-логику на SQL делают — в банках и других финансах, ритейле это очень распространено.
Я у них ACID'а что-то не вижу, а учитывая, что они позиционируются как analytics database, то возможно его там и нет. А я имел ввиду Distributed SQL сервера именно с ACID.
Они не входят в стандарт, про что вам сразу и сказали. И более того они не декларативны по своей сути.
Стандарт SQL вообще очень забавная штука. Они UPDATE и агрегирующие функции стандартизировать не могут. Ну я нигде и не говорил про стандарт, это чисто практическая статья, а хранимки и триггеры при разработке на SQL используются очень часто.
В каком месте и каким боком они к SQL подходят и как они туда ложатся? Единственное применение наследование которое я использовал в том же PostgreSQL это для секционирования таблиц, для другого оно как-то плохо применимо. Приведите хоть один вариант использования когда это прям дает какой-то внятный профит. Я вот не припомню ни одного применения где полиморфизм и наследование давали какой-то внятный эффект. Более того чем дальше тем больше в нем разочаровываются и откатываются к функцинальщине.
Функциональщина перпендикулярна наследованию и полиморфизму. А вообще возьмите крупный проект на C++ или Java и посчитайте там количество extends /: и abstract / virtual.
Так можно делать только с первичными данными (то есть с таблицами). Запросы (то есть VIEW) в реализацию добавлять нельзя (в смысле что VIEW наследовать от таблиц / друг от друга). То есть это как если бы вам в классическом ООП дали бы abstract, и сказали что в реализации можно только return field делать.
Плюс так можно делать при индуктивном задании логики. При дедуктивном, когда создаются таблицы со своими именами полей, а потом функционал надо обобщить в абстрактный так тоже не сработает.
Но вообще надо было конечно в примере GROUP BY написать или что-то по сложнее, чтобы вопросов не было.
В пункте Проблема N+1 есть уточнение про расширения вроде.
А полиморфизм с наследованием это очень даже декларативные концепции. Я бы даже сказал более декларативные чем SQL, если декларативностью вообще меряться можно. И я в статье написал зачем:
возможности эффективно декомпозировать задачи, а также не наращивать технический долг по мере роста сложности системы
SELECT SUM(cc.ls)
FROM Product pr
LEFT JOIN ( SELECT product, SUM(shipment/1000) AS ls
FROM shipmentDetail s
GROUP BY product
) cc on cc.product = pr.id
;
Так она не join протолкнула, а значение. То есть PPD там есть, JPPD нет.
А как оно должно в вашем запросе работать, какой предикат протолкнется в подзапрос, если условие по имени продукта, а подзапрос про имена продуктов не знает?
Я в общем то в статье описал как это делают другие субд. Но по сути да, они вставляют что-то вроде lateral, как вы и написали.
Что кстати весьма забавно, что lateral PostgreSQL поддерживает, а JPPD нет.
Тут вопрос что такое сущность. Если вы имеете ввиду под этим класс (для которого создаются объекты), то последняя цена — не класс.
Foreign key логически и есть join, то есть таблица, ключевое поле и поле связывания.
Это SQL замаскированный под PHP. А тощая корова — не газель.
Я вообще не понимаю этих суррогатов многие-ко-многим. А если четыре ключа в таблице, то это многие-ко-многим-ко-многим-ко-многим?
Потому что логически такого объекта как последняя цена нет. Есть функция последняя цена от двух объектов (склада и товара).
Это всегда можно было делать по другому. С тех пор как появились If'ы. Но если все делать If'ами получается адский лапше-код.
Да, но это объект изменения цены — PriceChange, но никак не последняя цена — LastPrice.
Так Airport это не Location. А вот Capital это City. А Роза — цветок.
Я не совсем понял, вы считаете что наследование не нужно и всегда можно обойтись композицией? Очень смелое утверждение.
То почему произвольными объектами плохо я писал в статье в разделе про наследование и полиморфизм. И как бы я хотел тоже там писал. И уже несколько раз тут отвечал, почему наследование таблиц не подходит.
В lsFusion просто множественное наследование есть. Это одинаковые классы. Просто так граф в виде дерева выглядит.
А можно все сделать нормально классами, наследованием и полиморфизмом. Вот пример кусочка дерева классов например:
А что в вашем понятии низконагруженные системы? Системы класса ERP с террабайтными базами и почти тысячей одновременных пользователей это какие системы? Вот у меня сейчас перед глазами с пяток таких систем, использующих описанный в статье подход.
Но тут вопрос то в другом. Остаток вам все равно надо хранить и обновлять инкрементально. Иначе система просто ляжет из-за нарушения баланса чтения и записи. Весь вопрос, как это делать. Задать декларативно представление и сказать серверу — теперь это твои проблемы, крутись как хочешь. Или самому разработчику императивно руками обновлять соответствующую таблицу. Не говоря о том что поддержать все случаи изменений очень тяжело, а значит нужно прибегать к разным хакам вроде запретов и перепроведений, о чем я и писал в статье.
Вы про update conflict'ы? Ну так с ними то как раз SQL сервера более менее сами справляются. От разработчика нужно только перестарт транзакции поддержать (хотя это тоже не такая тривиальная задача). Но это перпендикулярно задаче материализации представлений.
Проблема в том что пользователю надо и то и другое. А дальше вопрос балансов. Грубо говоря пользователь обычно согласен лишние 500мс подождать на проведении документа, но чтобы все остальное летало.
Почему поменяли? Решили одну. Ну а вторую значительно упростили. Так, достаточно обновлять поле «остаток у поставщика» как первичные данные в своей таблице, а дальше уже внутри сервер включает свою магию с материализациями, событиями и т.п.
Собственно в свое время было много холиваров на тему SQL vs noSQL, и, соответственно, много историй, когда людям с noSQL на SQL приходилось переходить из-за невозможности сделать простейший JOIN.
ACID'а не будет, то есть подойдет только для аналитики по большому счету, во всяком случае в бизнес-приложениях.
Тоже самое что и в пункте 1). Хотя во многих случаях действительно помогает (в lsFusion это асинхронные события).
Там в ORM те же и проблемы что в Oracle будут (в статье есть), то есть N+1 и непонятно что делать с таблицами скажем с 3 ключами.
Он все же обычно не под капотом, а сбоку. То есть у большинства других языков есть трансляторы в SQL, типа как в LINQ, Hibernate(HQL) или 1С.
Но и на голом SQL где очень важна производительность (банки, финансы, ритейл), тоже много чего пишут. Откуда по вашему у Oracle оборот 10млрд, не как у подложки же под SQL.
Проблемы и альтернативы это разные вещи. Хотя в статье как раз есть много и про альтернативы (и как это сделано в lsFusion).
Пока писал уже ответили на остальное.
Там в том то и дело что их больше чем 2. Я специально все собрал в одну статью, чтобы было видно что их гораздо больше, и что производители СУБД везде смогли накосячить.
Ну и я не говорил, что они чему-то не соответствуют. Тут вот какая ситуация. Смотрите мы когда делали платформу над SQL и компилировали запросы, к нам постоянно приходили разработчики и говорили вот мы тут такую логику нахерачили и у нас что-то тормозит. Ты им отвечаешь, смотрите вот видите запрос, видите план, видите как СУБД косячит. Знаете что они отвечали, а нам какая разница, чьи это проблемы, формируйте такие запросы, чтобы они выполнялись быстро. Понятно что это вопрос бренда. Будь у нас бренд Oracle или MS SQL мы бы всех посылали на три буквы и говорили бы, что «просто вы не так держите», крутитесь сами как хотите. Но нет, нам приходилось решать проблемы СУБД, мы конечно в конце концов все их решили, но сам факт почему это приходилось делать нам, хотя мы могли бы сфокусироваться именно на верхнем уровне, конечно дико раздражал. Собственно так и родилась эта статья.
Ну об этом в общем-то и статья.
Ну так я и говорю разработчик делает, а не ORM.
Ну так вы когда вызываете логику каждой сущности у вас N+1 и будет.
Или еще хуже вам эту логику дублировать придется. Сначала чтобы избежать проблемы N+1 докинуть в первый запрос все нужные данные, а потом еще раз повторить эту логику в ORM с обработкой этих данных (дублирование конечно не полное будет, но процентов на 80). Ну и вариантов выстрелить в ногу, если забыли, не те данные считали и т.п.
Но вообще конкретно эта статья не про ORM, тут как раз про то когда бизнес-логику на SQL делают — в банках и других финансах, ритейле это очень распространено.
Стандарт SQL вообще очень забавная штука. Они UPDATE и агрегирующие функции стандартизировать не могут. Ну я нигде и не говорил про стандарт, это чисто практическая статья, а хранимки и триггеры при разработке на SQL используются очень часто.
Функциональщина перпендикулярна наследованию и полиморфизму. А вообще возьмите крупный проект на C++ или Java и посчитайте там количество extends /: и abstract / virtual.
Плюс так можно делать при индуктивном задании логики. При дедуктивном, когда создаются таблицы со своими именами полей, а потом функционал надо обобщить в абстрактный так тоже не сработает.
Но вообще надо было конечно в примере GROUP BY написать или что-то по сложнее, чтобы вопросов не было.
А полиморфизм с наследованием это очень даже декларативные концепции. Я бы даже сказал более декларативные чем SQL, если декларативностью вообще меряться можно. И я в статье написал зачем:
То есть вот такой запрос:
Дает вот такой план:
А его то как раз эффективнее вот так выполнять:
То есть PostgreSQL не принимает никакого решения, а что сказали то и делаю.
Так она не join протолкнула, а значение. То есть PPD там есть, JPPD нет.
Я в общем то в статье описал как это делают другие субд. Но по сути да, они вставляют что-то вроде lateral, как вы и написали.
Что кстати весьма забавно, что lateral PostgreSQL поддерживает, а JPPD нет.