Cassandra CQL Quickstart Guide
Learn Cassandra Query Language (CQL) fundamentals in this hands-on tutorial. CQL is similar to SQL but designed for Cassandra's distributed architecture.
Prerequisites
Section titled “Prerequisites”- Cassandra running (see Installation Guide)
- Access to
cqlshor CQLAI
Connecting to Cassandra
Section titled “Connecting to Cassandra”Using cqlsh
Section titled “Using cqlsh”# Connect to local Cassandracqlsh
# Connect to remote hostcqlsh 192.168.1.10 9042
# Connect with authenticationcqlsh -u cassandra -p cassandra
# Connect with SSLcqlsh --sslUsing CQLAI (Recommended)
Section titled “Using CQLAI (Recommended)”# Connect with CQLAI (modern, AI-powered shell)cqlai
# Connect with specific hostcqlai --host 192.168.1.10Creating a Keyspace
Section titled “Creating a Keyspace”A keyspace is the top-level container for data (similar to a database in SQL).
Create a Keyspace
Section titled “Create a Keyspace”-- For development (single node)CREATE KEYSPACE IF NOT EXISTS my_appWITH replication = { 'class': 'SimpleStrategy', 'replication_factor': 1};
-- For production (multi-datacenter)CREATE KEYSPACE IF NOT EXISTS my_appWITH replication = { 'class': 'NetworkTopologyStrategy', 'dc1': 3, 'dc2': 3};Use the Keyspace
Section titled “Use the Keyspace”USE my_app;View Keyspaces
Section titled “View Keyspaces”-- List all keyspacesDESCRIBE KEYSPACES;
-- Show keyspace detailsDESCRIBE KEYSPACE my_app;Creating Tables
Section titled “Creating Tables”Basic Table
Section titled “Basic Table”CREATE TABLE users ( user_id UUID PRIMARY KEY, username TEXT, email TEXT, created_at TIMESTAMP);Compound Primary Key
Section titled “Compound Primary Key”-- Partition key: user_id-- Clustering column: message_time (sorted DESC)CREATE TABLE user_messages ( user_id UUID, message_time TIMESTAMP, message_id UUID, content TEXT, PRIMARY KEY ((user_id), message_time, message_id)) WITH CLUSTERING ORDER BY (message_time DESC);Composite Partition Key
Section titled “Composite Partition Key”-- Composite partition key: (tenant_id, year_month)CREATE TABLE events ( tenant_id TEXT, year_month TEXT, event_time TIMESTAMP, event_id UUID, event_type TEXT, data TEXT, PRIMARY KEY ((tenant_id, year_month), event_time, event_id)) WITH CLUSTERING ORDER BY (event_time DESC);View Table Structure
Section titled “View Table Structure”-- Show table schemaDESCRIBE TABLE users;
-- List all tables in keyspaceDESCRIBE TABLES;Inserting Data
Section titled “Inserting Data”Basic Insert
Section titled “Basic Insert”INSERT INTO users (user_id, username, email, created_at)VALUES (uuid(), 'john_doe', 'john@example.com', toTimestamp(now()));Insert with Specific UUID
Section titled “Insert with Specific UUID”INSERT INTO users (user_id, username, email, created_at)VALUES ( 550e8400-e29b-41d4-a716-446655440000, 'jane_smith', 'jane@example.com', '2024-01-15 10:30:00');Insert with TTL (Time To Live)
Section titled “Insert with TTL (Time To Live)”-- Data expires after 86400 seconds (24 hours)INSERT INTO users (user_id, username, email, created_at)VALUES (uuid(), 'temp_user', 'temp@example.com', toTimestamp(now()))USING TTL 86400;Insert If Not Exists (Lightweight Transaction)
Section titled “Insert If Not Exists (Lightweight Transaction)”INSERT INTO users (user_id, username, email, created_at)VALUES (uuid(), 'unique_user', 'unique@example.com', toTimestamp(now()))IF NOT EXISTS;Batch Insert
Section titled “Batch Insert”BEGIN BATCH INSERT INTO users (user_id, username, email, created_at) VALUES (uuid(), 'user1', 'user1@example.com', toTimestamp(now()));
INSERT INTO users (user_id, username, email, created_at) VALUES (uuid(), 'user2', 'user2@example.com', toTimestamp(now()));
INSERT INTO users (user_id, username, email, created_at) VALUES (uuid(), 'user3', 'user3@example.com', toTimestamp(now()));APPLY BATCH;Querying Data
Section titled “Querying Data”Select All
Section titled “Select All”SELECT * FROM users;Select Specific Columns
Section titled “Select Specific Columns”SELECT username, email FROM users;Query by Primary Key
Section titled “Query by Primary Key”-- By partition key (efficient)SELECT * FROM users WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Query with Clustering Columns
Section titled “Query with Clustering Columns”-- Get messages for a user, newest firstSELECT * FROM user_messagesWHERE user_id = 550e8400-e29b-41d4-a716-446655440000;
-- Get messages in time rangeSELECT * FROM user_messagesWHERE user_id = 550e8400-e29b-41d4-a716-446655440000 AND message_time >= '2024-01-01' AND message_time < '2024-02-01';
-- Limit resultsSELECT * FROM user_messagesWHERE user_id = 550e8400-e29b-41d4-a716-446655440000LIMIT 10;Query with ALLOW FILTERING (Use Sparingly!)
Section titled “Query with ALLOW FILTERING (Use Sparingly!)”-- WARNING: Scans entire table - avoid in productionSELECT * FROM users WHERE email = 'john@example.com' ALLOW FILTERING;Warning:
ALLOW FILTERINGperforms a full table scan. Only use for development or very small tables. For production, use proper data modeling or secondary indexes.
Using IN Clause
Section titled “Using IN Clause”-- Query multiple partition keysSELECT * FROM usersWHERE user_id IN ( 550e8400-e29b-41d4-a716-446655440000, 660e8400-e29b-41d4-a716-446655440001);IN Clause Behavior
The IN clause behaves differently on partition keys versus clustering columns. With composite partition keys, separate IN clauses create a cartesian product of all combinations. Multi-column tuple syntax such as (pk1, pk2) IN (...) is not supported on partition keys but is supported on clustering columns.
For detailed examples and the complete behavior reference, see Multi-Partition Queries.
Updating Data
Section titled “Updating Data”Basic Update
Section titled “Basic Update”UPDATE usersSET email = 'newemail@example.com'WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Update Multiple Columns
Section titled “Update Multiple Columns”UPDATE usersSET email = 'updated@example.com', username = 'updated_username'WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Update with TTL
Section titled “Update with TTL”UPDATE users USING TTL 3600SET email = 'temp@example.com'WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Conditional Update (Lightweight Transaction)
Section titled “Conditional Update (Lightweight Transaction)”UPDATE usersSET email = 'new@example.com'WHERE user_id = 550e8400-e29b-41d4-a716-446655440000IF email = 'old@example.com';Deleting Data
Section titled “Deleting Data”Delete a Row
Section titled “Delete a Row”DELETE FROM usersWHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Delete Specific Columns
Section titled “Delete Specific Columns”DELETE email FROM usersWHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Delete with Condition
Section titled “Delete with Condition”DELETE FROM usersWHERE user_id = 550e8400-e29b-41d4-a716-446655440000IF EXISTS;Truncate Table
Section titled “Truncate Table”-- Delete all data from tableTRUNCATE users;Working with Collections
Section titled “Working with Collections”List Type
Section titled “List Type”CREATE TABLE user_hobbies ( user_id UUID PRIMARY KEY, hobbies LIST<TEXT>);
-- Insert listINSERT INTO user_hobbies (user_id, hobbies)VALUES (uuid(), ['reading', 'gaming', 'hiking']);
-- Append to listUPDATE user_hobbiesSET hobbies = hobbies + ['cooking']WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;
-- Prepend to listUPDATE user_hobbiesSET hobbies = ['swimming'] + hobbiesWHERE user_id = 550e8400-e29b-41d4-a716-446655440000;
-- Remove from listUPDATE user_hobbiesSET hobbies = hobbies - ['gaming']WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Set Type
Section titled “Set Type”CREATE TABLE user_tags ( user_id UUID PRIMARY KEY, tags SET<TEXT>);
-- Insert setINSERT INTO user_tags (user_id, tags)VALUES (uuid(), {'premium', 'verified', 'active'});
-- Add to setUPDATE user_tagsSET tags = tags + {'vip'}WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;
-- Remove from setUPDATE user_tagsSET tags = tags - {'active'}WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Map Type
Section titled “Map Type”CREATE TABLE user_preferences ( user_id UUID PRIMARY KEY, preferences MAP<TEXT, TEXT>);
-- Insert mapINSERT INTO user_preferences (user_id, preferences)VALUES (uuid(), {'theme': 'dark', 'language': 'en', 'timezone': 'UTC'});
-- Update map entriesUPDATE user_preferencesSET preferences['theme'] = 'light'WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;
-- Add new map entryUPDATE user_preferencesSET preferences = preferences + {'notifications': 'enabled'}WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;
-- Remove map entryDELETE preferences['timezone'] FROM user_preferencesWHERE user_id = 550e8400-e29b-41d4-a716-446655440000;User-Defined Types (UDT)
Section titled “User-Defined Types (UDT)”-- Create a UDTCREATE TYPE address ( street TEXT, city TEXT, state TEXT, zip_code TEXT, country TEXT);
-- Use UDT in tableCREATE TABLE customers ( customer_id UUID PRIMARY KEY, name TEXT, billing_address FROZEN<address>, shipping_address FROZEN<address>);
-- Insert with UDTINSERT INTO customers (customer_id, name, billing_address, shipping_address)VALUES ( uuid(), 'John Doe', {street: '123 Main St', city: 'New York', state: 'NY', zip_code: '10001', country: 'USA'}, {street: '456 Oak Ave', city: 'Boston', state: 'MA', zip_code: '02101', country: 'USA'});
-- Query UDT fieldsSELECT name, billing_address.city, shipping_address.city FROM customers;Secondary Indexes
Section titled “Secondary Indexes”Create Secondary Index
Section titled “Create Secondary Index”-- Index on regular columnCREATE INDEX ON users (email);
-- Named indexCREATE INDEX users_email_idx ON users (email);
-- Query using indexSELECT * FROM users WHERE email = 'john@example.com';Storage-Attached Index (SAI) - Cassandra 5.0+
Section titled “Storage-Attached Index (SAI) - Cassandra 5.0+”-- Create SAI index (more efficient)CREATE INDEX ON users (email) USING 'sai';
-- SAI with optionsCREATE INDEX ON users (username) USING 'sai'WITH OPTIONS = {'case_sensitive': 'false'};Aggregate Functions
Section titled “Aggregate Functions”-- Count rowsSELECT COUNT(*) FROM users;
-- Min/Max (requires primary key or index)SELECT MIN(created_at), MAX(created_at) FROM user_messagesWHERE user_id = 550e8400-e29b-41d4-a716-446655440000;
-- Sum and AverageCREATE TABLE sales ( product_id UUID, sale_date DATE, amount DECIMAL, PRIMARY KEY ((product_id), sale_date));
SELECT SUM(amount), AVG(amount) FROM salesWHERE product_id = 550e8400-e29b-41d4-a716-446655440000;Useful Functions
Section titled “Useful Functions”UUID Functions
Section titled “UUID Functions”-- Generate random UUIDSELECT uuid();
-- Generate time-based UUIDSELECT now(); -- Returns timeuuid
-- Convert timeuuid to timestampSELECT toTimestamp(now());
-- Extract date from timeuuidSELECT toDate(now());Timestamp Functions
Section titled “Timestamp Functions”-- Current timestampSELECT toTimestamp(now());
-- Date from timestampSELECT toDate(toTimestamp(now()));
-- Specific timestampINSERT INTO users (user_id, username, email, created_at)VALUES (uuid(), 'user', 'user@example.com', '2024-01-15 14:30:00+0000');Token Function
Section titled “Token Function”-- Get token value for partition keySELECT token(user_id), * FROM users;
-- Query token range (useful for debugging)SELECT * FROM users WHERE token(user_id) > -9223372036854775808;TTL and WriteTime
Section titled “TTL and WriteTime”-- Check TTL remainingSELECT TTL(email) FROM users WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;
-- Check write timestampSELECT WRITETIME(email) FROM users WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Consistency Levels
Section titled “Consistency Levels”-- Set consistency for sessionCONSISTENCY QUORUM;
-- Check current consistencyCONSISTENCY;
-- Common consistency levels:-- ONE - Fastest, least consistent-- QUORUM - Balanced (majority of replicas)-- LOCAL_QUORUM - Majority in local datacenter-- ALL - Slowest, most consistentConsistency Level Reference
Section titled “Consistency Level Reference”| Level | Description | Use Case |
|---|---|---|
ONE | One replica responds | High throughput reads |
TWO | Two replicas respond | Improved consistency |
THREE | Three replicas respond | High consistency |
QUORUM | Majority responds | Default for most apps |
LOCAL_QUORUM | Majority in local DC | Multi-DC deployments |
EACH_QUORUM | Quorum in each DC | Strong multi-DC consistency |
ALL | All replicas respond | Highest consistency |
ANY | Any node (including hints) | Highest availability writes |
LOCAL_ONE | One replica in local DC | Low-latency local reads |
Tracing Queries
Section titled “Tracing Queries”-- Enable tracingTRACING ON;
-- Run query (trace shown automatically)SELECT * FROM users WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;
-- Disable tracingTRACING OFF;Practical Examples
Section titled “Practical Examples”Example 1: User Profile System
Section titled “Example 1: User Profile System”-- Create keyspaceCREATE KEYSPACE social_app WITH replication = { 'class': 'NetworkTopologyStrategy', 'dc1': 3};
USE social_app;
-- User profiles tableCREATE TABLE user_profiles ( user_id UUID, username TEXT, display_name TEXT, bio TEXT, avatar_url TEXT, follower_count COUNTER, created_at TIMESTAMP, PRIMARY KEY (user_id));
-- Separate counter table (counters need separate table)CREATE TABLE user_counters ( user_id UUID PRIMARY KEY, followers COUNTER, following COUNTER, posts COUNTER);
-- Update countersUPDATE user_counters SET followers = followers + 1WHERE user_id = 550e8400-e29b-41d4-a716-446655440000;Example 2: Time-Series IoT Data
Section titled “Example 2: Time-Series IoT Data”-- IoT sensor readingsCREATE TABLE sensor_readings ( sensor_id TEXT, date DATE, reading_time TIMESTAMP, temperature DOUBLE, humidity DOUBLE, pressure DOUBLE, PRIMARY KEY ((sensor_id, date), reading_time)) WITH CLUSTERING ORDER BY (reading_time DESC);
-- Insert readingINSERT INTO sensor_readings (sensor_id, date, reading_time, temperature, humidity, pressure)VALUES ('sensor-001', '2024-01-15', toTimestamp(now()), 23.5, 65.2, 1013.25);
-- Get today's readings for a sensorSELECT * FROM sensor_readingsWHERE sensor_id = 'sensor-001' AND date = '2024-01-15'LIMIT 100;
-- Get readings in time rangeSELECT * FROM sensor_readingsWHERE sensor_id = 'sensor-001' AND date = '2024-01-15' AND reading_time >= '2024-01-15 08:00:00' AND reading_time < '2024-01-15 17:00:00';Example 3: E-commerce Orders
Section titled “Example 3: E-commerce Orders”-- Orders by customerCREATE TABLE orders_by_customer ( customer_id UUID, order_date DATE, order_id UUID, status TEXT, total DECIMAL, items LIST<FROZEN<MAP<TEXT, TEXT>>>, PRIMARY KEY ((customer_id), order_date, order_id)) WITH CLUSTERING ORDER BY (order_date DESC, order_id ASC);
-- Insert orderINSERT INTO orders_by_customer (customer_id, order_date, order_id, status, total, items)VALUES ( 550e8400-e29b-41d4-a716-446655440000, '2024-01-15', uuid(), 'pending', 99.99, [ {'product_id': 'prod-001', 'name': 'Widget', 'quantity': '2', 'price': '49.99'}, {'product_id': 'prod-002', 'name': 'Gadget', 'quantity': '1', 'price': '0.01'} ]);
-- Get customer's recent ordersSELECT * FROM orders_by_customerWHERE customer_id = 550e8400-e29b-41d4-a716-446655440000LIMIT 10;Common Mistakes to Avoid
Section titled “Common Mistakes to Avoid”1. Using ALLOW FILTERING in Production
Section titled “1. Using ALLOW FILTERING in Production”-- BAD: Full table scanSELECT * FROM users WHERE email = 'john@example.com' ALLOW FILTERING;
-- GOOD: Create an index or denormalizeCREATE INDEX ON users (email);SELECT * FROM users WHERE email = 'john@example.com';2. Large Partitions
Section titled “2. Large Partitions”-- BAD: All orders in one partition (unbounded growth)CREATE TABLE orders ( order_id UUID PRIMARY KEY, customer_id UUID, ...);
-- GOOD: Partition by customer with time bucketingCREATE TABLE orders_by_customer ( customer_id UUID, year_month TEXT, -- e.g., '2024-01' order_id UUID, ... PRIMARY KEY ((customer_id, year_month), order_id));3. Using Collections for Large Data
Section titled “3. Using Collections for Large Data”-- BAD: Collections have 64KB limit per elementCREATE TABLE user_posts ( user_id UUID PRIMARY KEY, posts LIST<TEXT> -- Will fail with many/large posts);
-- GOOD: Use a separate tableCREATE TABLE posts_by_user ( user_id UUID, post_time TIMESTAMP, post_id UUID, content TEXT, PRIMARY KEY ((user_id), post_time, post_id));Next Steps
Section titled “Next Steps”After learning CQL basics:
- Data Modeling Guide - Design effective schemas
- First Cluster Setup - Multi-node deployment
- CQL Reference - Complete language reference
- Install CQLAI - Modern CQL shell with AI