[sql] PostgreSQL에서 LATERAL과 하위 쿼리의 차이점은 무엇입니까?

Postgres는 LATERAL조인 을 수행 할 수있는 기능을 제공 했으므로 현재 전체 쿼리에 4 분 이상 걸리는 비효율적 인 하위 쿼리가 많은 팀에 대해 복잡한 데이터 덤프를 수행하고 있기 때문에 자세히 읽었습니다.

나는 이해가 LATERAL나를 도울 수 있습니다 조인하지만, 심지어 같은 기사를 읽은 후 이 하나의 힙 애널리틱스를, 나는 여전히 매우 따르지 않습니다.

LATERAL조인 의 사용 사례는 무엇입니까 ? LATERAL조인과 하위 쿼리 의 차이점은 무엇입니까 ?



답변

상관 된 것처럼 하위 쿼리

A는 LATERAL(포스트 그레스 9.3 이상)이 더 같은 조인 상관 하위 쿼리 가 아닌 일반 서브 쿼리. 마찬가지로 Andomar 지적 A는 오른쪽에, 함수 또는 하위 쿼리를 LATERAL단지처럼 – 그것의 왼쪽 각 행에 대해 한 번 평가되어야 할 참여 상관 하위 쿼리 – 일반 하위 쿼리 (테이블 식)이 평가되는 동안 한 번 만. 쿼리 플래너는 성능을 최적화 할 수있는 방법
이 있습니다 . 이 관련 답변에는 동일한 문제를 해결하는 코드 예제가 나란히 있습니다.

복귀를 들어 하나 이상의 열을 , A는 LATERAL가입 일반적으로, 간단 깨끗하고 빠릅니다.
또한 상관 된 하위 쿼리와 동등한 내용은 다음과 같습니다 LEFT JOIN LATERAL ... ON true.

에 설명서를 읽으십시오 LATERAL

우리가 여기에 대답 할 것보다 권위가 있습니다.

부질의가 할 수없는 것들

있습니다 A는 것들 LATERAL할 수있는 참여는하지만 (상관 관계) 하위 쿼리는 (쉽게) 할 수 없습니다. 상관 서브 쿼리는 베어 함수 호출 (여러 행을 리턴하면 결과 행을 곱함)을 제외하고 여러 열이 아니라 단일 행만 리턴 할 수 있습니다. 그러나 특정 반환 함수조차도 FROM절 에서만 허용됩니다 . unnest()Postgres 9.4 이상의 여러 매개 변수 와 유사합니다 .매뉴얼 :

이것은 오직 FROM 조항 .

따라서 이것은 작동하지만 하위 쿼리로 쉽게 바꿀 수는 없습니다.

CREATE TABLE tbl (a1 int[], a2 int[]);
SELECT * FROM tbl, unnest(a1, a2) u(elem1, elem2);  -- implicit LATERAL

절의 쉼표 ( ,) FROM는의 짧은 표기법입니다 CROSS JOIN.
LATERAL테이블 함수에 대해 자동으로 가정됩니다.
특별한 경우에 대한 자세한 내용 UNNEST( array_expression [, ... ] ):

의 반환 기능 SELECT리스트의

리스트 unnest()에서 와 같이 set-returning 기능을 SELECT직접 사용할 수도 있습니다. 이것은 동일한 SELECT목록에서 Postgres 9.6까지 하나 이상의 이러한 기능으로 놀라운 동작을 나타 냈습니다. 그러나 마침내 Postgres 10으로 위생 처리되었으며 현재 표준 SQL이 아니더라도 유효한 대안입니다. 보다:

위의 예를 바탕으로 :

SELECT *, unnest(a1) AS elem1, unnest(a2) AS elem2
FROM   tbl;

비교:

pg 9.6 용 dbfiddle 여기서
pg 10 용 dbfiddle 여기

잘못된 정보를 명확하게

매뉴얼 :

들어 INNEROUTER참여 유형 A는 조건, 즉, 정확히 하나를 지정해야 가입 NATURAL, ON join_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 JOINA가 조인 조건을 필요로하지 않습니다)와 아틸라의 @ IS 잘못되었습니다.


답변

비의 차이 lateral와는 lateral당신이 왼쪽 테이블의 행을 볼 수 있는지 여부에 거짓말을 가입 할 수 있습니다. 예를 들면 다음과 같습니다.

select  *
from    table1 t1
cross join lateral
        (
        select  *
        from    t2
        where   t1.col1 = t2.col1 -- Only allowed because of lateral
        ) sub

이 “외형”은 하위 쿼리가 두 번 이상 평가되어야 함을 의미합니다. 결국 t1.col1많은 값을 가정 할 수 있습니다.

반대로 lateral조인 이 아닌 이후의 하위 쿼리 는 한 번만 평가할 수 있습니다.

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에서 오랫동안 구현되었으므로 Lateral에 대한 자세한 정보를 찾을 수 있습니다.

둘째, 내 이해에 따르면 측면을 사용하는 대신 하위 쿼리를 사용하여 수행 할 수있는 것은 없습니다. 그러나:

다음 쿼리를 고려하십시오.

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에서 이런 종류의 작업을 수행하는 방법을 아는 유일한 방법입니다.


답변