по моему мнению, идея языка, компилирующегося в SQL — правильная идея. В статье описаны несколько проблем, которые встречаются регулярно. Ну и опциональная материализация, на самом деле, может решить очень много проблем с базой данных.
У меня есть практический вопрос. Предположим, у меня есть проект с бизнес логикой на MS SQL. Возможно ли 'вместо MS SQL' использовать lsFusion + PostgreSQL (только как бек енд сервер)?
да, согласен, просто неверно прочитал «Например, есть HAVING, который полностью эквивалентен подзапросу и WHERE на подзапрос» — почему-то у менять осталось впечатление, что будет что-то вроде: where (select sum(salary) ...) > 100000
Не совсем согласен, что HAVING не нужен, но согласен, что функционально его можно заменить. Можете привести код на lsfusion или Вы не по этой части?
Я в основном работаю на Ms Sql и там никогда не было триггеров per row, триггеры только per statement. То есть в вашем конкретном случае в на один стейтмент один запуск триггера и в контексте триггера будут две таблицы inserted и deleted, которые будут содержать данные до и после апдейта (100 записей в каждой)
То есть для Ms Sql ответ будет — на один стейтмент один запуск триггера.
В Postgres как минимум в версии 11 уже были два варианта — можно объявить триггер per row а можно объявить триггер per statement. Соответственно, per statement работает так же, как и Ms Sql, а per row реально запустит триггер 100 раз.
Я не большой спец в Postgres поэтому не могу дать 100% гарантии что per row триггер не оптимизируется, но когда я около полугода назад это тестировал на 10000 записях, пустой per row триггер работал явно медленнее пустого per statement триггера.
Давайте так, тут по хорошему надо не запросами, а планами меряться. И в нашем и в вашем все будет хорошо, чудеса начнутся когда вы их материализовать захотите. Но в любом случае спор уже куда то не туда ушел.
да я не хотел меряться. Вы просто утверждаете, что lsfusion как язык программирования более выразителен для работы с данными, чем sql, а я с этим не согласен. Поэтому хотел увидеть, как в вашем случае выглядит пример такого запроса (элементарного в sql) и как будет выглядеть его модификация при необходимости.
да, абсолютно. Все селект запросы в sql это типичные функции без сайд эффектов.
Собственно, мы с моим коллегой вынашиваем идею языка-надстройки над sql который позволит так работать — передавать запросы как параметры в функции, возвращать запросы и т.д. Сейчас, в принципе, можно такое сделать через курсоры, но это сразу убивает всю set-based-thinking парадигму.
Как обычно, времени на это нет.
Пытаюсь внедрить эту идею в мозги разработчиков Postgres на митапах :) Потому что, насколько знаю, Postgresовская система типов лучшая среди всех RDBMS, было бы логично её расширить.
ну вот, а вы говорили оптимизатор Postgres глупый. Но все равно этот запрос не эквивалентен моему. Постараюсь объяснить
1. Мой первый запрос — группировка сотрудника по департаментам -> фильтр по результатам агрегации.
2. Мой второй запрос — фильтр сотрудников -> группировка по департаментам -> фильтр по результатам агрегации.
спасибо, я абсолютно согласен с этим заявлением. Здесь это усугубляется тем, что предполагается, что на этой платформе разрабатывают бизнес пользователи, а не разработчики. Судя по опыту, бизнес пользователем уж точно всё равно что будет завтра с этими функциями, им надо сейчас сделать чтоб отчёт работал.
ваш последний пример, кстати, не эквивалентен моему запросу. И linq, как раз, именно в этом примере обыгрывает sql за счет комбинируемости. В sql я могу сделать так
select
department_id,
array_agg(id) as employees,
sum(tax) as tax
from employee
group by
department_id
having
sum(salary) > 100000 and
count(*) < 10 and
count(distinct gender) = 2
а потом мне надо отфильтровать employee по каким-то критериям, мне придется править исходный запрос
select
department_id,
array_agg(id) as employees,
sum(tax) as tax
from employee
where
salary > 10000
group by
department_id
having
sum(salary) > 100000 and
count(*) < 10 and
count(distinct gender) = 2
и в sql я не могу сделать так, чтобы employee была переменной (я могу конечно сделать темп таблицу, и использовать её, но это перекладывание данных). А вот что я хотел бы уметь делать в sql
set @employee = (select * from employee);
--optional
set @employee = (select * from @employee where salary > 10000);
select
department_id,
array_agg(id) as employees,
sum(tax) as tax
from @employee
group by
department_id
having
sum(salary) > 100000 and
count(*) < 10 and
count(distinct gender) = 2;
и linq как раз тут просто обыгрывает стандартный сиквел на порядки.
а всего-то надо сделать query first class citizen
Вот это очень странное утверждение. Я как бы тоже неплохо разбираюсь в запросах, но сказать на сто процентов какой запрос будет эффективнее без реального плана выполнения я не смогу. Более того, на разной статистике у разных запросов будут разные планы и, соответственно, разная эффективность.
я в данном случае говорю только об эквивалентности запроса, не об эффективности. То есть логика запроса должна быть одинаковая
немного переформатировал запрос, чтобы стало понятнее. Но где там фильтр по employee.salary > 10000?
with cte_employee as (
select
t0.Main_department_Employee as k0,
t0.Main_gender_Employee as k1,
notZero(count(*)) as e0
from _auto_Main_Employee as t0
where
t0.Main_department_Employee is not null and
t0.Main_gender_Employee is not null
group by
t0.Main_department_Employee,
t0.Main_gender_Employee
), cte_employee_count as (
select
t0.k0 as k0,
notZero(count(*)) as e0
from cte_employee as t0
where
notZero(coalesce(t0.e0, 0)) is not null
group by
t0.k0
)
select
t0.k0 as jkey0,
t0.k0 as jprop0
from cte_employee_count as t0
inner join (
select
t1.Main_department_Employee as k0,
notZero(sum(t1.Main_salary_Employee)) as e0,
notZero(count(*)) as e1
from _auto_Main_Employee as t1
inner join cte_employee_count as t0 on
t0.k0 = t1.Main_department_Employee
where
t0.e0 = 2
group by
t1.Main_department_Employee
) as t1 on
t1.k0 = t0.k0
where
(notZero(coalesce(t1.e0, 0)) > 100000) and
(10 > notZero(coalesce(t1.e1, 0))) and
t0.e0 = 2
я почти уверен что неэквивалентно (и, похоже, вы не понимаете, как работает having). Можете, пожалуйста, написать полный запрос на lsfusion и показать в какой sql он трансформируется в вашей платформе.
Не надо мне рассказывать про проблему оптимизации sql запроса, просто напишите этот запрос и покажите sql который получается в результате. Тогда я смогу оценить, эквивалентно оно или нет.
Я понимаю, что ваша платформа позволяет писать стандартные формы очень быстро, и это хорошее и, похоже, нужное вам свойство, но вы также утверждаете, что язык более выразителен чем sql, и с этим я совершенно не согласен.
ну вообще-то в тех RDBMS в которых я работал (MS SQL в основном, но вроде в постгресе тоже), можно определять табличные функции, так что ничего копипастить не надо
если я правильно понимаю как оно у вас работает это не эквивалентно запросу выше. Либо N надо передать во все функции либо у вас там какой-то искуственный интеллект
select
department_id,
array_agg(id) as employees,
sum(tax) as tax
from employee
where
salary > 10000
group by
department_id
having
sum(salary) > 100000 and
count(*) < 10 and
count(distinct gender) = 2
что если требования такие — только в одном месте должен появиться фильтр по зарплате, в остальных местах все остаётся как прежде?
Пусть так — можете написать вариант кода который позволит сымитировать поведение такой функции:
select
department_id,
array_agg(id) as employees,
sum(tax) as tax
from employee
where
salary between [param1] and [param2]
group by
department_id
having
sum(salary) > [param3] and
count(*) < 10 and
count(distinct gender) = 2
Для постгреса пока удобного метода не нашёл (смотрю на FlyWay), для Sql Server используем https://www.red-gate.com/products/sql-development/sql-change-automation/. Он позволяет хранить миграционные скрипты + текущий снапшот схемы данных.
У меня есть практический вопрос. Предположим, у меня есть проект с бизнес логикой на MS SQL. Возможно ли 'вместо MS SQL' использовать lsFusion + PostgreSQL (только как бек енд сервер)?
Не совсем согласен, что HAVING не нужен, но согласен, что функционально его можно заменить. Можете привести код на lsfusion или Вы не по этой части?
Я в основном работаю на Ms Sql и там никогда не было триггеров per row, триггеры только per statement. То есть в вашем конкретном случае в на один стейтмент один запуск триггера и в контексте триггера будут две таблицы inserted и deleted, которые будут содержать данные до и после апдейта (100 записей в каждой)
То есть для Ms Sql ответ будет — на один стейтмент один запуск триггера.
В Postgres как минимум в версии 11 уже были два варианта — можно объявить триггер per row а можно объявить триггер per statement. Соответственно, per statement работает так же, как и Ms Sql, а per row реально запустит триггер 100 раз.
Я не большой спец в Postgres поэтому не могу дать 100% гарантии что per row триггер не оптимизируется, но когда я около полугода назад это тестировал на 10000 записях, пустой per row триггер работал явно медленнее пустого per statement триггера.
да я не хотел меряться. Вы просто утверждаете, что lsfusion как язык программирования более выразителен для работы с данными, чем sql, а я с этим не согласен. Поэтому хотел увидеть, как в вашем случае выглядит пример такого запроса (элементарного в sql) и как будет выглядеть его модификация при необходимости.
Собственно, мы с моим коллегой вынашиваем идею языка-надстройки над sql который позволит так работать — передавать запросы как параметры в функции, возвращать запросы и т.д. Сейчас, в принципе, можно такое сделать через курсоры, но это сразу убивает всю set-based-thinking парадигму.
Как обычно, времени на это нет.
Пытаюсь внедрить эту идею в мозги разработчиков Postgres на митапах :) Потому что, насколько знаю, Postgresовская система типов лучшая среди всех RDBMS, было бы логично её расширить.
1. Мой первый запрос — группировка сотрудника по департаментам -> фильтр по результатам агрегации.
2. Мой второй запрос — фильтр сотрудников -> группировка по департаментам -> фильтр по результатам агрегации.
а потом мне надо отфильтровать employee по каким-то критериям, мне придется править исходный запрос
и в sql я не могу сделать так, чтобы employee была переменной (я могу конечно сделать темп таблицу, и использовать её, но это перекладывание данных). А вот что я хотел бы уметь делать в sql
и linq как раз тут просто обыгрывает стандартный сиквел на порядки.
а всего-то надо сделать query first class citizen
я в данном случае говорю только об эквивалентности запроса, не об эффективности. То есть логика запроса должна быть одинаковая
Не надо мне рассказывать про проблему оптимизации sql запроса, просто напишите этот запрос и покажите sql который получается в результате. Тогда я смогу оценить, эквивалентно оно или нет.
Я понимаю, что ваша платформа позволяет писать стандартные формы очень быстро, и это хорошее и, похоже, нужное вам свойство, но вы также утверждаете, что язык более выразителен чем sql, и с этим я совершенно не согласен.
Пусть так — можете написать вариант кода который позволит сымитировать поведение такой функции: