본문 바로가기
WIKI 기술 지식 베이스

배열 함수

원문 보기 위키 갱신

배열 함수 (Array Functions)

Turso는 SQL에서 배열 컬럼과 배열을 다루는 함수들을 기본으로 제공해요. 배열을 만들고, 원소에 접근하고, 변환하는 작업을 쿼리 안에서 바로 할 수 있어요. 배열은 내부적으로는 압축된 레코드 형식 BLOB으로 저장되고, 출력될 때는 JSON 배열 모양으로 보여져요.

출처: 문서

본문

**Turso 확장 기능**: 배열 타입과 배열 함수는 SQL에서 순서 있는 컬렉션을 직접 다루는 Turso 전용 기능이에요. 표준 SQLite에는 없는 기능이에요. 배열 컬럼은 [STRICT 테이블](/sql-reference/statements/create-table#strict-tables)이 필요해요.

Turso는 배열 컬럼과 배열을 만들고, 조회하고, 변환하는 함수 집합을 기본으로 지원해요. 배열은 내부적으로는 압축된 레코드 형식 BLOB으로 저장되고, 출력 시에는 JSON 배열로 표시돼요.


배열 만들기

ARRAY[] 리터럴

표현식 목록으로 배열을 만들어요.

ARRAY[expr1, expr2, ...]
SELECT ARRAY[1, 2, 3];
-- [1,2,3]

SELECT ARRAY['hello', 'world'];
-- ["hello","world"]

SELECT ARRAY[];
-- []

배열은 JSON 텍스트로 삽입할 수도 있어요:

CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[]) STRICT;

INSERT INTO t VALUES (1, ARRAY['a', 'b', 'c']);
INSERT INTO t VALUES (2, '["x", "y", "z"]');

SELECT tags FROM t ORDER BY id;
-- ["a","b","c"]
-- ["x","y","z"]

string_to_array

문자열을 구분자 기준으로 잘라서 배열로 만들어요.

string_to_array(text, delimiter)
string_to_array(text, delimiter, null_string)
매개변수 타입 설명
text TEXT 자를 문자열
delimiter TEXT 나눌 구분자. NULL이면 개별 문자로 나눠요
null_string TEXT 선택. 이 문자열과 일치하는 원소는 NULL로 바꿔요

반환: BLOB -- 배열 값.

SELECT string_to_array('one,two,three', ',');
-- ["one","two","three"]

SELECT string_to_array('hello', NULL);
-- ["h","e","l","l","o"]

SELECT string_to_array('a,NULL,b', ',', 'NULL');
-- ["a",null,"b"]

원소 접근

첨자 연산자 []

0부터 시작하는 인덱스로 개별 원소에 접근해요. 범위를 벗어나거나 음수 인덱스면 NULL을 반환해요.

array_expr[index]
CREATE TABLE t (id INTEGER PRIMARY KEY, items TEXT[]) STRICT;
INSERT INTO t VALUES (1, '["first", "second", "third"]');

SELECT items[0], items[1], items[2] FROM t;
-- first|second|third

SELECT items[100] FROM t;
-- NULL

SELECT items[-1] FROM t;
-- NULL

첨자는 체인처럼 연결해서 다차원 배열에도 쓸 수 있어요:

CREATE TABLE m (id INTEGER PRIMARY KEY, matrix INTEGER[][]) STRICT;
INSERT INTO m VALUES (1, ARRAY[ARRAY[1,2], ARRAY[3,4]]);

SELECT matrix[0][1] FROM m;
-- 2

SELECT matrix[1][0] FROM m;
-- 3

슬라이스 연산자 [start:end]

반열린 범위 [start, end)로 부분 배열을 뽑아내요.

array_expr[start:end]
CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[]) STRICT;
INSERT INTO t VALUES (1, ARRAY['a','b','c','d']);

SELECT tags[1:3] FROM t;
-- ["b","c"]

스칼라 함수

array_length

배열의 주어진 차원에 있는 원소 개수를 반환해요.

array_length(array)
array_length(array, dimension)
매개변수 타입 설명
array BLOB/TEXT 길이를 재 배열
dimension INTEGER 선택. 1부터 시작하는 차원 인덱스. 기본값은 1 (가장 바깥 차원)

반환: INTEGER -- dimension의 원소 개수. 입력이 NULL이거나 dimension < 1이거나 dimension이 배열의 중첩 깊이를 넘으면 NULL이에요.

다차원 배열에서는 탐색기가 각 단계에서 0번 원소를 들여다보기 때문에, 같은 깊이의 모든 내부 배열이 같은 길이를 가진 것처럼 값이 보고돼요 (Turso가 실제로 만드는 레이아웃이 그래요).

SELECT array_length(ARRAY[10, 20, 30]);
-- 3

SELECT array_length('[]');
-- 0

SELECT array_length(NULL);
-- NULL

SELECT array_length(ARRAY[ARRAY[1,2,3], ARRAY[4,5,6]], 1);
-- 2 (outer dimension)

SELECT array_length(ARRAY[ARRAY[1,2,3], ARRAY[4,5,6]], 2);
-- 3 (inner dimension)

SELECT array_length(ARRAY[1, 2, 3], 2);
-- NULL (dimension beyond the array's depth)

SELECT array_length(ARRAY[1, 2, 3], 0);
-- NULL (dimension < 1)

array_upper

배열의 주어진 차원의 상한(포함)을 반환해요.

array_upper(array, dimension)
매개변수 타입 설명
array BLOB/TEXT 길이를 재 배열
dimension INTEGER 1부터 시작하는 차원 인덱스

반환: INTEGER -- dimension의 상한. NULL 조건은 array_length와 같아요.

Turso 배열은 항상 1 인덱스라서, 유효한 입력에 대해서는 array_upper(arr, dim)이 array_length(arr, dim)과 같아요. 이 함수는 PostgreSQL 호환성을 위해 제공되는 거예요.

SELECT array_upper(ARRAY[10, 20, 30], 1);
-- 3

SELECT array_upper(ARRAY[ARRAY[1,2,3], ARRAY[4,5,6]], 2);
-- 3

SELECT array_upper(ARRAY[1, 2, 3], 2);
-- NULL

array_append

배열 끝에 원소를 붙여요.

array_append(array, element)
매개변수 타입 설명
array BLOB/TEXT 원소를 붙일 배열. NULL이면 원소 하나짜리 새 배열을 만들어요
element any 붙일 원소

반환: BLOB -- 원소가 붙은 새 배열.

SELECT array_append('[1, 2]', 3);
-- [1,2,3]

SELECT array_append('[]', 'x');
-- ["x"]

SELECT array_append(NULL, 'x');
-- ["x"]

array_prepend

배열 앞에 원소를 붙여요.

array_prepend(element, array)
매개변수 타입 설명
element any 앞에 붙일 원소
array BLOB/TEXT 원소를 붙일 배열

반환: BLOB -- 원소가 앞에 붙은 새 배열.

SELECT array_prepend('z', '["a", "b"]');
-- ["z","a","b"]

array_cat

두 배열을 이어 붙여요.

array_cat(array1, array2)
매개변수 타입 설명
array1 BLOB/TEXT 첫 번째 배열
array2 BLOB/TEXT 두 번째 배열

반환: BLOB -- 두 배열의 모든 원소를 담은 새 배열.

SELECT array_cat('[1, 2]', '[3, 4]');
-- [1,2,3,4]

SELECT array_cat('[]', '[1, 2]');
-- [1,2]

|| 연산자로도 배열을 이어 붙일 수 있어요:

CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[]) STRICT;
INSERT INTO t VALUES (1, ARRAY['a', 'b']);

-- Append element
SELECT tags || 'c' FROM t;
-- ["a","b","c"]

-- Prepend element
SELECT 'z' || tags FROM t;
-- ["z","a","b"]

-- Concatenate arrays
SELECT tags || ARRAY['x', 'y'] FROM t;
-- ["a","b","x","y"]

array_remove

배열에서 어떤 원소가 나오는 부분을 모두 없애요.

array_remove(array, element)
매개변수 타입 설명
array BLOB/TEXT 원소를 뺄 배열
element any 뺄 원소

반환: BLOB -- 해당 원소가 모두 제거된 새 배열.

SELECT array_remove('[1, 2, 3, 2, 1]', 2);
-- [1,3,1]

SELECT array_remove('[1, 2, 3]', 99);
-- [1,2,3]

array_contains

배열에 특정 원소가 들어 있는지 검사해요.

array_contains(array, element)
매개변수 타입 설명
array BLOB/TEXT 찾아볼 배열
element any 찾을 원소

반환: INTEGER -- 찾으면 1, 없으면 0.

SELECT array_contains('[1, 2, 3]', 2);
-- 1

SELECT array_contains('[1, 2, 3]', 5);
-- 0

array_position

어떤 원소가 처음 나오는 위치를 0부터 시작하는 인덱스로 반환해요.

array_position(array, element)
매개변수 타입 설명
array BLOB/TEXT 찾아볼 배열
element any 찾을 원소

반환: INTEGER -- 0부터 시작하는 인덱스, 없으면 NULL.

SELECT array_position('[10, 20, 30]', 20);
-- 1

SELECT array_position('[10, 20, 30]', 10);
-- 0

SELECT array_position('[10, 20, 30]', 99);
-- NULL

array_slice

인덱스 범위로 부분 배열을 뽑아내요.

array_slice(array, start, end)
매개변수 타입 설명
array BLOB/TEXT 원본 배열
start INTEGER 시작 인덱스 (0부터 시작, 포함). NULL이면 0
end INTEGER 끝 인덱스 (제외)

반환: BLOB -- start부터 end - 1까지의 원소를 담은 새 배열.

SELECT array_slice('[1, 2, 3, 4]', 1, 3);
-- [2,3]

SELECT array_slice('[1, 2, 3, 4]', NULL, 2);
-- [1,2]

array_to_string

배열 원소를 구분자로 이어서 하나의 문자열로 만들어요.

array_to_string(array, delimiter)
array_to_string(array, delimiter, null_string)
매개변수 타입 설명
array BLOB/TEXT 이어 붙일 배열
delimiter TEXT 원소 사이에 넣을 구분자
null_string TEXT 선택. NULL 원소를 대신할 텍스트. 생략하면 NULL은 건너뛰어요

반환: TEXT -- 이어 붙인 문자열.

SELECT array_to_string('[1, 2, 3]', ',');
-- 1,2,3

SELECT array_to_string('["hello", "world"]', ' ');
-- hello world

SELECT array_to_string('[1, null, 3]', ',');
-- 1,3

SELECT array_to_string('[1, null, 3]', ',', 'N/A');
-- 1,N/A,3

array_contains_all

한 배열이 다른 배열의 모든 원소를 포함하는지 검사해요.

array_contains_all(haystack, needles)
매개변수 타입 설명
haystack BLOB/TEXT 찾아볼 배열
needles BLOB/TEXT 찾을 원소들의 배열

반환: INTEGER -- needles의 모든 원소가 haystack에 있으면 1, 아니면 0.

SELECT array_contains_all('[1, 2, 3, 4, 5]', '[2, 4]');
-- 1

SELECT array_contains_all('[1, 2, 3]', '[2, 4]');
-- 0

SELECT array_contains_all('[1, 2, 3]', '[]');
-- 1

@> 연산자가 이 함수의 별칭이에요. 배열 연산자를 참고하세요.

array_overlap

두 배열이 공통 원소를 하나라도 공유하는지 검사해요.

array_overlap(array1, array2)
매개변수 타입 설명
array1 BLOB/TEXT 첫 번째 배열
array2 BLOB/TEXT 두 번째 배열

반환: INTEGER -- 어느 하나의 원소라도 양쪽에 모두 나타나면 1, 아니면 0.

SELECT array_overlap('[1, 2, 3]', '[3, 4, 5]');
-- 1

SELECT array_overlap('[1, 2, 3]', '[4, 5, 6]');
-- 0

SELECT array_overlap('[]', '[1, 2]');
-- 0

array_overlaps도 별칭으로 받아들여요. && 연산자 역시 별칭이에요. 배열 연산자를 참고하세요.


집계 함수

array_agg

행 그룹의 값들을 모아서 하나의 배열로 만들어요.

array_agg(expression)
매개변수 타입 설명
expression any 모을 값. NULL 값도 포함돼요

반환: BLOB -- 그룹의 각 행마다 원소 하나씩 담긴 배열. 빈 그룹이면 NULL을 반환해요.

CREATE TABLE t (grp TEXT, val TEXT);
INSERT INTO t VALUES ('x', 'a'), ('x', 'b'), ('y', 'c'), ('y', 'd'), ('y', 'e');

SELECT grp, array_agg(val) FROM t GROUP BY grp ORDER BY grp;
grp array_agg(val)
x ["a","b"]
y ["c","d","e"]
-- Control order with a subquery
SELECT array_agg(val) FROM (SELECT val FROM t ORDER BY val);
-- ["a","b","c","d","e"]

-- Combine with array_length
SELECT array_length(array_agg(val)) FROM t;
-- 5

배열 연산자

포함: @>

왼쪽 배열이 오른쪽 배열의 모든 원소를 포함하는지 검사해요. array_contains_all(left, right)와 같아요.

array1 @> array2
SELECT ARRAY[1,2,3] @> ARRAY[1,2];
-- 1

SELECT ARRAY[1,2,3] @> ARRAY[1,4];
-- 0

겹침: &&

두 배열이 공통 원소를 하나라도 공유하는지 검사해요. array_overlap(left, right)와 같아요.

array1 && array2
SELECT ARRAY[1,2,3] && ARRAY[3,4,5];
-- 1

SELECT ARRAY[1,2] && ARRAY[3,4];
-- 0

비교: =, !=, &lt;, >, &lt;=, >=

배열은 원소별 비교를 지원해요. 원소는 왼쪽에서 오른쪽으로 짝지어 비교하고, 비교한 원소가 모두 같으면 더 짧은 배열이 더 작은 것으로 취급돼요.

SELECT ARRAY[1,2,3] = ARRAY[1,2,3];   -- 1
SELECT ARRAY[1,2] < ARRAY[1,3];       -- 1
SELECT ARRAY[1,2] < ARRAY[1,2,3];     -- 1 (shorter is less)

이어 붙이기: ||

피연산자 중 하나라도 배열이면, ||는 문자열 연결 대신 배열 연결이나 원소 추가/앞붙이기를 해줘요.

SELECT ARRAY[1,2] || ARRAY[3,4];  -- [1,2,3,4]  (array + array)
SELECT ARRAY[1,2] || 3;           -- [1,2,3]    (append element)
SELECT 0 || ARRAY[1,2];           -- [0,1,2]    (prepend element)

WHERE 절에서 연산자 사용하기

CREATE TABLE docs (id INTEGER PRIMARY KEY, tags TEXT[]) STRICT;
INSERT INTO docs VALUES (1, ARRAY['sql','database','tutorial']);
INSERT INTO docs VALUES (2, ARRAY['rust','systems']);
INSERT INTO docs VALUES (3, ARRAY['sql','rust']);

-- Find docs tagged with both 'sql' and 'rust'
SELECT id FROM docs WHERE tags @> ARRAY['sql','rust'];
-- 3

-- Find docs with any overlap with a set of tags
SELECT id FROM docs WHERE tags && ARRAY['database','systems'] ORDER BY id;
-- 1
-- 2

첨자 대입

UPDATE 문에서 첨자 문법을 쓰면 배열 원소를 개별적으로 갱신할 수 있어요.

UPDATE table SET column[index] = value WHERE ...;
CREATE TABLE t (id INTEGER PRIMARY KEY, tags TEXT[]) STRICT;
INSERT INTO t VALUES (1, ARRAY['a','b','c']);

UPDATE t SET tags[0] = 'X' WHERE id = 1;
SELECT tags FROM t;
-- ["X","b","c"]

-- Multiple subscripts in one UPDATE
UPDATE t SET tags[0] = 'A', tags[2] = 'C' WHERE id = 1;
SELECT tags FROM t;
-- ["A","b","C"]

범위를 벗어나는 대입은 조용히 무시돼요 (배열은 그대로 유지돼요).


예제

태깅 시스템

CREATE TABLE articles (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL,
    tags TEXT[]
) STRICT;

INSERT INTO articles VALUES (1, 'Getting Started with SQL', ARRAY['sql','beginner','tutorial']);
INSERT INTO articles VALUES (2, 'Advanced Indexing', ARRAY['sql','performance','advanced']);
INSERT INTO articles VALUES (3, 'Rust for Systems', ARRAY['rust','systems']);

-- Find articles tagged 'sql'
SELECT title FROM articles WHERE array_contains(tags, 'sql');

-- Find articles with both 'sql' and 'advanced'
SELECT title FROM articles WHERE tags @> ARRAY['sql','advanced'];
-- Advanced Indexing

-- Count tags per article
SELECT title, array_length(tags) AS tag_count FROM articles;

-- Collect all unique tags
SELECT array_agg(DISTINCT tag) FROM (
    SELECT array_to_string(tags, ',') AS tag FROM articles
);

다차원 배열

CREATE TABLE matrices (
    id INTEGER PRIMARY KEY,
    data INTEGER[][]
) STRICT;

INSERT INTO matrices VALUES (1, ARRAY[ARRAY[1,2,3], ARRAY[4,5,6]]);

-- Access a row
SELECT data[0] FROM matrices WHERE id = 1;
-- [1,2,3]

-- Access a single element
SELECT data[1][2] FROM matrices WHERE id = 1;
-- 6

-- Get number of rows
SELECT array_length(data) FROM matrices WHERE id = 1;
-- 2

타입 검사

STRICT 테이블의 배열 컬럼은 삽입과 갱신 시점에 원소 타입을 검증해요:

CREATE TABLE scores (id INTEGER PRIMARY KEY, vals INTEGER[]) STRICT;

-- OK: exact integers
INSERT INTO scores VALUES (1, '[1, 2, 3]');

-- OK: floats that are exact integers are coerced
INSERT INTO scores VALUES (2, '[1.0, 2.0]');

-- OK: numeric strings are coerced
INSERT INTO scores VALUES (3, '["42", "100"]');

-- Error: non-numeric string in INTEGER array
INSERT INTO scores VALUES (4, '["hello"]');
-- Error: cannot store TEXT value in INTEGER

-- NULL elements are always allowed
INSERT INTO scores VALUES (5, '[1, null, 3]');

더 알아보기 (Learn more)