SELECT…WHERE запросы в Cassandra 2.0 на CQL3

от автора

Cassandra (далее C*) ограничивает WHERE запросы из-за своей внутренней структуры. Эта статья вам покажется сложной, запутанной, если вы не читали первую статью из цикла, где я рассказывал как устроена С*. Прочтите её, пожалуйста, прежде чем приступать к этой.

Цель этой статьи — выступать справочником для C* новичков.

Некоторые отличия CQL от SQL

В SELECT запросах Cassandra Query Language (CQL) отсутсвутют привычные нам SQL операции JOIN, GROUP BY. А операция WHERE сильно урезана. В SQL вы можете фильтровать по любой колонке, тогда как в CQL только по распределительным ключам (partition key), кластерным ключам (clustering columns) и вторичным индексам.

Заметка: В С* 2.0 можно создавать вторичные INDEX-ы у любой колонки наподобие SQL индексов. Фактически же, вторичные индексы Кассандры — это скрытая от вас дополнительная таблица, поэтому производительность WHERE запросов по ним хуже запросов по ключевым колонкам.

Disclaimer

  • CQL видоизменяется от релиза к релизу. Данная статья отображает состояние версии 3. Писалась когда последняя версия была 3.1.2.
  • Все примеры производятся над таблицей ad_click, созданной в предыдщуей, второй по счету, статье. Также там объяснена терминология используемая в этой статье. Тоже рекомендую хотя бы взглянуть.
CREATE TABLE ad_click (   reseller_id text,   day text, -- day in the format of 'YYYY-MM-DD'   time timestamp,   ad_id text,   amount float,   PRIMARY KEY ((reseller_id, day), time, ad_id) -- распределительный ключ (reseller_id,day) и кластерные ключи (time,ad_id) ) WITH CLUSTERING ORDER BY (time DESC); 

Фильтрация данных используя WHERE

Грубо говоря слово «фильтрация» тут не уместно. Правильнее сказать поиск. C* почти не даёт возможности фильтровать.

Каждый WHERE запрос говорит Кассандре найти ноду, в которой хранится строка и передать туда запрос.

Сравнение колонок друг с другом

Так как JOIN-ов нет, то и сравнивать колонки друг с другом нельзя.

SELECT * from ad_click WHERE  maxTimeuuid(day) = maxTimeuuid(3141592653589); -- ОШИБКА; 
AND, OR

Множественные условия WHERE запросов нельзя объединять оператором OR, работают только AND. И всего несколько операторов сравнения работает, да и то не всегда.

Равенство =

Использование операторов равенства (=) почти не ограничено. Оно ограничено колонками-ключами и колонками-индексами. А также нужно обязательно сравнить все предыдущие колонки-ключи, прежде чем сравнить следующий.

Правильно:

SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND -- OK day = '2013-11-29' AND -- OK time = 3141592653589 AND -- OK ad_id = '890_567_234'; -- OK 

Неправильно:

SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND  day = '2013-11-29' AND  amount = 0; -- ОШИБКА! Это не ключевая колонка. SELECT * FROM ad_click WHERE  day = '2013-11-29' AND -- ОШИБКА! Забыли сравнить колонку reseller_id. time = 3141592653589 AND  ad_id = '890_567_234'; SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND  day = '2013-11-29' AND  ad_id = '890_567_234'; -- ОШИБКА! Забыли сравнить колонку time. 
Включение IN

Использование оператора включения (IN) ограничено последней колонкой в распрделительном ключе и последней колонкой в кластерном ключе.

Правильно:

SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND  day IN ('2013-11-28', '2013-11-29') -- OK AND time = 3141592653589  AND ad_id IN ('890_567_234', '890_567_010'); -- OK 

Неправильно:

SELECT * FROM ad_click WHERE  reseller_id IN ('supaboobs') AND -- ОШИБКА! Это не последняя колонка распределительного ключа. day = '2013-11-28' AND  time = 3141592653589 AND ad_id = '890_567_234'; SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND day = '2013-11-28' AND  time IN (3141592653589) AND  -- ОШИБКА! Это не последняя колонка кластерного ключа. ad_id = ('890_567_234'); 
Синтаксис сравнений

Имя колонки должно быть слева от оператора сравнения, значение — справа.
Правильно:

SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND -- OK day = '2013-11-29'; -- OK 

Неправильно:

SELECT * FROM ad_click WHERE  'supaboobs' = reseller_id AND  -- ОШИБКА! Нарушен порядок оператора сравнения. '2013-11-29' = day AND, -- ОШИБКА! Нарушен порядок оператора сравнения. AND time < 3141592653589;  -- ОШИБКА! Нарушен порядок оператора сравнения. 
Оперторы сравнения = > >= < <=

Их можно использовать в последней колонке вашего CQL запроса, причём колонка должна быть исключительно кластерной.

Правильно:

SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND day = '2013-11-29' AND time >= 3141592653589; -- OK SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND day = '2013-11-29' AND time = 3141592653589 AND ad_id > '890_567_234'; -- OK 

Неправильно:

SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND day = '2013-11-29' AND time >= 3141592653589 AND  -- ОШИБКА! Это не последняя колонка запроса. ad_id = '890_567_234'; SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND day = '2013-11-29' AND time >= 3141592653589 AND ad_id < '890_567_234';  -- ОШИБКА! Предыдущий не фильтровался операцией равенство. SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND day = '2013-11-29' AND ad_id < '890_567_234';  -- ОШИБКА! Пропущена фильтрация по колонке time. SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND day < '2013-11-29'; -- ОШИБКА! Это не кластерная колонка. 
Обходной манёвр — ALLOW FILTERING

Можно не указывать распределительный ключ, а оставить только кластерный, поставив в конец запроса ALLOW FILTERING. Все остальные ограничения сохраняются.

ВАЖНО! Timeout в этом случае очень вероятен, потому что вы пробегаемся по всем нодам, по всем строкам.

Правильно:

SELECT * FROM ad_click WHERE  time = 3141592653589 AND -- OK ad_id > '890_567_234' -- OK ALLOW FILTERING; SELECT * FROM ad_click WHERE  time >= 3141592653589 AND -- OK time <= 3141592653589 -- OK ALLOW FILTERING; 

Неправильно:

SELECT * FROM ad_click WHERE  time >= 3141592653589 AND ad_id > '890_567_234' -- ОШИБКА! Нельзя больше двух сравнений. ALLOW FILTERING; SELECT * FROM ad_click WHERE  time >= 3141592653589 AND time <= 3241592653589 AND ad_id = '890_567_234'  -- ОШИБКА! Предыдущий не фильтровался операцией равенство. ALLOW FILTERING; 
Вторичные индексы

Вторичные индексы поддерживают исключительно оператор равенства, и всё. Запросы по вторичным индексам можно делать как с указанием других ключей, так и без.

Чтобы создать вторичный индекс следует выполнить комманду:

CREATE INDEX on ad_click (amount); 

Правильно:

SELECT * FROM ad_click WHERE  amount = 0.0075; -- OK SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND day = '2013-11-29' AND time = 3141592653589 AND ad_id = '890_567_234' AND amount = 0.0075; -- OK 

Неправильно:

SELECT * FROM ad_click WHERE  reseller_id = 'supaboobs' AND day = '2013-11-29' AND time = 3141592653589 AND ad_id = '890_567_234' AND amount > 0.0; -- ОШИБКА! Для вторичных индексов разрешен только оператор равенства. 

Заключение

Надеюсь у вас больше не возникнет вопросов «почему мой SELECT не работает?»

Источники

Предыдущая статья цикла.

ссылка на оригинал статьи http://habrahabr.ru/post/205176/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *