- PVSM.RU - https://www.pvsm.ru -
Cassandra (далее C*) ограничивает WHERE
запросы из-за своей внутренней структуры. Эта статья вам покажется сложной, запутанной, если вы не читали первую статью [1] из цикла, где я рассказывал как устроена С*. Прочтите её, пожалуйста, прежде чем приступать к этой.
Цель этой статьи — выступать справочником для C* новичков.
В SELECT
запросах Cassandra Query Language (CQL) отсутсвутют привычные нам SQL операции JOIN
, GROUP BY
. А операция WHERE
сильно урезана. В SQL вы можете фильтровать по любой колонке, тогда как в CQL только по распределительным ключам (partition key), кластерным ключам (clustering columns) и вторичным индексам.
Заметка: В С* 2.0 можно создавать вторичные
INDEX
-ы у любой колонки наподобие SQL индексов. Фактически же, вторичные индексы Кассандры — это скрытая от вас дополнительная таблица, поэтому производительностьWHERE
запросов по ним хуже запросов по ключевым колонкам.
ad_click
, созданной в предыдщуей [3], второй по счету, статье. Также там объяснена терминология используемая в этой статье. Тоже рекомендую хотя бы взглянуть.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);
Грубо говоря слово «фильтрация» тут не уместно. Правильнее сказать поиск. C* почти не даёт возможности фильтровать.
Каждый WHERE
запрос говорит Кассандре найти ноду, в которой хранится строка и передать туда запрос.
Так как JOIN
-ов нет, то и сравнивать колонки друг с другом нельзя.
SELECT * from ad_click WHERE
maxTimeuuid(day) = maxTimeuuid(3141592653589); -- ОШИБКА;
Множественные условия 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) ограничено последней колонкой в распрделительном ключе и последней колонкой в кластерном ключе.
Правильно:
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
. Все остальные ограничения сохраняются.
ВАЖНО! 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 не работает?»
Автор: koresar
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/nosql/50210
Ссылки в тексте:
[1] первую статью: http://habrahabr.ru/post/203200/
[2] версии 3: http://cassandra.apache.org/doc/cql3/CQL.html
[3] предыдщуей: http://habrahabr.ru/post/204026/
[4] Пост в блоге Flite Mechanics: http://mechanics.flite.com/blog/2013/11/05/breaking-down-the-cql-where-clause/
[5] Источник: http://habrahabr.ru/post/205176/
Нажмите здесь для печати.