{#create-elabel-create-elabel}
# CREATE ELABEL
간선 레이블을 만듭니다. 그래프의 기반 간선 레이블을 상속하는 `(id, start, end, properties)` 테이블이며,
속성을 타입 컬럼으로 승격할 수도 있습니다.
{#create-elabel-synopsis}
## 개요
굵게 키워드 · 기울임 직접 채우는 자리 · [ ] 생략 가능 · ... 반복 가능. 구문 표기 규약을 참고하십시오.
{#create-elabel-description}
## 설명
간선 레이블은 컬럼 네 개를 가진 테이블입니다. `graphid` 타입의 `id`, 간선의 `start`·`end` 정점의
`graphid`, 그리고 `jsonb` 타입의 `properties` 컬럼입니다. 부모를 지정하지 않으면 그래프의
`ag_edge`를 상속합니다.
간선 레이블을 만들면 그 id에 번호를 매기는 시퀀스와 인덱스 세 개도 함께 만들어집니다. 둘은
`(start, "end")`와 `("end", start)`에 걸린 btree 인덱스이고, 그것이 순회를 어느 방향으로든 빠르게
만들어 줍니다. 셋째는 `id`에 걸린 BRIN 인덱스입니다. 간선 레이블에는 `id`에 대한 기본 키가 생기지
않습니다. 간선은 양 끝점을 통해 도달하기 때문입니다. 그 배치는
[그래프가 어떻게 저장되는가](../../concepts/graph-on-postgresql.md)가 다룹니다.
레이블은 `graph_path`가 지정한 그래프에 만들어집니다.
- **옵션은 순서가 중요합니다.** 보인 순서대로 와야 합니다. `DISABLE INDEX`, 그다음 `INHERITS`,
그다음 `WITH`, 그다음 `TABLESPACE`입니다. 다른 순서는 구문 오류입니다.
- `INHERITS`에 지정한 부모는 같은 그래프에 있어야 하고 간선 레이블이어야 합니다. 정점 레이블을
지정하면 거부됩니다.
- `IF NOT EXISTS`와 `ONLY`는 서로 다른 형태에 속하므로 함께 쓸 수 없습니다.
- 간선 레이블은 양 끝점이 어떤 레이블이어야 하는지 제한하지 않습니다. `start`와 `end`는 외래 키가
아니라 평범한 `graphid` 컬럼입니다.
- Cypher는 필요할 때 레이블을 만듭니다. `directed` 레이블이 없는 그래프에
`CREATE (a)-[:directed]->(b)`를 쓰면 그 레이블이 만들어집니다. 옵션, 승격 컬럼, 부모가 필요할 때
`CREATE ELABEL`을 쓰십시오.
{#create-elabel-promoted-properties}
### 승격된 속성
승격된 속성은 `properties` 옆에 있는 타입 컬럼이며, `properties`에서 계산해 저장한 것입니다.
그 속성을 읽으면 JSON 문서 안이 아니라 그 컬럼으로 갑니다. 세 형태와 그 제한은 정점 레이블과 같고,
[`CREATE VLABEL`](#create-vlabel-promoted-properties)이 그것을 설명합니다. 간선 레이블에서 예약된
컬럼 이름은 `id`, `properties`, `start`, `end`입니다.
{#create-elabel-parameters}
## 매개변수
UNLOGGED
: 레이블의 테이블을 unlogged로 만듭니다. 그 내용은 미리 쓰기 로그에 기록되지 않으며 비정상 종료
뒤에는 사라집니다.
IF NOT EXISTS
: 그 이름의 레이블이 이미 있을 때 오류가 아니라 알림을 냅니다. 아무것도 만들어지지 않고, 이미 있는
레이블의 옵션도 바뀌지 않습니다.
label_name
: 만들 레이블의 이름이며, `graph_path`가 지정한 그래프 안에 만듭니다.
promoted_property
: 타입 컬럼으로 함께 비출 속성입니다. 컬럼은 적은 순서대로 `properties` 뒤에 추가됩니다.
DISABLE INDEX
: 레이블의 인덱스 세 개를 만들되 무효 상태로 두어, 행을 적재할 때 그것을 유지하지 않게 합니다. 읽기와
쓰기는 그대로 동작하고, 플래너는 무효 인덱스를 무시합니다. 다시 만들 때는
[`REINDEX ELABEL`](reindex.md)을 쓰십시오. `ENABLE INDEX`는 없습니다.
INHERITS ( parent_label [, ...] )
: 기본 부모인 `ag_edge`를 대신하는 부모 레이블입니다. 각각 같은 그래프의 간선 레이블이어야 합니다.
[레이블 상속](../../concepts/label-inheritance.md)을 참고하십시오.
WITH ( storage_parameter [= value] [, ...] )
: `CREATE TABLE`에서와 같이, 레이블 테이블의 저장 매개변수입니다.
TABLESPACE tablespace_name
: 레이블 테이블의 테이블스페이스입니다. 이것이 없으면 `default_tablespace`가 결정합니다.
ONLY label_name ( label_id )
: 주어진 레이블 id로, 기본 부모·id 기본값·끝점 인덱스 없이 레이블을 만듭니다. 덤프한 그래프의 id를
복원이 보존할 수 있도록 `pg_dump`가 내보내는 형태입니다. 덤프를 재현할 때만 쓰십시오.
{#create-elabel-examples}
## 예제
예제는 [예제 데이터셋](../../get_started/example-dataset.md)을 쓰지 않고 자체 그래프인
`castlists`를 만들고, 끝에서 그것을 지웁니다.
{#create-elabel-create-an-edge-label}
### 간선 레이블 만들기
```cypher
CREATE GRAPH castlists;
SET graph_path = castlists;
CREATE VLABEL person;
CREATE VLABEL film;
CREATE ELABEL worked_on;
```
```
CREATE ELABEL
```
```psql
\d castlists.worked_on
```
```
Table "castlists.worked_on"
Column | Type | Collation | Nullable | Default
------------+---------+-----------+----------+-------------------------------------------------------------------------------------------------------
id | graphid | | not null | graphid(graph_labid('castlists.worked_on'::cstring), nextval('castlists.worked_on_id_seq'::regclass))
start | graphid | | not null |
end | graphid | | not null |
properties | jsonb | | not null | jsonb_build_object()
Indexes:
"worked_on_end_idx" btree ("end", start)
"worked_on_id_idx" brin (id)
"worked_on_start_idx" btree (start, "end")
Inherits: castlists.ag_edge
```
레이블을 만들 때 `start`와 `end`의 통계 목표도 1000으로 설정됐습니다. 순회의 행 추정이 그 두 컬럼의
분포에 달려 있기 때문입니다:
```sql
SELECT attname, attstattarget FROM pg_attribute
WHERE attrelid = 'castlists.worked_on'::regclass AND attnum > 0;
```
```
attname | attstattarget
------------+---------------
id |
start | 1000
end | 1000
properties |
(4 rows)
```
{#create-elabel-group-several-edge-types-under-one-parent}
### 여러 간선 종류를 한 부모 아래로 묶기
```cypher
CREATE ELABEL acted_in INHERITS (worked_on);
CREATE ELABEL directed INHERITS (worked_on);
```
```psql
\dGe castlists.acted_in
```
```
List of labels
Graph | Name | Type | Owner
-----------+----------+------+-------
castlists | acted_in | edge | agens
(1 row)
Edge label "castlists.acted_in"
--
Inherits: castlists.worked_on
```
이제 부모에 대한 패턴이 두 종류의 간선 모두에 일치하고, `label()`은 여전히 그 간선이 만들어질 때의
레이블을 보고합니다:
```cypher
CREATE (:person {name: 'Denis Villeneuve'})-[:directed {credited: true}]->(:film {title: 'Arrival'});
MATCH (p)-[e:worked_on]->(f) RETURN label(e) AS type, e.credited AS credited;
```
```
type | credited
------------+----------
"directed" | true
(1 row)
```
{#create-elabel-create-a-label-only-if-it-is-not-there}
### 없을 때만 레이블 만들기
```cypher
CREATE ELABEL IF NOT EXISTS acted_in;
```
```
NOTICE: label "acted_in" already exists, skipping
CREATE ELABEL
```
{#create-elabel-load-an-edge-label-before-building-its-indexes}
### 인덱스를 만들기 전에 간선 레이블 적재하기
인덱스 세 개가 모두 무효 상태로 만들어지고, 테이블 자체는 unlogged입니다:
```cypher
CREATE UNLOGGED ELABEL staged DISABLE INDEX WITH (fillfactor = 70) TABLESPACE pg_default;
```
```psql
\d castlists.staged
```
```
Unlogged table "castlists.staged"
Column | Type | Collation | Nullable | Default
------------+---------+-----------+----------+-------------------------------------------------------------------------------------------------
id | graphid | | not null | graphid(graph_labid('castlists.staged'::cstring), nextval('castlists.staged_id_seq'::regclass))
start | graphid | | not null |
end | graphid | | not null |
properties | jsonb | | not null | jsonb_build_object()
Indexes:
"staged_end_idx" btree ("end", start) INVALID
"staged_id_idx" brin (id) INVALID
"staged_start_idx" btree (start, "end") INVALID
Inherits: castlists.ag_edge
```
간선을 적재하고, 그다음 `REINDEX ELABEL staged`를 실행해 인덱스 세 개를 만드십시오.
{#create-elabel-promote-an-edge-property-to-a-typed-column}
### 간선 속성을 타입 컬럼으로 승격하기
```cypher
CREATE ELABEL scored (rating numeric GENERATED, source text GENERATED (site));
```
```psql
\d castlists.scored
```
```
Table "castlists.scored"
Column | Type | Collation | Nullable | Default
------------+---------+-----------+----------+-------------------------------------------------------------------------------------------------
id | graphid | | not null | graphid(graph_labid('castlists.scored'::cstring), nextval('castlists.scored_id_seq'::regclass))
start | graphid | | not null |
end | graphid | | not null |
properties | jsonb | | not null | jsonb_build_object()
rating | numeric | | | generated always as (ag_property_text(properties, 'rating'::text, 'n'::"char")::numeric) stored
source | text | | | generated always as (ag_property_text(properties, 'site'::text, 's'::"char")) stored
Indexes:
"scored_end_idx" btree ("end", start)
"scored_id_idx" brin (id)
"scored_start_idx" btree (start, "end")
Inherits: castlists.ag_edge
```
간선을 쓰면 두 컬럼이 모두 채워집니다:
```cypher
MATCH (p:person), (f:film) CREATE (p)-[:scored {rating: 8.5, site: 'critics'}]->(f);
```
```
UPDATE 1
```
SQL은 그것을 컬럼 이름으로 읽습니다:
```sql
SELECT rating, source FROM castlists.scored;
```
```
rating | source
--------+---------
8.5 | critics
(1 row)
```
Cypher는 그것을 속성 키로 읽으므로, 두 번째 것은 `e.source`가 아니라 `e.site`입니다:
```cypher
MATCH ()-[e:scored]->() RETURN e.rating AS rating, e.site AS site;
```
```
rating | site
--------+---------
8.5 | critics
(1 row)
```
{#create-elabel-refusals-worth-recognizing}
### 알아 둘 만한 거부 사례
간선 레이블은 간선 레이블만 상속할 수 있습니다:
```cypher
CREATE ELABEL wrong INHERITS (person);
```
```
ERROR: parent graph label "person" is not edge label.
```
마지막으로 예제가 만든 그래프를 지웁니다:
```cypher
DROP GRAPH castlists CASCADE;
```
{#create-elabel-notes}
## 참고
레이블 이름은 수식할 수 없습니다. `CREATE ELABEL castlists.directed`는 구문 오류이고, 그래프는 항상
`graph_path`에서 옵니다. 설정된 것이 없으면 이 문장은 `graph_path is NULL`로 멈춥니다.
`\dGe`는 간선 레이블을 나열하고 일치하는 것마다 설명해 줍니다. `\dGl`은 두 종류의 레이블을 자세한
설명 없이 나열합니다.
{#create-elabel-compatibility}
## 호환성
{#create-elabel-isoiec-39075-gql}
### ISO/IEC 39075 (GQL)
GQL에는 레이블 DDL이 없습니다. 간선은 그래프 타입 안의 *간선 타입*으로 기술되며,
``(§12.6)에 해당합니다. 간선 타입은 그 간선의 끝점 정점 타입과 속성을
지정하고, 그것을 참조하는 그래프를 제약합니다. AgensGraph는 간선 레이블을 하나씩 선언하고, 간선
레이블은 자기 끝점도 속성도 제약하지 않습니다. 그래프 타입은
[GQL 적합성 표](../../appendix/gql-conformance.md)에 계획으로 기록돼 있습니다.
{#create-elabel-postgresql}
### PostgreSQL
`CREATE ELABEL`은 `CREATE TABLE`에 대응합니다. `UNLOGGED`, `IF NOT EXISTS`, `INHERITS`, `WITH`,
`TABLESPACE`는 거기서와 같은 뜻이고, 승격 속성 형태는 `GENERATED ALWAYS AS ... STORED` 생성 컬럼이
됩니다. 다른 점은 고정된 네 컬럼 모양, 함께 만들어지는 인덱스 세 개, `ag_label` 등록, 자동으로
붙는 부모, 그리고 순서가 중요한 옵션 배치입니다. `start`와 `end`는 외래 키로 선언되지 않으므로,
테이블 수준에서는 간선이 이미 지워진 정점을 가리키는 것을 막는 것이 없습니다. 그 일은
[`DROP VLABEL`](drop-label.md)이 처리합니다.
{#create-elabel-see-also}
## 관련 항목
- [`CREATE VLABEL`](create-vlabel.md)
- [`ALTER VLABEL`·`ALTER ELABEL`](alter-label.md)
- [`DROP VLABEL`·`DROP ELABEL`](drop-label.md)
- [`REINDEX VLABEL`·`REINDEX ELABEL`](reindex.md)
- [레이블 상속](../../concepts/label-inheritance.md)