How to Use Metadata Annotations¶
This guide shows how to annotate dimensions, metrics, facts, and tables with comments, synonyms, and access modifiers in a semantic view definition.
Prerequisites:
A working semantic view with
TABLES,DIMENSIONS, andMETRICS(see Multi-Table Semantic Views)Familiarity with DESCRIBE SEMANTIC VIEW output
Add Comments¶
Comments are human-readable descriptions attached to any entry in the view definition. They appear in DESCRIBE SEMANTIC VIEW output and in the comment column of SHOW commands.
View-level comment¶
Set a comment on the semantic view itself using ALTER:
ALTER SEMANTIC VIEW sales SET COMMENT = 'Revenue and order analytics for the North America region';
Remove a view-level comment:
ALTER SEMANTIC VIEW sales UNSET COMMENT;
Tip
View-level comments appear in the comment column of SHOW SEMANTIC VIEWS and as a SEMANTIC_VIEW object kind row in DESCRIBE SEMANTIC VIEW.
Table-level comment¶
Add a COMMENT clause after the table declaration:
CREATE SEMANTIC VIEW sales AS
TABLES (
o AS orders PRIMARY KEY (id) COMMENT = 'Core order transactions',
c AS customers PRIMARY KEY (id) COMMENT = 'Customer master data'
)
DIMENSIONS (o.region AS o.region)
METRICS (o.revenue AS SUM(o.amount));
Add Synonyms¶
Synonyms are alternative names for an entry. They are informational metadata – they do not affect query resolution, but they appear in DESCRIBE and SHOW output for discoverability.
Add WITH SYNONYMS after the expression (or after the COMMENT clause if both are present):
CREATE SEMANTIC VIEW sales AS
TABLES (
o AS orders PRIMARY KEY (id) COMMENT = 'Order data' WITH SYNONYMS = ('transactions', 'purchases')
)
DIMENSIONS (
o.region AS o.region WITH SYNONYMS = ('sales_region', 'territory')
)
METRICS (
o.revenue AS SUM(o.amount) COMMENT = 'Total sales' WITH SYNONYMS = ('total_sales', 'gmv')
);
Synonyms appear as a JSON array in DESCRIBE SEMANTIC VIEW output (e.g., ["sales_region","territory"]) and in the synonyms column of SHOW SEMANTIC DIMENSIONS.
Set Access Modifiers (PRIVATE / PUBLIC)¶
Metrics and facts support PRIVATE and PUBLIC access modifiers. PUBLIC is the default. PRIVATE items cannot be queried directly – they can only be referenced by derived metric expressions.
CREATE SEMANTIC VIEW sales AS
TABLES (
li AS line_items PRIMARY KEY (id)
)
FACTS (
PRIVATE li.raw_margin AS li.price - li.cost
)
METRICS (
li.total_revenue AS SUM(li.price),
PRIVATE li.total_cost AS SUM(li.cost),
li.total_margin AS SUM(li.raw_margin),
profit AS total_revenue - total_cost
);
In this example:
raw_marginis a private fact – it can be referenced by metrics (liketotal_margin) but cannot be queried viafacts := ['raw_margin'].total_costis a private metric – it can be referenced by derived metrics (likeprofit) but cannot be queried viametrics := ['total_cost'].total_marginandprofitare public (default) and use the private items to compute their values.
Warning
PRIVATE is placed before the table alias (PRIVATE li.total_cost), not after the expression. Dimensions do not support access modifiers.
Mark a Named Filter¶
A named filter is a boolean-valued fact or dimension meant to be reused in a query’s pre-aggregation predicate instead of selected as output. Declare one by adding LABELS = (FILTER) after the expression:
CREATE SEMANTIC VIEW sales AS
TABLES (
o AS orders PRIMARY KEY (id)
)
FACTS (
o.amount AS o.amount,
o.is_large AS o.amount > 100 LABELS = (FILTER)
)
DIMENSIONS (
o.is_domestic AS o.country = 'US' LABELS = (FILTER)
)
METRICS (
o.revenue AS SUM(o.amount)
);
Reference the filter by name in where_clause, which applies it before the metrics aggregate:
SELECT * FROM semantic_view('sales', metrics := ['revenue'], where_clause := 'is_domestic AND is_large');
The label is declarative metadata, not an access restriction or a resolution rule:
where_clausealready substitutes any declared fact or dimension name, labelled or not. The label records that the member exists to be filtered on, and surfaces it in DESCRIBE andGET_DDLfor discoverability.A filter is still a queryable member.
dimensions := ['is_domestic']returns its boolean values like any other dimension. To hide a member from queries, usePRIVATE(facts only) instead.
Note
The BOOLEAN requirement is checked by DuckDB’s binder at query time, not at CREATE. Typing an arbitrary SQL expression requires a binder, so a filter over a non-boolean expression is created successfully and raises DuckDB’s own type error the first time it is used in a predicate.
FILTER is the only supported label. Any other value (Snowflake’s tags, for instance) is rejected at CREATE rather than silently dropped, so a definition cannot round-trip having quietly lost a label you wrote.
LABELS is likewise valid only on a fact or a dimension. Writing it on a table, on a metric, or as a view-level annotation is rejected for the same reason: those entries carry no filter flag, so accepting it there would mean discarding it on the way to storage.
Inspect Annotations¶
Via DESCRIBE¶
DESCRIBE SEMANTIC VIEW shows annotation properties as additional rows:
DESCRIBE SEMANTIC VIEW sales;
Look for these property rows:
COMMENT– the comment text (conditional, only when set)SYNONYMS– JSON array of synonyms (conditional, only when set)LABELS–["FILTER"]for a named filter (conditional, only on labelled facts and dimensions)ACCESS_MODIFIER–PUBLICorPRIVATE(always emitted for facts and metrics)NON_ADDITIVE_BY– non-additive dimension list (conditional, only for semi-additive metrics)WINDOW_SPEC– reconstructed OVER clause (conditional, only for window metrics)
Via SHOW commands¶
The SHOW SEMANTIC DIMENSIONS, SHOW SEMANTIC METRICS, and SHOW SEMANTIC FACTS commands include synonyms and comment columns in their output:
SHOW SEMANTIC DIMENSIONS IN sales;
┌───────────────┬─────────────┬────────────────────┬────────────┬────────┬───────────┬──────────────────────────────┬──────────────────────────────────────┐
│ database_name │ schema_name │ semantic_view_name │ table_name │ name │ data_type │ synonyms │ comment │
├───────────────┼─────────────┼────────────────────┼────────────┼────────┼───────────┼──────────────────────────────┼──────────────────────────────────────┤
│ memory │ main │ sales │ orders │ region │ │ ["sales_region","territory"] │ Sales region from shipping address │
└───────────────┴─────────────┴────────────────────┴────────────┴────────┴───────────┴──────────────────────────────┴──────────────────────────────────────┘
Tip
Private items are excluded from SHOW COLUMNS IN SEMANTIC VIEW and from wildcard expansion (alias.*). They only appear in DESCRIBE SEMANTIC VIEW.
Troubleshooting¶
- Comment not appearing in SHOW SEMANTIC VIEWS
Only view-level comments appear in SHOW SEMANTIC VIEWS. Table/dimension/metric/fact comments appear in DESCRIBE SEMANTIC VIEW and in the
commentcolumn of SHOW SEMANTIC DIMENSIONS, SHOW SEMANTIC METRICS, and SHOW SEMANTIC FACTS.- Cannot query a private metric or fact
Private items return an error when queried directly. Use them only in derived metric expressions. To make an item queryable again, recreate the view without the
PRIVATEkeyword.- Synonyms not affecting query resolution
Synonyms are informational metadata only. They do not expand the set of names recognized by semantic_view() or explain_semantic_view(). Use the declared name to query an item.
- COMMENT, WITH SYNONYMS and LABELS order
The annotations on one entry may appear in any order –
o.region AS o.region WITH SYNONYMS = ('territory') COMMENT = 'c'parses the same as the reverse. What the parser does require is that the annotation region be tiled by recognized clauses: once the first annotation keyword is seen, everything after it must be a validCOMMENT/WITH SYNONYMS/LABELSclause separated by whitespace. Leftover text (COMMENT = 'a' banana) or a repeated clause is an error rather than being silently dropped.- Unsupported label
FILTERis the only value accepted inLABELS = (...). Snowflake’s tags and other label values are rejected atCREATE– deliberately, so a definition cannot round-trip having quietly lost a label. Remove the unsupported value to create the view.- LABELS rejected on a table, metric, or the view itself
LABELSapplies only to facts and dimensions – they are the entries that carry the filter flag. On aTABLESorMETRICSentry, or in the trailing view-level annotation position, it raises LABELS is not valid on a …. Move the annotation to the fact or dimension you meant to mark.- A named filter still shows up in query output
Expected.
LABELS = (FILTER)declares intent and drives introspection; it does not hide the member. UsePRIVATE(facts and metrics only) to make an item unqueryable.
Comments on dimensions, metrics, and facts¶
Add
COMMENTafter the expression on any entry: