GROUP BY в CROSS ПРИМЕНИТЬ - PullRequest
       25

GROUP BY в CROSS ПРИМЕНИТЬ

2 голосов
/ 11 апреля 2019

Пусть у нас будет две таблицы

create table A (
  fkb int,
  groupby int
);

create table B (
  id int,
  search int
);

insert into A values (1, 1);
insert into B values (1, 1);
insert into B values (2, 1);

тогда следующий запрос

select B.id, t.max_groupby - B.search diff
from B
cross apply ( 
  select max(A.groupby) max_groupby
  from A 
  where A.fkb = B.id 
) t

возвращает ожидаемый результат следующим образом

id  diff
---------
1   0
2   NULL

Однако, когда я добавляю group by A.fkb к кресту, строка B, в которой соответствующий A.fkb не существует, исчезает.

select B.id, t.max_groupby - B.search diff
from B
cross apply ( 
  select max(A.groupby) max_groupby
  from A 
  where A.fkb = B.id 
  group by A.fkb
) t

Я тестировал на SQL Server, а также на PostgreSQL (с cross join lateral вместо cross apply). Почему group by заставляет ряд исчезнуть? Кажется, что cross apply ведет себя как внешнее соединение в первом случае и как внутреннее соединение во втором случае. Однако, мне не понятно, почему.

Ответы [ 2 ]

3 голосов
/ 11 апреля 2019

Вы можете увидеть это, если посмотреть на результат внутреннего запроса отдельно:

select max(A.groupby) max_groupby
from A 
where A.fkb = 2;

возвращает одну строку с max_groupby = null:

max_groupby
-----------
     (null)

Однако, поскольку нет строки с A.fkb = 2 группировкой по ней, вы получите пустой результат, который можно увидеть при запуске:

select max(A.groupby) max_groupby
from A 
where A.fkb = 2
group by A.fkb

и, таким образом, перекрестное соединение не возвращает возвращаемые строки для fkb = 2

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

В Postgres вы должны написать это как:

select B.id, t.max_groupby - B.search diff
from B
  left join lateral ( 
    select max(A.groupby) max_groupby
    from A 
    where A.fkb = B.id 
    group by A.fkb
  ) t on true

Я не знаю, каким будет эквивалент left join lateral в SQL Server.
on true необходимо записать как on 1=1.

1 голос
/ 11 апреля 2019

Это происходит потому, что:

  • GROUP BY ничего не возвращает, когда A.fkb = 2
  • без GROUP BY возвращает NULL

Таким образом, ваш запрос CROSS APPLY возвращает разные результаты.

select B.id, t.max_groupby - B.search diff
from B
outer apply ( 
  select max(A.groupby) max_groupby
  from A 
  where A.fkb = B.id 
  group by A.fkb
) t

ВЫВОД:

id  diff
1   0
2   NULL
...