powered by simpleCommunicator - 2.0.50     © 2025 Programmizd 02
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Вложенные запросы или как правильно это сделать?
25 сообщений из 48, страница 1 из 2
Вложенные запросы или как правильно это сделать?
    #39964278
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Имеется три таблицы, основная и две подчинённые. При этом в приложении необходимо отображать данные из основной и несколько вычисляемых столбцов на основе подчинённых.

Примерно так:

Таблицы: A - основная, B и C - подчинённые.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
     A     |     B     |     C    
 ID      | ID      | ID
 ...     | ID_A    | ID_A
 ...     | DATE    | SOMEVAL 
 ...     | NUM1    | ...
 ...     | NUM2    | ...
 ...     | ...     | ...

Нужно отобразить в сводной таблице для каждой записи из A:
  • все столбцы из A
  • две суммы по двум столбцам из B по некоторому условию (ключ и дата, например)
  • минимальное значение по ключу из C.
Код: plaintext
1.
2.
3.
4.
5.
 Результат:                                                                                                         
A.ID1, ..., SUM(NUM1), SUM(NUM2) [WHERE B.ID_A = A.ID1 AND DATE BETWEEN ....], MIN(SOMEVAL) [WHERE C.ID_A = A.ID1]
A.ID2, ..., SUM(NUM1), SUM(NUM2) [WHERE B.ID_A = A.ID2 AND DATE BETWEEN ....], MIN(SOMEVAL) [WHERE C.ID_A = A.ID2]
A.ID3, ..., SUM(NUM1), SUM(NUM2) [WHERE B.ID_A = A.ID3 AND DATE BETWEEN ....], MIN(SOMEVAL) [WHERE C.ID_A = A.ID3]
...
Вопрос: как это сделать правильно и чтобы работало максимально быстро?
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964282
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ответ зависит от многих факторов, включая версию Firebird, размеры таблиц и т.д. и т.п.

Ну и было бы неплохо посмотреть на твои собственные попытки, их планы и статистику.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964284
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov

Ответ зависит от многих факторов, включая версию Firebird, размеры таблиц и т.д. и т.п.

Версия 3.0, таблицы... ну пока небольшие - до десятков тысяч записей.
Dimitry Sibiryakov

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

Например в данный момент таблицы B ещё нет, а для таблицы C есть поле в A, которое обновляется по триггеру при изменении C.
Но мне кажется это не совсем правильным решением, особенно для новых условий.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964288
Фотография kdv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp,

а чего не a join b join c ? left по вкусу, в зависимости от наличия записей в b и c соответствующих a.

в sql можно по всякому делать. "вложенные запросы" - это обычно от неумения join.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964336
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvpМне не код нужен, а направление в котором двигаться.

Все направления работоспособны при определённых условиях:
1) Простой JOIN + GROUP.
2) JOIN + DERIVED TABLE с GROUP внутри
3) SUBQUERY
4) Хранимые агрегаты.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964355
Polesov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Привет.

alekcvp
Имеется три таблицы, основная и две подчинённые
  • все столбцы из A
  • две суммы по двум столбцам из B по некоторому условию (ключ и дата, например)
  • минимальное значение по ключу из C.
Я бы для таблиц B и С агрегатные вычисления оформил бы в виде CTE, которые заджоинил к таблице A.
И все это оформил бы, как VIEW.
Основной вопрос - какой должен быть join (left или inner)?
Могут ли FK-поля в таблицах B и C принимать значение NULL?
Должны ли в результате запроса присутствовать записи из A, ссылки на которые отсутствуют в B и/или C?

С уважением, Polesov.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964380
KreatorXXI
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp,

скорее всего обычным джойном такое не получится. Агрегаты с разными условиями так не сделать. Можно ещё рассмотреть оконные функции, в них агрегаты настраиваются по условию. Совсем просто - это подзапросы по таблицам B и С. Но может оказаться перепробег, т.е. по одним и тем же данным будет несколько проходов. На первый взгляд это Ваш случай - сумма по таблице B, частичная сумма по таблице B... По-моему, оконные для этого - полная выборка и агрегаты по разным условиям.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964428
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Polesov

Я бы для таблиц B и С агрегатные вычисления оформил бы в виде CTE, которые заджоинил к таблице A.

CTE?
Polesov

Могут ли FK-поля в таблицах B и C принимать значение NULL?
Не могут.
Polesov

Должны ли в результате запроса присутствовать записи из A, ссылки на которые отсутствуют в B и/или C?
Должны быть все записи из А.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964429
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kdv
а чего не a join b join c ? left по вкусу, в зависимости от наличия записей в b и c соответствующих a.
Я не знал что в обычных запросах можно делать аггрегатные join, причём с условиями...
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964433
zeon11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp,

Начни в лоб:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
select 
   a.ID,
   a.Name,
   (select sum(b.summ) from TableB b where b.ID_A=a.ID and 1=1) SumB,
   (select count(b.ID)    from TableB b where b.ID_A=a.ID and 1=1) CountB,
   (select sum(c.summ) from TableC c where c.ID_A=a.ID and 1=1) SumC,
   (select count(c.ID)    from TableC c where c.ID_A=a.ID and 1=1) CountC

from TableA a 
where 1=1



на нескольких десятках тысяч записей будет отрабатываться очень быстро,
по крайней мере, прототип приложения отладишь, а там и новые идеи придут.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964439
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Polesov,

Что-то типа такого?
Код: sql
1.
2.
3.
4.
5.
6.
with 
  t_in as (select id_a1 as id, sum(num1) as sum1 from b where date between .... group by id_a1),
  t_out as (select id_a2 as id, sum(num2) as sum2 from b where date between .... group by id_a2)
select * from a 
  left join t_in on a.id = t_in.id 
  left join t_out on a.id = t_out.id
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964463
Polesov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp
Polesov,

Что-то типа такого?
Код: sql
1.
2.
3.
4.
5.
6.
with 
  t_in as (select id_a1 as id, sum(num1) as sum1 from b where date between .... group by id_a1),
  t_out as (select id_a2 as id, sum(num2) as sum2 from b where date between .... group by id_a2)
select * from a 
  left join t_in on a.id = t_in.id 
  left join t_out on a.id = t_out.id



Агрегат по В можно делать в одной CTE.

Я тут интереса ради смакетировал свое предложение (скрипт под спойлером).

/* TABLE A ****************************************/
create table A (
ID integer not null,
VAL varchar(32)
);

alter table A
add constraint PK_A
primary key ( ID );

/* TABLE B ****************************************/
create table B (
ID integer not null,
ID_A integer not null,
SUM1 float,
SUM2 float,
FIX date
);

alter table B
add constraint PK_B
primary key ( ID );

alter table B
add constraint FK_B_A
foreign key ( ID_A )
references A ( ID );

create index IDX_B_FIX on B ( FIX );

/* TABLE C ****************************************/
create table C (
ID integer not null,
ID_A integer not null,
MIN1 float
);

alter table C
add constraint PK_C
primary key ( ID );

alter table C
add constraint FK_C_A
foreign key ( ID_A )
references A ( ID );

create index IDX_C_MIN1 on C ( MIN1 );

/* VIEW V_ABC *************************************/
create view V_ABC (
ID,
VAL,
SUM_1,
SUM_2,
MIN_1 )
as
with
CTE_B ( ID_A, SUM_1, SUM_2 ) as
( select ID_A,
sum( SUM1 ) as SUM_1,
sum( SUM2 ) as SUM_2
from B
where FIX between '01-JAN-2020' and '31-DEC-2020'
group by ID_A ),

CTE_C ( ID_A, MIN_1 ) as
( select ID_A,
min( MIN1 ) as MIN_1
from C
group by ID_A )

select a.ID,
a.VAL,
cte_b.SUM_1,
cte_b.SUM_2,
cte_c.MIN_1
from A
join CTE_B on ( cte_b.ID_A = a.ID )
join CTE_C on ( cte_c.ID_A = a.ID )
;

/* FILL DATA **************************************/

set term ^ ;

execute block
as
declare variable ID_A integer;
declare variable ID_B integer;
declare variable ID_C integer;
declare variable XX_N integer;
begin
ID_A = 0;
ID_B = 0;
ID_C = 0;
while ( :ID_A < 100000 ) do
begin
ID_A = :ID_A + 1;

insert into A (
ID,
VAL )
values (
:ID_A,
'VAL#' || :ID_A );

XX_N = 0;
while ( :XX_N < 5 ) do
begin
ID_B = :ID_B + 1;
insert into B (
ID,
ID_A,
SUM1,
SUM2,
FIX )
values (
:ID_B,
:ID_A,
:ID_B / 0.1,
:ID_B / 0.3,
'01-JUL-2020' );
XX_N = :XX_N + 1;
end

XX_N = 0;
while ( :XX_N < 7 ) do
begin
ID_C = :ID_C + 1;
insert into C (
ID,
ID_A,
MIN1 )
values (
:ID_C,
:ID_A,
:ID_C / 0.7 );
XX_N = :XX_N + 1;
end
end
end ^

set term ; ^

commit;

A - 100000 записей.
B - 500000 записей.
C - 700000 записей.

Ну, что сказать, как и ожидалось:
inner join - выполняется быстро (select count(*) from V_ABC с inner join ~0.9 sec на Cor-i7 3.6 HGz + SSD )
left join - уш-да-уш ... ((
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964464
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Polesov

Агрегат по В можно делать в одной CTE.

Я в исходном посте упустил, что в B два ключа для A и суммы, соответственно, считаются по разным.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964469
Polesov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp
Polesov

Агрегат по В можно делать в одной CTE.

Я в исходном посте упустил...

Ну, это уже не столь существенно.
А вот если все-же требование на left join , я бы рассмотрел возможность изменения схемы выборки.
Уж больно left join в данной схеме тормозной.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964471
Polesov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp
Должны быть все записи из А.

Возможна ли ситуация, когда в А есть записи, на которые нет ссылок из С и из B по обеим FK?
Если да, то требуется left join, а в моем варианте это, мягко говоря, не очень быстрый вариант.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964474
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Polesov
alekcvp
Должны быть все записи из А.

Возможна ли ситуация, когда в А есть записи, на которые нет ссылок из С и из B по обеим FK?
Если да, то требуется left join, а в моем варианте это, мягко говоря, не очень быстрый вариант.

В C для каждой записи из A должна быть минимум одна запись, а вот в B - не обязательно, плюс там date between будет, под который может ни одна запись не попадать.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964475
Polesov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp
Изначально предусмотрено что ...

Надеюсь, идея и ее плюсы и минусы, понятны.
Далее надо смотреть по месту.
На мой взгляд, странно иметь в таблице B 2 FK на таблицу A - это безусловное применение let join.
Впрочем, сложно что-то советовать, не зная предметной области.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964476
Polesov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp
плюс там date between будет, под который может ни одна запись не попадать.

Это условие на inner/left join не влияет.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964482
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Polesov
На мой взгляд, странно иметь в таблице B 2 FK на таблицу A - это безусловное применение let join.
Впрочем, сложно что-то советовать, не зная предметной области.

Хм... чё-то я задумался... возможно стоит таблицу B разбить на две... но всё равно в этих B1 и B2 может не быть записей для A, т.е. никуда от left join не деться.
Может как-то индексами помочь?
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964486
Polesov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp
Polesov
На мой взгляд, странно иметь в таблице B 2 FK на таблицу A - это безусловное применение let join.
Впрочем, сложно что-то советовать, не зная предметной области.

Хм... чё-то я задумался... возможно стоит таблицу B разбить на две... но всё равно в этих B1 и B2 может не быть записей для A, т.е. никуда от left join не деться.
Может как-то индексами помочь?

А обязательно данные получать в одном рекорд-сете?
Может быть, для отображения данных на клиенте, дешевле сделать master-detail?
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964492
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Polesov
А обязательно данные получать в одном рекорд-сете?
Может быть, для отображения данных на клиенте, дешевле сделать master-detail?

По ТЗ - обязательно... но в таблице A вряд ли будет больше 100000 записей.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964501
Polesov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp
Polesov
А обязательно данные получать в одном рекорд-сете?
Может быть, для отображения данных на клиенте, дешевле сделать master-detail?

По ТЗ - обязательно...

Для обеспечения inner join можно рассмотреть вариант создания в B и C фиктивных записей,
что бы каждой записи из A было соответствие в B и C, но это уже, на мой взгляд, костыль.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964513
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Polesov
Для обеспечения inner join можно рассмотреть вариант создания в B и C фиктивных записей,
что бы каждой записи из A было соответствие в B и C, но это уже, на мой взгляд, костыль.

ИМХО проще тогда сделать эти поля в A и при изменении записей в B и C триггером это поле обновлять...
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964515
Polesov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
alekcvp,
А связь A к B и A к C один к одному?
Если нет, то этот способ еще хуже.
...
Рейтинг: 0 / 0
Вложенные запросы или как правильно это сделать?
    #39964522
alekcvp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Polesov
alekcvp,
А связь A к B и A к C один к одному?
Если нет, то этот способ еще хуже.

Нет, один ко многим.
А почему хуже? По триггеру пересчитать одно поле одной записи вроде не должно занимать много времени?

Почему всё-таки Left Join так катастрофически влияет на производительность?
...
Рейтинг: 0 / 0
25 сообщений из 48, страница 1 из 2
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Вложенные запросы или как правильно это сделать?
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]