В чем разница между латеральным и подзапросом в PostgreSQL?
так как Postgres вышел с возможностью делать LATERAL joins, я читал об этом, так как в настоящее время я делаю сложные дампы данных для своей команды с большим количеством неэффективных подзапросов, которые заставляют общий запрос занимать четыре минуты или больше.
Я понимаю, что LATERAL соединения могут помочь мне, но даже после прочтения таких статей, как этот из кучи аналитики, я все еще не совсем понимаю.
каков вариант использования для LATERAL присоединиться? Что разница между LATERAL join и подзапрос?
4 ответов:
еще как коррелируется подзапрос
A
LATERALjoin (Postgres 9.3+) больше похоже на коррелированный подзапрос, а не простой подзапрос. Как @Andomar указал, функция или подзапрос справа отLATERALjoin обычно должен оцениваться много раз-один раз для каждой строки слева отLATERALприсоединиться-так же, как коррелируется подзапрос - при вычислении простого подзапроса (табличного выражения) после только. (Однако планировщик запросов имеет способы оптимизации производительности для любого из них.)
Этот связанный ответ имеет примеры кода для обоих бок о бок, решая одну и ту же проблему:возвращение более одной колонки, a
LATERALjoin обычно проще, чище и быстрее. Кроме того, помните, что эквивалент коррелированного подзапросLEFT JOIN LATERAL ... ON true:читать инструкции для
LATERALэто более авторитетно, чем все, что мы собираемся положить в ответы здесь:
- https://www.postgresql.org/docs/current/static/queries-table-expressions.html#QUERIES-LATERAL
- http://www.postgresql.org/docs/current/static/sql-select.html
вещи, которые подзапрос не может сделать
здесь are вещи, которые a
LATERALjoin может сделать, но (коррелированный) подзапрос не может (легко). Коррелированный подзапрос может возвращать только одно значение, а не несколько столбцы, а не несколько строк - за исключением голых вызовов функций (которые умножают результирующие строки, если они возвращают несколько строк). Но даже некоторые функции возврата набора разрешены только вFROMпредложения. Как новыйunnest()С несколькими параметрами в Postgres 9.4. инструкции:это разрешено только в
FROMп.;так что это работает, но не может быть легко заменен на подзапрос:
CREATE TABLE tbl (a1 int[], a2 int[]); SELECT * FROM tbl t, unnest(t.a1, t.a2) u(elem1, elem2); -- implicit LATERAL(запятая (
,) вFROMпредложение-это короткая нотация дляCROSS JOIN.LATERALпредполагается автоматически для табличных функций.)подробнее о частном случае
UNNEST( array_expression [, ... ] )при этом позже вопрос о dba.SE:Set-функции возврата в
SELECTсписоквы также можете использовать функции set-returning, такие как
unnest()наSELECTсписок напрямую. Это раньше демонстрировало удивительное поведение с более чем одним экземпляром в одном и том жеSELECTсписок до Postgres 9.6. но он, наконец, был дезинфицирован с Postgres 10 и является допустимой альтернативой сейчас (даже если не стандартный SQL).
Основываясь на приведенном выше примере:SELECT *, unnest(t.a1) AS elem1, unnest(t.a2) AS elem2 FROM tbl t;для сравнения:
dbfiddle для pg 9.6 здесь
dbfiddle для pg 10 здесьуточнить дезу
руководство разъясняет вводящую в заблуждение информацию здесь:
на
INNERиOUTERтипы соединений, условие соединения должно быть указано, а именно ровно один изNATURAL,ONjoin_condition, илиUSING(join_column [, ...]). См. ниже для значения.
ИбоCROSS JOIN, ни один из этих пунктов не может появиться.таким образом, эти два запроса действительны (даже если не особенно полезны):
SELECT * FROM tbl t LEFT JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t ON TRUE; SELECT * FROM tbl t, LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;а это:
SELECT * FROM tbl t LEFT JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;вот почему @Andomar-х пример кода является правильным (
CROSS JOINне требует условия соединения) и @Аттилыибыл недействительный.
разницу между
lateralиlateraljoin заключается в том, Можете ли вы посмотреть на строку левой таблицы. Например:select * from table1 t1 cross join lateral ( select * from t2 where t1.col1 = t2.col1 -- Only allowed because of lateral ) subэтот "внешний вид" означает, что подзапрос должен быть оценен более одного раза. В конце концов,
t1.col1может принимать разные значения.напротив, подзапрос после
lateraljoin можно оценить один раз:select * from table1 t1 cross join ( select * from t2 where t2.col1 = 42 -- No reference to outer query ) subкак требуется без
lateral, внутренний запрос никак не зависит от внешний запрос. Аlateralзапрос-примерcorrelatedзапрос, из-за его связи со строками вне самого запроса.
первый, боковое и поперечное применение-это одно и то же. Поэтому вы также можете прочитать о Cross Apply. Поскольку он был реализован в SQL Server на протяжении веков, вы найдете дополнительную информацию о нем, а затем боковую.
второе, по моему пониманию, вы ничего не можете сделать с помощью подзапроса, а не через боковые. Но:
рассмотрим следующий запрос.
Select A.* , (Select B.Column1 from B where B.Fk1 = A.PK and Limit 1) , (Select B.Column2 from B where B.Fk1 = A.PK and Limit 1) FROM Aвы можете использовать боковые в этом состоянии.
Select A.* , x.Column1 , x.Column2 FROM A LEFT JOIN LATERAL ( Select B.Column1,B.Column2,B.Fk1 from B Limit 1 ) x ON X.Fk1 = A.PKв этом запросе вы не можете использовать обычное соединение из-за предложения limit. Боковое или поперечное применение можно использовать когда нет простого условия соединения.
есть больше обычаев для бокового или поперечного применения, но это наиболее распространенный, который я нашел.
одна вещь, которую никто не указал, что вы можете использовать
LATERALзапросы для применения пользовательской функции к каждой выбранной строке.например:
CREATE OR REPLACE FUNCTION delete_company(companyId varchar(255)) RETURNS void AS $$ BEGIN DELETE FROM company_settings WHERE "company_id"=company_id; DELETE FROM users WHERE "company_id"=companyId; DELETE FROM companies WHERE id=companyId; END; $$ LANGUAGE plpgsql; SELECT * FROM ( SELECT id, name, created_at FROM companies WHERE created_at < '2018-01-01' ) c, LATERAL delete_company(c.id);Это единственный способ, которым я знаю, как делать такие вещи в PostgreSQL.
Comments