Web UI 정렬·필터링·페이지네이션
Web UI 정렬·필터링·페이지네이션 (Sorting, Filtering and Pagination)
ClickHouse 내장 Web SQL UI에서 쿼리를 수정하지 않고도 결과를 컬럼별로 정렬하고, 값으로 필터링하고, 페이지를 넘길 수 있습니다. 이 모든 작업은 브라우저가 아니라 서버에서 이루어져 화면의 결과가 항상 전체 쿼리의 정렬·필터·페이지 결과입니다.
출처: 문서
본문
내장 Web SQL UI(play.html, ClickHouse HTTP 포트의 /play 경로에서 제공됨)는 쿼리를 수정하지 않고도 결과를 컬럼별로 정렬하고, 값으로 필터링하고, 페이지를 넘길 수 있게 해 줍니다.
이 중 어떤 것도 브라우저에서 일어나지 않습니다. 변경할 때마다 쿼리가 해당 쿼리 구성(query-construction) 설정과 함께 다시 실행되는데, 서버가 쿼리를 파생 테이블로 감싸 바깥쪽에 ORDER BY, WHERE, LIMIT를 붙여 구체화합니다. 따라서 화면의 결과는 페이지가 우연히 들고 있던 행을 재배열한 것이 아니라 전체 쿼리의 정렬·필터·페이지가 적용된 결과입니다.
정렬 (Sorting)
모든 컬럼 헤더 오른쪽에 화살표 두 개가 나타납니다: ▲는 해당 컬럼으로 오름차순 정렬, ▼는 내림차순 정렬입니다. 화살표를 클릭하면 그 정렬이 활성화되고 쿼리가 다시 실행됩니다. 이미 활성화된 방향의 화살표를 클릭하면 비활성화되고, 반대 방향 화살표를 클릭하면 전환됩니다. 화살표는 실제 버튼이라 키보드 사용자가 탭으로 이동해 키보드로 활성화할 수 있고, 각각 상태를 보조 기술(assistive technology)에 노출합니다(헤더 자체가 aria-sort를 지닙니다).
호버링 포인터(마우스)가 있는 기기에서는 헤더에 호버하거나 화살표가 포커스될 때만 화살표가 표시됩니다. 호버가 없는 터치·조잡한 포인터 기기에서는 항상 표시되어 바로 탭할 수 있습니다. 정렬에 포함된 컬럼은 호버하지 않아도 두 화살표를 모두 보여줍니다. 하나는 정렬을 한눈에 읽기 위함이고, 다른 하나는 방향을 뒤집는 것이 가장 유력한 다음 동작이기 때문입니다.
컬럼 헤더의 모든 아이콘은 어떤 기능을 제어하든 같은 방식으로 읽힙니다: 해당 기능이 컬럼에 적용되지 않는 동안에는 흐리게(muted) 표시되고, 적용되면 효과 중인 컨트롤용으로 예약된 색 — 밝은 테마에서는 마젠타, 어두운 테마에서는 노랑 — 으로 그려집니다. 이때는 호버 없이도 계속 표시됩니다. 흐림(muting)은 색이 빠지는 것이지 투명도가 빠지는 것이 아니므로, 아이콘이 컬럼 이름이나 셀의 색상 코딩 위에서 흐려 보이지 않습니다.
여러 컬럼으로 정렬
정렬을 활성화하면 현재 정렬을 대체합니다: 이전에 정렬되던 컬럼은 비활성화되고, 클릭한 컬럼이 유일한 정렬 키가 됩니다. 클릭하는 동안 Shift를 누르고 있으면 이미 적용된 정렬 키를 유지한 채 이 컬럼을 그 뒤에 추가합니다. 이것이 ORDER BY가 사용하는 순서입니다 — 첫 번째 키가 우선하고, 이후의 각 키가 앞선 키들의 동률을 깨줍니다. 정렬 키가 여러 개면 모든 활성 화살표가 해당 컬럼의 순서 내 위치를 위첨자로 보여줍니다 (▼¹, ▲², …).
이미 정렬 키인 컬럼에 Shift를 누르면 방향만 바뀌고 순서 내 위치는 유지됩니다. 컬럼을 비활성화하면 그 컬럼만 빠지고 나머지 키는 그 자리에 남습니다.
필터링 (Filtering)
컬럼 헤더에서
깔때기 아이콘이 모든 컬럼 헤더에 나타나며, 정렬 화살표 옆에 같은 방식으로 드러납니다. 클릭하면 해당 컬럼에 대한 조건(predicate) 입력이 열리고 오른쪽에 적용 버튼(▶)이 붙습니다. placeholder는 컬럼 타입에 맞는 조건 형태를 제안합니다 — 숫자는 > 10, 문자열은 LIKE '%test%'. 입력은 헤더 셀 안에서 열리며 헤더 셀이 두 번째 줄로 늘어나 공간을 만듭니다 — 필터가 설정되면 그 필터가 표시되는 바로 그 줄이어서, 필터는 읽히는 곳에서 편집됩니다. 입력하는 것은 컬럼 이름 뒤에 오는 부분이라서 > 10은 WHERE column > 10이 되고, 서버가 그 자리에서 수용하는 어떤 표현식이든 동작합니다 — BETWEEN 1 AND 5, IN (1, 2, 3), IS NOT NULL, % 2 = 0 등.
적용 버튼(또는 Enter)을 누르면 필터가 적용되고 쿼리가 다시 실행됩니다. Esc 또는 다른 곳을 클릭·탭하면 편집을 버리고 입력을 치웁니다. 적용 버튼은 적용할 것이 없을 때 — 필터가 없는 컬럼의 빈 상자 — 비활성화되지만, 필터가 있는 컬럼의 빈 상자에서는 활성화됩니다. 상자를 비우고 적용하는 것이 입력에서 필터를 제거하는 방법이기 때문입니다. 여러 컬럼의 필터는 AND로 결합됩니다.
셀에서
셀을 선택해도 그 값에 대한 필터가 컬럼이 지원하는 것이라면 복사 아이콘 옆에 깔때기 아이콘이 나타납니다. 클릭하면 맞는 비교 연산을 제안합니다:
| 값 | 제안 |
|---|---|
| 숫자 | = , != , > , < , >= , <= |
| 날짜·시간 ( Date , Date32 , DateTime , DateTime64 ) | = , != , > , < , >= , <= |
| 최대 100자 문자열 | = , != , contains — 빈 문자열은 = 와 != 만, 모든 문자열이 그것을 포함하므로 |
| Bool | true , false — 이 셀의 값에 대한 비교가 아니라 두 값 그 자체 |
| Enum | = , != — 닫힌 이름 집합에 대한 부분 일치는 유용한 필터가 아님 |
| NULL | IS NULL , IS NOT NULL |
하나를 고르면 즉시 적용됩니다. 날짜·시간·enum은 서버가 렌더링한 바로 그 텍스트와 비교되는데, ClickHouse가 이를 컬럼 자신의 타입으로 다시 파싱합니다(enum의 경우 값의 이름). contains는 값 자신의 %와 _를 이스케이프한 LIKE 패턴이 되어 값을 리터럴로 일치시킵니다. 위 목록에 해당하지 않는 값 — 배열, 튜플, 맵, 긴 텍스트 — 의 셀은 메뉴를 제공하지 않습니다. 해당 컬럼은 여전히 헤더 입력에서 필터링할 수 있습니다.
적용 중인 필터
필터링 중인 컬럼은 설정된 동안 이름 아래에 조건을 표시하므로, 화면의 행이 결과의 부분집합이라는 사실이 절대 보이지 않게 숨지지 않습니다. 깔때기는 그 옆, 헤더의 왼쪽 아래 모서리로 내려갑니다. 조건을 클릭하면 그 자리에 입력이 다시 열리고, 옆의 ✕는 필터를 제거합니다(빈 입력을 적용하는 것도 마찬가지). 둘 다 쿼리를 다시 실행합니다.
컬럼당 필터는 하나입니다. 출처와 무관하게 동일합니다: 셀에서 필터를 설정하면 헤더 입력이 넣었던 것을 대체하고, 헤더는 항상 적용 중인 필터를 표시합니다.
필터링된 결과가 비어 돌아와도 컬럼 헤더를 유지합니다(빈 결과가 아니면 보여줄 세로 레이아웃 대신). 그래서 아무것도 일치시키지 못한 필터를 보고 다시 제거할 수 있습니다.
페이지네이션 (Pagination)
결과는 상한까지 표시됩니다 — 1000행, 또는 매우 넓은 결과라면 더 적게. 결과가 그 상한에서 잘리면 볼 것이 더 있다는 뜻이고 테이블 아래에 페이저가 나타납니다:
Page: 1 2 … Per page: 1000
페이지 번호를 클릭하면 페이지 크기를 표시 상한으로 설정하고 그 페이지를 page 설정으로 삼아 쿼리를 다시 실행합니다. 서버는 페이지를 해당 OFFSET으로 번역합니다. 페이저는 결과가 페이지 처리되는 동안 계속 유지됩니다. 각 페이지가 정확히 가득 차 더 이상 잘려 보이지 않더라도요.
페이지 수는 표시되지 않습니다. 알 수 없기 때문입니다 — 결과의 행을 세려면 두 번째 쿼리를 실행해야 합니다. 페이저는 현재 페이지 앞의 10개 페이지, 현재 페이지, 다음 페이지를 나열하고 …를 뒤에 붙입니다. …를 클릭하면 임의 페이지 번호 입력이 되고, 입력이 포커스를 잃으면 적용됩니다(Enter를 누르면 그렇게 됩니다).
다음 페이지는 현재 페이지가 가득 찼을 때만 제안됩니다. 페이지가 담을 수 있는 것보다 적은 행으로 돌아온 페이지는 결과의 끝이므로 그 뒤에 페이지가 없습니다. (결과 길이가 우연히 페이지 크기의 정확한 배수라면 페이지가 하나 더 제안되는데, 이 페이지는 빈 채로 돌아옵니다: 그 경우를 구분하려면 역시 행을 세어야 하기 때문입니다.)
Per page는 페이지가 담는 행 수를 보여주고, 같은 방식으로 편집합니다: 값을 클릭하고 다른 것을 입력하세요. 결과가 한 번에 표시할 수 있는 것보다 클 수는 없습니다 — 더 큰 페이지는 테이블이 잘라버리는 행을 반환하고 다음 페이지가 그 뒤에서 시작하므로, 페이지를 넘기면 맞지 않았던 행을 조용히 건너뛰게 됩니다. 더 큰 숫자는 그 최댓값으로 취급되고 값이 그렇게 표시됩니다. 값을 바꾸면 새 크기의 페이지는 다른 행을 담으므로 첫 페이지에서 다시 시작합니다.
정렬이나 필터 중 하나라도 바꾸면 첫 페이지로 돌아갑니다: 둘 다 결과가 어떤 행을 갖는지, 또는 어떤 순서인지를 바꾸므로 사용자가 있던 페이지가 더 이상 같은 조각을 가리키지 않기 때문입니다.
적용 방식
모양(shape)은 order, filter, limit, page 쿼리 구성 설정으로 전송됩니다. 서버가 이를 텍스트가 아니라 파싱된 쿼리에 적용하기 때문에 쿼리가 이미 무엇이든 그와 어울립니다: UNION, 끝의 FORMAT 절, 또는 자체 ORDER BY와 LIMIT 모두 계속 동작하고, 편집기의 쿼리는 절대 다시 쓰이지 않습니다.
컬럼 이름은 인용된 식별자로 전달되므로 이름이 표현식(count())이거나 공백을 담은 결과 컬럼도 정렬·필터 키로 쓸 수 있습니다.
제공되는 경우
모양(shape)은 그 설정들이 다루는 문장, 즉 SELECT와 UNION 쿼리에 대해서만 제공됩니다(WITH 절이나 FROM으로 시작하는 쿼리 포함). SHOW, DESCRIBE, EXISTS, EXPLAIN에는 제공되지 않습니다: 그것들도 테이블을 만들긴 하지만 설정이 적용되지 않으므로, 거기에 컨트롤을 두면 행이 갖지 않는 결과를 약속하게 됩니다.
모양은 단일 문장의 결과에 적용되므로, 문장마다 별도 쿼리와 별도 컬럼을 갖는 "Run all" 다중 문장 실행에는 제공되지 않습니다.
첫 페이지에 행이 최대 하나뿐인 결과에도 제공되지 않습니다: 단일 행의 어떤 순서도 같은 순서이고, 필터는 그 행을 유지하거나 버릴 뿐이므로 컨트롤이 같은 행들에 대해 쿼리를 다시 실행하는 일밖에 못 하기 때문입니다 — 빈 결과는 버릴 행조차 없습니다. 색상 코딩 토글이 거기서 숨겨지는 것과 같은 이유입니다: 행이 하나 이하이면 비교할 것이 없습니다.
그러나 두 가지 단일 행 결과는 컨트롤을 유지합니다. 컨트롤이 유일한 복귀 수단이기 때문입니다:
- 이미 정렬되거나 필터된 결과로, 정렬이 되돌릴 수 있고 필터가 지울 수 있어야 하며 — 단일 행과 일치하는 필터는 정확히 긴 결과가 짧은 결과가 되는 방식이고, 컨트롤을 함께 빼면 사용자가 그 상태에 갇히게 됩니다;
- 표시 상한에서 잘린 결과로, 더 긴 결과의 첫 페이지입니다: 페이지 크기는 테이블이 한 번에 보여줄 수 있는 셀 수로 제한되므로 매우 넓은 결과는 단일 행으로 잘릴 수 있고, 그 너머의 행이 바로 정렬과 페이지넘이 도달하려는 대상입니다.
모양은 그것이 만들어졌던 문장에 속합니다. 쿼리를 편집한 후 또는 다중 문장 편집기의 다른 문장으로 커서를 옮긴 후 다른 문장을 실행하면, 새 문장이 가질 수 없는 컬럼에 ORDER BY나 WHERE를 적용하는 대신 모양을 버립니다.
또한 그 문장이 실행된 컨텍스트에도 속합니다: 선택된 데이터베이스, 전송된 서버·사용자, 쿼리 파라미터 값. 이 중 어느 하나라도 바뀐 후에는 같은 텍스트가 다른 컬럼을 가리킵니다 — 데이터베이스를 전환한 후의 SELECT * FROM events, 파라미터를 편집한 후의 SELECT * FROM {tbl:Identifier} — 그래서 그곳에서도 모양이 버려지고, 다음 실행은 모양 없는 결과를 반환합니다.
다운로드와 복사
다운로드는 같은 모양으로 생성 쿼리를 다시 실행하므로, 내보낸 파일에는 화면의 결과와 같은 순서로 같은 행이 담깁니다. 복사는 렌더링된 결과를 사용하므로 역시 그와 일치합니다.
지속성 (Persistence)
모양은 페이지 URL(sort_columns, filters, page, page_size), 브라우저 히스토리, 탭별 결과 스냅샷에 기억됩니다. 그래서 페이지를 새로고침하거나 링크를 공유하거나 뒤로/앞으로 탐색해도 유지됩니다. 모양은 표시 방식뿐 아니라 행을 결정하므로, 쿼리를 자동 실행하는(run=1) 공유 링크는 같은 모양으로 다시 실행되어 결과 자체를 재현합니다. 간결함을 위해 활성 모양만 저장되고, 모양 없는 결과는 URL이나 히스토리 상태에 아무것도 추가하지 않습니다.
복원된 결과는 위에서 설명한 대로 그것을 만든 컨텍스트에 모양을 묶어둡니다: 스냅샷은 행이 만들어진 데이터베이스·연결·파라미터 값을 기록하므로, 그중 하나를 바꾼 후 문장을 다시 실행하면 다양한 결과에 적용하는 대신 모양을 버립니다.
색상 코딩 모드와 고정 컬럼처럼 모양도 쿼리 탭별로 유지됩니다. 그래서 한 탭에서 결과를 정렬·필터링해도 다른 탭의 결과가 다시 실행되지 않습니다.
제한사항
- 서버가 적용할 수 없는 모양은 쿼리를 실패시키고, 다른 실패 쿼리와 마찬가지로 오류가 표시됩니다. 실패한 실행은 헤더와 페이저를 모두 렌더링하지 않으므로 모양이 그때 버려집니다.
- 정렬과 필터링은 결과에서 컬럼을 이름으로 식별합니다. 결과가 같은 이름을 두 번 담을 수 있고(
SELECT 1 AS x, 2 AS x, 컬럼 이름을 공유하는 테이블의 조인), 그런 컬럼은 이름으로 구분할 수 없으므로 정렬·필터 컨트롤을 얻지 못합니다. 같은 결과의 이름이 유일한 컬럼은 컨트롤을 유지합니다. - 다중 컬럼 정렬은
Shift키가 필요하므로 터치 기기에서는 사용할 수 없고, 단일 컬럼 정렬은 가능합니다. - 단일 행 결과의 세로(전치) 레이아웃에는 컬럼 헤더가 없어 컨트롤이 없습니다. 사용자가 이미 모양을 입힌 결과는 그 이유로 가로 레이아웃을 유지합니다.