CQL Reference

Keyspace & Table Creation

-- Create keyspace
CREATE KEYSPACE IF NOT EXISTS shop
  WITH replication = {
    'class': 'NetworkTopologyStrategy',
    'datacenter1': 3
  }
  AND durable_writes = true;

USE shop;

-- Create table with composite primary key
-- (partition_key) = single partition key
-- (partition_key, ...) = composite partition key
CREATE TABLE orders_by_user (
  user_id    UUID,
  order_id   TIMEUUID,
  status     TEXT,
  total      DECIMAL,
  created_at TIMESTAMP,
  PRIMARY KEY (user_id, order_id)
) WITH CLUSTERING ORDER BY (order_id DESC);

-- Composite partition key (distributes across nodes)
CREATE TABLE events_by_day (
  sensor_id TEXT,
  day       DATE,
  event_id  UUID,
  value     DOUBLE,
  PRIMARY KEY ((sensor_id, day), event_id)
);

SELECT & Partition Queries

Cassandra requires the partition key in every query for efficient routing. Clustering columns can be filtered with = or ranges.

-- Query by full partition key (required for performance)
SELECT * FROM orders_by_user WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;

-- Filter on clustering column (range allowed)
SELECT * FROM orders_by_user
WHERE user_id = 550e8400-e29b-41d4-a716-446655440000
  AND order_id > maxTimeuuid('2024-01-01')
  AND order_id < minTimeuuid('2025-01-01');

-- LIMIT results
SELECT * FROM orders_by_user
WHERE user_id = ?
LIMIT 10;

-- Token-based scan (full table scan across partitions)
SELECT * FROM orders_by_user WHERE token(user_id) > token(?);

-- ALLOW FILTERING (use with caution: full cluster scan)
SELECT * FROM orders_by_user WHERE status = 'pending' ALLOW FILTERING;

INSERT, UPDATE, DELETE & TTL

-- INSERT (upsert semantics: no duplicate check)
INSERT INTO orders_by_user (user_id, order_id, status, total)
VALUES (uuid(), now(), 'pending', 99.99)
USING TTL 2592000;    -- expire after 30 days

-- INSERT IF NOT EXISTS (lightweight transaction, avoid for high throughput)
INSERT INTO users (id, email) VALUES (uuid(), '[email protected]')
IF NOT EXISTS;

-- UPDATE (upsert: creates row if not exists)
UPDATE orders_by_user
USING TTL 86400
SET status = 'shipped'
WHERE user_id = ? AND order_id = ?;

-- Conditional update (LWT)
UPDATE users SET email = '[email protected]'
WHERE id = ?
IF email = '[email protected]';

-- DELETE specific columns
DELETE status FROM orders_by_user WHERE user_id = ? AND order_id = ?;

-- DELETE entire row
DELETE FROM orders_by_user WHERE user_id = ? AND order_id = ?;

Collection Types

-- List, Set, Map columns
CREATE TABLE user_profiles (
  user_id UUID PRIMARY KEY,
  tags    SET<TEXT>,
  history LIST<TEXT>,
  metadata MAP<TEXT, TEXT>
);

-- Append to list
UPDATE user_profiles SET history = history + ['login'] WHERE user_id = ?;

-- Add to set
UPDATE user_profiles SET tags = tags + {'premium', 'verified'} WHERE user_id = ?;

-- Remove from set
UPDATE user_profiles SET tags = tags - {'unverified'} WHERE user_id = ?;

-- Update map entry
UPDATE user_profiles SET metadata['theme'] = 'dark' WHERE user_id = ?;

-- Frozen collections (can be used in primary keys)
CREATE TABLE articles (
  id      UUID PRIMARY KEY,
  authors FROZEN<LIST<TEXT>>
);

Secondary Indexes & SASI

-- Regular secondary index (on low-cardinality columns)
CREATE INDEX idx_orders_status ON orders_by_user (status);

-- SASI (SSTable Attached Secondary Index): supports LIKE and range queries
CREATE CUSTOM INDEX sasi_orders_status ON orders_by_user (status)
  USING 'org.apache.cassandra.index.sasi.SASIIndex'
  WITH OPTIONS = {'mode': 'CONTAINS', 'case_sensitive': 'false'};

-- Query using SASI index
SELECT * FROM orders_by_user WHERE status LIKE '%pend%' ALLOW FILTERING;

-- Drop index
DROP INDEX IF EXISTS shop.idx_orders_status;

-- Materialized views (maintained by Cassandra, limited flexibility)
CREATE MATERIALIZED VIEW orders_by_status AS
  SELECT * FROM orders_by_user
  WHERE user_id IS NOT NULL AND order_id IS NOT NULL AND status IS NOT NULL
  PRIMARY KEY (status, user_id, order_id);