CALL { subquery }#
GQL 절 2.18에 추가
읽기 전용 서브쿼리를 파이프라인의 한 절로 실행하며, 그 서브쿼리가 감싸는 쿼리에서 변수를 가져오면 입력 행마다 한 번 실행합니다.
중요
CALL { … }만 쓰면 아무것도 가져오지 않습니다. 중괄호 안의 패턴은 자기 변수 이름을
선언하므로, 본문에 적은 바깥 변수의 이름은 그 바깥 변수를 가리키는 것이 아니라 같은 이름의
제약 없는 새 변수를 바인딩합니다. 필요한 변수를 이름으로 적으십시오. CALL (v) { … },
CALL (*) { … }, 또는 CALL { WITH v … }입니다.
개요#
굵게 키워드 · 기울임 직접 채우는 자리 · [ ] 생략 가능 · ... 반복 가능. 구문 표기 규약을 참고하십시오.
설명#
CALL { … }은 서브쿼리를 평가해 그 결과를 작업 표에 조인합니다. 본문이 무엇을 볼 수 있는지는
가져오기 목록이 전적으로 결정합니다.
가져오기 목록이 있으면 본문은 그 변수들이 바인딩된 상태로 입력 행마다 실행되고, 반환하는 행은 그 행에 조인됩니다.
가져오는 것이 없으면 본문은 상관되지 않습니다. 한 번 실행되고 그 결과가 모든 입력 행에 교차 조인됩니다.
본문은 읽기 전용이며
RETURN으로 끝나야 합니다. 쓰기 절은 파싱 시점에 거부되고,FINISH로 끝나는 본문은CALL subquery must RETURN at least one variable을 냅니다.본문이 반환하는 이름은 감싸는 쿼리의 바인딩이 됩니다. 바깥에 이미 바인딩된 이름은 덮어 가려지는 것이 아니라 거부됩니다. 본문 안에서도 가져온 변수는 그 이름을 유지합니다. 그중 하나를 다시 바인딩하게 될
LET,UNWIND,FOR, 이름을 바꾸는WITH는 거부됩니다.본문이 아무것도 반환하지 않은 입력 행은 버려집니다. 그것을 남기려면
OPTIONAL CALL을 쓰십시오.
가져오는 형태들#
표기 다섯 가지가 받아들여집니다. 앞의 둘은 아무것도 가져오지 않습니다. 그 둘과 뒤의 셋의 차이가 위 경고의 요점 전부입니다:
형태 |
본문이 보는 것 |
|---|---|
|
없음 |
|
없음 |
|
|
|
유효 범위의 모든 변수 |
|
|
범위 절과 가져오는 WITH를 함께 쓸 수 있습니다. CALL (v) { WITH w … }는 둘 다 가져옵니다.
비슷해 보이는 세 가지는 대안이 아니라 구문 오류입니다. CALL (*) { WITH v … },
CALL (*, v) { … }, 그리고 이름만 나열한 것이 아닌 모든 가져오기 WITH입니다(WITH *와
WITH v AS x 둘 다 거부됩니다).
매개변수#
- variable
감싸는 쿼리에서 바인딩된 변수이며, 중괄호 안에서 보이게 할 대상입니다. 존재하지 않는 변수는
variable "<name>" to import into CALL does not exist를 냅니다. 앞에 절이 없는 상태의 범위 절은CALL has no preceding clause to import variables from을 냅니다.- subquery
RETURN으로 끝나는 읽기 전용 쿼리입니다.MATCH,OPTIONAL MATCH,WITH,LET,UNWIND,FOR,LOAD FROM, 중첩된CALL,DISTINCT,ORDER BY,SKIP/LIMIT, 집합 연산자가 모두 허용됩니다.NEXT는 허용되지 않습니다.
예제#
모든 예제는 예제 데이터셋에서 실행됩니다.
가져오기 목록이 중요한 이유#
서브쿼리 형태는 이런 질문을 위해 만들어졌습니다. 누군가가 연출한 영화마다 출연진은 몇 명이었나? 가져오기 목록 없이 쓰면 다른 질문에 답합니다:
MATCH (d:person)-[:directed]->(m:movie)
CALL { MATCH (a:person)-[:acted_in]->(m) RETURN count(a) AS cast_size }
RETURN m.title AS film, cast_size ORDER BY film LIMIT 4;
film | cast_size
---------------------+-----------
"Avatar" | 25
"Blade Runner" | 25
"Blade Runner 2049" | 25
"Elysium" | 25
(4 rows)
모든 영화의 출연진 수가 같고, 25는 그래프 전체의 acted_in 간선 수입니다:
MATCH ()-[r:acted_in]->() RETURN count(r) AS acted_in_edges;
acted_in_edges
----------------
25
(1 row)
중괄호 안의 m은 바깥에서 일치시킨 m이 아닙니다. 본문 자신의 패턴이 선언한 완전히 새로운
변수이고, acted_in 간선으로만 제약됩니다. 그래서 본문은 그래프의 모든 acted_in 간선을 상관
없이 한 번 세고, 그 하나의 숫자를 모든 행에 교차 조인합니다.
가져올 것을 이름으로 적으면 의도한 질문을 하게 됩니다:
MATCH (d:person)-[:directed]->(m:movie)
CALL (m) { MATCH (a:person)-[:acted_in]->(m) RETURN count(a) AS cast_size }
RETURN m.title AS film, cast_size ORDER BY film LIMIT 4;
film | cast_size
---------------------+-----------
"Avatar" | 1
"Blade Runner" | 1
"Blade Runner 2049" | 2
"Elysium" | 1
(4 rows)
CALL (*) { … }과 CALL { WITH m … }도 같은 숫자를 냅니다.
아무것도 보고되지 않는 것은, 변수를 가리키는 것은 표현식뿐이고 패턴은 변수를 선언하기
때문입니다. 같은 실수를 WHERE로 옮기면 잡힙니다:
MATCH (d:person)-[:directed]->(m:movie)
CALL { MATCH (a:person) WHERE a.name = m.title RETURN count(a) AS n }
RETURN n;
ERROR: variable does not exist
LINE 2: CALL { MATCH (a:person) WHERE a.name = m.title RETURN count(...
^
그러므로 이 침묵은 패턴에서 다시 쓴 이름에 한정된 것입니다. 그리고 그것이 흔한 경우이며, 가져오기 목록을 매번 쓰는 이유입니다.
파이프라인을 접지 않고 행마다 집계하기#
집계하는 본문은 계산한 값이 0일 때에도 입력 행마다 정확히 한 행을 반환하므로, 바깥의 행 수는 그대로입니다:
MATCH (p:person) WHERE p.name IN ['Keanu Reeves', 'Christopher Nolan']
CALL (p) { MATCH (p)-[:acted_in]->(m:movie) RETURN count(m) AS films }
RETURN p.name AS person, films ORDER BY person;
person | films
---------------------+-------
"Christopher Nolan" | 0
"Keanu Reeves" | 4
(2 rows)
숫자 하나를 한 번만 계산하기#
빈 범위 절은 본문이 상관되지 않았음을 분명히 말해 줍니다. 본문이 그 쿼리의 상수라면 그렇게 쓰십시오:
MATCH (p:person {name: 'Keanu Reeves'})
CALL () { MATCH (m:movie) RETURN count(m) AS movies }
RETURN p.name AS person, movies;
person | movies
----------------+--------
"Keanu Reeves" | 18
(1 row)
변수 여러 개 가져오기#
이름을 적은 모든 변수가 보이고, 본문은 그중 일부만 써도 됩니다:
MATCH (p:person {name: 'Keanu Reeves'}), (q:person {name: 'Carrie-Anne Moss'})
CALL (p, q) { MATCH (p)-[:acted_in]->(m:movie)<-[:acted_in]-(q) RETURN count(m) AS shared }
RETURN shared;
shared
--------
2
(1 row)
참고#
상관된 본문이 실행 계획에서 행마다 도는 서브쿼리인 것은 아닙니다. 플래너가 그것을 끌어올리므로,
위 쿼리는 그 자리에 패턴을 적었을 때와 같은 조인으로 계획됩니다. person·acted_in·movie에 대한
Hash Join이고, MATCH (p:person)-[:acted_in]->(m:movie)와 똑같습니다. 집계하는 본문처럼 플래너가
평평하게 만들 수 없는 본문만 Nested Loop 아래 Aggregate가 놓인, 행마다 도는 lateral 조인으로
남습니다.
쓰기 절은 그 쓰기 키워드 자리에서 구문 오류로 거부되며, 종류마다 그렇습니다. CREATE, INSERT,
MERGE, SET, REMOVE, DELETE, DETACH DELETE입니다.
바깥 쿼리가 이미 바인딩한 이름을 반환하는 본문은
variable "<name>" returned by the CALL subquery is already bound in the outer query를 냅니다.
그것은 뒤따르는 모든 것에 대해 바깥 바인딩을 덮어 가리게 됩니다. 본문의 RETURN에서 별칭을
붙이십시오.
호환성#
GQL 적합성
§14.5의 쿼리 자리에서, §15.1의 call procedure statement 아래에 GQL의 inline procedure call(ISO/IEC 39075:2024 §15.2)을 구현합니다.
지원 — 바인딩 변수 목록을 적은 variable scope clause와 빈
()형태, 중첩, 그리고OPTIONAL접두.미지원 — 데이터를 바꾸는 본문(§13.6, call data-modifying procedure statement).
이탈, 그리고 반드시 알아야 할 것 — §15.2 구문 규칙 3은 variable scope clause가 없을 때 들어오는 작업 레코드의 모든 필드를 나열한 scope clause로 호출을 다시 쓴다고 규정합니다. 표준대로라면 그냥 쓴
CALL { … }은CALL (*) { … }과 똑같이 전부를 들여옵니다. AgensGraph는 scope clause가 없으면CALL () { … }처럼 아무것도 들여오지 않는 것으로 봅니다.확장 —
(*)는 표준의 binding variable reference list에 없고, 들여오는WITH도 없습니다. 둘 다 openCypher에서 왔습니다.
진행 상황은 GQL 적합성 표에서 확인하십시오.
openCypher#
openCypher도 같은 가져오기 목록을 요구하지만, 그것이 없으면 알려 줍니다. 거기서는 CALL { } 안에서
선언되지 않은 바깥 변수를 가리키는 것이 조용한 재바인딩이 아니라 오류입니다. openCypher에서 옮겨 온
코드는 계속 동작합니다. CALL (v) { }, CALL (*) { }, CALL { WITH v … }가 모두 거기서와 같은
뜻이기 때문입니다. openCypher가 거부했던 CALL { }만 쓴 코드는 여기서 실행되고 다른 답을 냅니다.
관련 항목#
CALLGQL — 두 호출 형태OPTIONAL CALLGQL — 본문이 아무것도 반환하지 않은 행 남기기CALL … YIELDGQL — 대신 이름 있는 함수 호출하기서브쿼리 값 표현식 GQL — 표현식 안의 서브쿼리