{"record":{"id":"7a2ffd2ab8be8fdc","repo":"apache/cassandra","slug":"cannot-create-materialized-view-s-without-prima","errorCode":null,"errorMessage":"Cannot create materialized view '%s' without primary key columns %s from base table '%s'","messagePattern":"Cannot create materialized view '(.+?)' without primary key columns (.+?) from base table '(.+?)'","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java","lineNumber":284,"sourceCode":"        });\n\n        // If we give a clustering order, we must explicitly do so for all aliases and in the order of the PK\n        if (!clusteringOrder.isEmpty() && !clusteringColumns.equals(new ArrayList<>(clusteringOrder.keySet())))\n            throw ire(\"Clustering key columns must exactly match columns in CLUSTERING ORDER BY directive\");\n\n        /*\n         * We need to include all of the primary key columns from the base table in order to make sure that we do not\n         * overwrite values in the view. We cannot support \"collapsing\" the base table into a smaller number of rows in\n         * the view because if we need to generate a tombstone, we have no way of knowing which value is currently being\n         * used in the view and whether or not to generate a tombstone. In order to not surprise our users, we require\n         * that they include all of the columns. We provide them with a list of all of the columns left to include.\n         */\n        List<ColumnIdentifier> missingPrimaryKeyColumns =\n            Lists.newArrayList(filter(transform(table.primaryKeyColumns(), c -> c.name), c -> !primaryKeyColumns.contains(c)));\n\n        if (!missingPrimaryKeyColumns.isEmpty())\n        {\n            throw ire(\"Cannot create materialized view '%s' without primary key columns %s from base table '%s'\",\n                      viewName, join(\", \", transform(missingPrimaryKeyColumns, ColumnIdentifier::toString)), tableName);\n        }\n\n        Set<ColumnIdentifier> regularBaseTableColumnsInViewPrimaryKey = new HashSet<>(primaryKeyColumns);\n        transform(table.primaryKeyColumns(), c -> c.name).forEach(regularBaseTableColumnsInViewPrimaryKey::remove);\n        if (regularBaseTableColumnsInViewPrimaryKey.size() > 1)\n        {\n            throw ire(\"Cannot include more than one non-primary key column in materialized view primary key (got %s)\",\n                      join(\", \", transform(regularBaseTableColumnsInViewPrimaryKey, ColumnIdentifier::toString)));\n        }\n\n        /*\n         * Process WHERE clause\n         */\n        if (whereClause.containsTokenRelations())\n            throw new InvalidRequestException(\"Cannot use token relation when defining a materialized view\");\n\n        if (whereClause.containsCustomExpressions())","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java#L266-L302","documentation":"At least one of the view's key columns (partition or clustering) must be a clustering column of the base table; the view must narrow rows differently than the base partitioning. A view whose key adds nothing new (only reusing base partition keys) would be a duplicate partitioning and is rejected.","triggerScenarios":"CREATE MATERIALIZED VIEW on a base table where the view PRIMARY KEY columns are all base partition key columns with no base clustering column among them, on a table that has clustering columns.","commonSituations":"Trying to make a view that just re-partitions by base partition keys; misunderstanding the requirement that MVs index by base clustering columns; crafting a key purely from base partition key columns on a table with clustering columns.","solutions":["Include at least one base clustering column in the view's PRIMARY KEY (typically as partition key of the view)","Remove the view if the base table's key already provides the needed access pattern","Use a separate denormalized table populated by application logic for alternative partitionings the view cannot express"],"exampleFix":"// before\nCREATE MATERIALIZED VIEW mv AS SELECT pk, ck FROM base WHERE pk IS NOT NULL AND ck IS NOT NULL PRIMARY KEY (pk);\n// after\nCREATE MATERIALIZED VIEW mv AS SELECT pk, ck FROM base WHERE pk IS NOT NULL AND ck IS NOT NULL PRIMARY KEY (ck, pk);","handlingStrategy":"validation","validationCode":"boolean hasBaseClustering = viewKeyCols.stream().anyMatch(baseClusteringKeys::contains); if (!hasBaseClustering) throw new IllegalArgumentException(\"MV key must include a base clustering column\");","typeGuard":null,"tryCatchPattern":"try { session.execute(createMvStmt); } catch (InvalidQueryException e) { if (e.getMessage().contains(\"can only be done on tables with clustering columns\")) { /* rework key to include a base clustering column */ } }","preventionTips":["Design MVs to index rows by base clustering columns; they are not for re-partitioning by base partition keys alone","Use denormalized base tables for alternative partitionings"],"tags":["cql","materialized-view","clustering-key"],"backgroundTag":"schema-validation-failed","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}