Migrate from Snowflake Semantic Views

Convert your Snowflake semantic views into governed Malloy data models

Snowflake semantic views and Malloy are close cousins — both are dimension, measure, and relationship graphs over SQL tables. Credible reads your semantic view definition, maps it almost concept-for-concept to Malloy, and — where you connect it — validates against Snowflake's own verified queries.

Still weighing the move? Credible vs. Snowflake Cortex sets the two side by side.

What Credible Reads

Your native SEMANTIC VIEW objects — logical tables, relationships, facts, dimensions, and metrics — or any legacy Cortex Analyst YAML models. The agent works from the exported definition (the CREATE SEMANTIC VIEW DDL or the YAML). Connecting to Snowflake — via SQL (DESCRIBE SEMANTIC VIEW, SHOW SEMANTIC VIEWS) or the Snowflake-managed MCP server (Cortex) — is optional: it lets the agent read views in place and run parity queries under Snowflake's own RBAC.

What Comes Across

The everyday modeling carries over. The bigger pieces land like this:

In SnowflakeIn Credible
Semantic view: logical tables & relationshipsMalloy sources and joins
Dimensions, facts, metricsDimensions, row-level facts, and measures — facts feed measures, so you can re-aggregate at any grain
WITH SYNONYMS / COMMENT#(doc) / #(index), compressed into the engine's concept index
Verified queriesParity fixtures and named views
PUBLIC / PRIVATE, RBACFine-grained access control in the model, versioned and enforced at the gateway on every query
Cortex Analyst / BI on topEvery surface — agents over MCP, data apps, notebooks, dashboards, APIs

The Migration Flow

Credible reads the semantic view definition, translates logical tables and relationships to sources and joins (facts to row-level dimensions, metrics to measures), enriches with #(doc)/#(index) tags seeded from synonyms and comments, and — where you connect it — validates against Snowflake.

What Credible Handles

  • Two coexisting formats — a deployment may use native SEMANTIC VIEW objects, legacy Cortex Analyst YAML on a stage, or both. Credible detects which and reads each accordingly.
  • Facts vs. metrics — a fact is a row-level expression; a metric is its aggregation. Credible keeps them distinct (fact → row-level dimension, metric → measure) so you can still re-aggregate at different grains. Collapsing them would lose that.
  • Verified queries are the best thing to validate against — Credible replays each question→SQL pair and diffs the result against the migrated Malloy. (Verified SQL references logical names, not physical tables.)

Before & After

A CREATE SEMANTIC VIEW statement:

CREATE OR REPLACE SEMANTIC VIEW sales.sales_analytics
  TABLES (
    orders AS sales.orders PRIMARY KEY (order_id)
      WITH SYNONYMS = ('purchase_orders')
      COMMENT = 'Order transactions at the order grain',
    customers AS sales.customers PRIMARY KEY (customer_id)
  )
  RELATIONSHIPS (
    orders_to_customers AS orders (customer_id) REFERENCES customers (customer_id)
  )
  FACTS (
    orders.line_amount AS orders.amount COMMENT = 'Per-row order amount in USD',
    PRIVATE orders.is_shipped AS CASE WHEN orders.status = 'shipped' THEN 1 ELSE 0 END
  )
  DIMENSIONS (
    orders.order_date AS CAST(orders.created_at AS DATE)
      COMMENT = 'Date the order was placed',
    customers.region AS customers.geographic_region
  )
  METRICS (
    orders.total_revenue AS SUM(orders.line_amount) COMMENT = 'Total revenue in USD',
    orders.shipped_revenue AS SUM(orders.line_amount * orders.is_shipped),
    orders.shipped_revenue_pct AS
      SUM(orders.line_amount * orders.is_shipped) / SUM(orders.line_amount)
  );
#(doc) Order transactions at the order grain. Also known as: purchase_orders
source: orders is conn.table('sales.orders') extend {
  primary_key: order_id
  join_one: customers is conn.table('sales.customers') on customer_id

  dimension:
    #(doc) Date the order was placed
    order_date is created_at::date

    #(doc) Per-row order amount in USD (fact)
    line_amount is amount

    #(doc) Row-level shipped flag (fact)
    is_shipped is pick 1 when status = 'shipped' else 0

  measure:
    #(doc) Total revenue in USD
    # currency
    total_revenue is sum(line_amount)

    #(doc) Revenue from shipped orders
    # currency
    shipped_revenue is sum(line_amount) { where: status = 'shipped' }

    #(doc) Share of revenue that shipped
    # percent
    shipped_revenue_pct is shipped_revenue / total_revenue
}

FACTS become row-level dimensions that feed measures; METRICS become measures. WITH SYNONYMS and COMMENT fold into #(doc) (Malloy has no synonym primitive, so alternate names go in the description for retrieval), and PRIVATE facts map to private: fields so they feed measures without being independently queryable. Don't map them to internal:, which a query can re-expose with extend.

More than a reformat. Semantic views map almost 1:1, so the gain is what surrounds them — composable follow-up queries, AI Analytics Engine discovery, and one model that reaches agents, apps, and BI beyond Cortex. See what you gain →

Next Steps

On this page