2017년 8월 14일 월요일

[Cassandra 레퍼런스] [Data Manipulation] SELECT 문

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.




SELECT


데이터에서 데이터 쿼리는 SELECT 문을 사용하여 수행됩니다.

select_statement ::=  SELECT [ JSON | DISTINCT ] ( select_clause | '*' )
                      FROM table_name
                      [ WHERE where_clause ]
                      [ GROUP BY group_by_clause ]
                      [ ORDER BY ordering_clause ]
                      [ PER PARTITION LIMIT (integer | bind_marker) ]
                      [ LIMIT (integer | bind_marker) ]
                      [ ALLOW FILTERING ]
select_clause    ::=  selector [ AS identifier ] ( ',' selector [ AS identifier ] )
selector         ::=  column_name
                      | term
                      | CAST '(' selector AS cql_type ')'
                      | function_name '(' [ selector ( ',' selector )* ] ')'
                      | COUNT '(' '*' ')'
where_clause     ::=  relation ( AND relation )*
relation         ::=  column_name operator term
                      '(' column_name ( ',' column_name )* ')' operator tuple_literal
                      TOKEN '(' column_name ( ',' column_name )* ')' operator term
operator         ::=  '=' | '<' | '>' | '<=' | '>=' | '!=' | IN | CONTAINS | CONTAINS KEY
group_by_clause  ::=  column_name ( ',' column_name )*
ordering_clause  ::=  column_name [ ASC | DESC ] ( ',' column_name [ ASC | DESC ] )*

예를 들어,

SELECT name, occupation FROM users WHERE userid IN (199, 200, 207);
SELECT JSON name, occupation FROM users WHERE userid = 199;
SELECT name AS user_name, occupation AS user_occupation FROM users;

SELECT time, value
FROM events
WHERE event_type = 'myEvent'
  AND time > '2011-02-03'
  AND time <= '2012-01-01'

SELECT COUNT (*) AS user_count FROM users;


SELECT 문은 테이블의 하나 이상의 행에 대해 하나 이상의 열을 읽습니다.
그리고 해당 요청과 일치하는 행의 결과를 반환하며, 각 행에는 쿼리에 해당하는 선택 항목의 값이 들어 있습니다.
또한 Aggregate 함수를 포함한 함수를 결과에 적용 할 수 있습니다.
SELECT 문은 table에서의 선택 조항과 항목을 가지는 최소한 하나의 select 절을 포함합니다.
대부분의 경우, select 는 where 절을 포함하고 있고, 선택적으로 order 혹은 limit을 결과에 적용할 수 있습니다.
마지막으로, 필터링이 필요한 쿼리는 ALLOW FILTERING가 적용되는 경우에만 허용됩니다.


Selection clause 

select 절은 어떠한 column이 쿼리되고 결과로 리턴될 것인가를 결정하는 동시에, 리턴되는 결과가 어떠한 형식으로 나타내어질 것인가를 결정합니다.
이 항목들은 콤마(,)로 구분되는 항목들로 구성되거나, 혹은 와일드카드(*)를 사용해 모든 항목을 가져올 수 있습니다.


Selectors

select 항목은 다음 중에 하나가 될 수 있습니다.

- 선택한 열의 이름으로 해당 열의 값을 검색
- 기간, 흔히 다른 항목들 안에서 사용되는 function
- 다른 type으로 변환하는 casting
- function
- count


Aliases 

모든 상위의 selector는 별칭으로 사용될 수 있습니다.
만약, 그렇게 사용한다면 리턴되는 결과에서도 해당 column은 별칭으로 표현되게 됩니다.
예를 들어, 아래와 같이 결과가 리턴되게 됩니다.


// Without alias
SELECT intAsBlob(4) FROM t;

//  intAsBlob(4)
// --------------
//  0x00000004

// With alias
SELECT intAsBlob(4) AS four FROM t;

//  four
// ------------
//  0x00000004



WRITETIME and TTL function

Selection은 특별한 두가지 기능을 제공합니다 : WRITETIME, TTL
두 기능은 하나의 값만을 가지며 그 값은 반드시 column name이어야만 합니다.
이러한 기능은 내부적으로 저장된 해당 column의 meta 값을 가져올 수 있게 합니다.

- WRITETIME : 해당 칼럼의 값의 timestamp
- TTL : 종료 시간을 설정한 경우, 해당 칼럼 값의 남아있는 시간


The WHERE clause

Where 절은 어떠한 row가 쿼리되어야 하는지 명시합니다.
이것은 PRIMARY KEY의 일부 혹은 secondary index의 관계로 구성됩니다.
모든 관계가 쿼리 내에서 허용되는 것은 아닙니다.
예를 들면, partition key에서 non-equal 관계는 지원되지 않습니다.
또한, 주어진 partition key에서, 클러스터링 칼럼은 인접한 행 세트를 선택할 수 있는 관계로 제한됩니다.

예를 들어, 아래 주어진 테이블에서,

CREATE TABLE posts (
    userid text,
    blog_title text,
    posted_at timestamp,
    entry_title text,
    content text,
    category int,
    PRIMARY KEY (userid, blog_title, posted_at)
)

아래 쿼리는 허용됩니다 :

SELECT entry_title, content FROM posts
 WHERE userid = 'john doe'
   AND blog_title='John''s Blog'
   AND posted_at >= '2012-01-01' AND posted_at < '2012-01-31'

하지만 아래 쿼리는 인접한 행 세트를 선택하지 않았기 때문에 허용되지 않습니다.

// Needs a blog_title to be set to select ranges of posted_at
SELECT entry_title, content FROM posts
 WHERE userid = 'john doe'
   AND posted_at >= '2012-01-01' AND posted_at < '2012-01-31'

관계를 지정할 때, TOKEN 기능은 PARTITION KEY 칼럼에서 쿼리를 할 때 사용될 수 있습니다.
이러한 경우에, 행은 PARTITION KEY에서 해당 값이 아닌 token에 의해서 선택됩니다.
명심해야할 점은 해당 키의 token은 사용되는 partitioner에 의해 정해지고, RandomPartitioner 는 의미있는 순서를 지정하지 않는다는 것입니다.
또한 ordering partitioner는 항상 token값이 아닌 byte에 의해 정렬됩니다.

예를 들어 :

SELECT * FROM posts
 WHERE token(userid) > token('tom') AND token(userid) < token('bob')

또한, IN 관계는 항상 partition key의 마지막 칼럼과 전체 primary key의 마지막 칼럼에만 적용이 가능합니다.
그리고 튜플 표기법을 사용하여 관계에서 CLUSTERING COLUMNS를 "그룹화"하는 것도 가능합니다.

SELECT * FROM posts
 WHERE userid = 'john doe'
   AND (blog_title, posted_at) > ('John''s Blog', '2012-01-01')

위 select 문은 blog_tile 이 "John’s Blog"이고, posted_at 이 ‘2012-01-01’ 다음에 정렬되어 있는 행들을 가져올 것입니다.
하지만, 이때 return되는 결과에는 post_at <= '2012-01-01'에 부합하는 값이 포함될 수 있습니다.

하지만, 아래 케이스에서는 해당 값이 포함되지 않습니다.

SELECT * FROM posts
 WHERE userid = 'john doe'
   AND blog_title > 'John''s Blog'
   AND posted_at > '2012-01-01'

튜플 표기법은 클러스터링 칼럼의 IN 절에도 사용될 수 있습니다.

SELECT * FROM posts
 WHERE userid = 'john doe'
   AND (blog_title, posted_at) IN (('John''s Blog', '2012-01-01'), ('Extreme Chess', '2014-06-01'))

CONTAINS 연산자는 컬렉션 칼럼 (lists, sets, and maps)에서만 사용할 수 있습니다.
Map의 경우, CONTAINS는 map의 키가 아닌 값에만 적용됩니다.
CONTAINS KEY 연산자는 map 칼럼에만 적용이 가능하고, 이것은 map의 key에 적용됩니다.


Grouping results

GROUP BY 옵션은 컬럼 세트에 대해 동일한 값을 공유하는 선택된 모든 행을 단일 행으로 압축합니다.
GROUP BY 옵션을 사용하면 파티션 키 레벨 또는 클러스터링 컬럼 레벨에서 행을 그룹화 할 수 있습니다.
결과적으로, GROUP BY 옵션은 오직 primary key 순서에서 primary key 칼럼 이름만을 사용할 수 있습니다.
만약 primary key 칼럼이 동일하지 않게 제한이 걸려있다면, 해당 key가 group by 절에 있을 필요는 없습니다.
Aggregate 함수는 각각의 group에 따라 값을 생성하기 때문에, 만약 GROUP BY 절이 명시되어 있지 않다면, 모든 행에 대한 단일 값을 생성할 것입니다. 
Aggregate 함수없이 열을 선택하면 GROUP BY가있는 명령문에서 각 그룹의 첫 번째 값이 반환됩니다.


Ordering results 

ORDER BY 절은 리턴되는 결과의 순서를 선택할 수 있게 합니다.
해당 칼럼의 순서와 함께 name을 인수로 받게 됩니다. (ASC,DESC)
현재 가능한 순서는 테이블에 정의 된 클러스터링 순서에 의해 제한됩니다. 

- 테이블이 특정 CLUSTERING ORDER없이 정의 된 경우 허용되는 순서는 클러스터링 컬럼과 그 역순으로 유도 된 순서입니다.

- 그렇지 않은 경우, 허용되는 순서는 CLUSTERING ORDER 옵션의 순서와 반대 순서입니다.


Limiting results

SELECT 문에 대한 LIMIT 옵션은 쿼리에서 반환하는 행 수를 제한하는 반면, PER PARTITION LIMIT 옵션은 쿼리에서 지정된 파티션에 대해 반환되는 행 수를 제한합니다.
두 가지 유형의 제한은 동일한 명령문에서 사용될 수 있습니다.


Allowing filtering

기본적으로 CQL은 서버 측 "filtering"을 포함하지 않는 선택 쿼리 만 허용합니다.
즉, 읽은 모든 레코드가 결과에서 반환됩니다.
이러한 "non filtering"쿼리는 LIMIT를 통해 제어 할 수있는 쿼리에서, 반환하는 데이터 양과 실행 시간은 비례한다는 점에서 예측 가능한 성능을 제공합니다.
ALLOW FILTERING 옵션을 사용하면 필터링이 필요한 (일부) 쿼리를 명시 적으로 허용 할 수 있습니다.
ALLOW FILTERING을 사용하는 쿼리는 예측할 수없는 성능(위의 정의에 해당)을 가질 수 있습니다.
즉, 소수의 레코드를 선택하는 쿼리조차도 클러스터에 저장된 총 데이터 양에 따라 성능이 나타날 수 있습니다.
예를 들어, 생년월일 (보조 색인 포함) 및 거주 국가가있는 사용자 프로필을 보유하고있는 다음 표를 고려해보겠습니다.

CREATE TABLE users (
    username text PRIMARY KEY,
    firstname text,
    lastname text,
    birth_year int,
    country text
)

CREATE INDEX ON users(birth_year);

그러면 다음 쿼리가 유효합니다 :

SELECT * FROM users;
SELECT * FROM users WHERE birth_year = 1981;

왜냐하면 두 경우 모두 카산드라는 이러한 쿼리 성능이 반환되는 데이터의 양에 비례한다는 것을 보장하기 때문입니다.
물론 두 쿼리 모두 실제로 매우 큰 결과를 반환 할 수 있지만 반환되는 데이터의 양은 LIMIT를 추가하여 항상 제어 가능합니다.

하지만, 아래 쿼리는 거부됩니다. :

SELECT * FROM users WHERE birth_year = 1981 AND country = 'FR';

왜냐하면 카산드라는 그 쿼리에 대한 결과가 작더라도 많은 양의 데이터를 스캔 할 필요가 없다는 것을 보장 할 수 없기 때문입니다.
일반적으로, 위와 같은 쿼리에서 프랑스 출신은 소수에 불과한 경우도 1981년에 태어난 사용자의 모든 색인 항목을 검사합니다.
그러나 만약 당신이 하고 있는 작업이 어떠한 것인지를 알고 있다면, ALLOW FILTERING을 사용하여 이 쿼리를 강제로 실행할 수 있으므로 다음 쿼리가 유효합니다.

SELECT * FROM users WHERE birth_year = 1981 AND country = 'FR' ALLOW FILTERING;

출처 : http://cassandra.apache.org/doc/latest/cql/dml.html#select

댓글 없음 :

댓글 쓰기