Omni → Snowflake Semantic View
Convertit un topic Omni en une définition YAML de Snowflake Semantic View en explorant d'abord le modèle Omni via API, puis en traduisant ses définitions au format Snowflake Semantic View.
Prérequis
# Vérifiez que l'Omni CLI est installé — sinon, demandez à l'utilisateur de l'installer
# Voir : https://github.com/exploreomni/cli#readme
command -v omni >/dev/null || echo "ERROR: Omni CLI is not installed."
# Affiche les profils disponibles et en sélectionne un
omni config show
# S'il existe plusieurs profils, demandez à l'utilisateur lequel utiliser, puis basculez :
omni config use <profile-name>
Conseil : Utilisez
-o jsonpour forcer la sortie structurée en analyse programmatique, ou-o humanpour des tableaux lisibles. La valeur par défaut estauto(lisible en TTY, JSON quand redirigé).
Détection de l'environnement d'exécution
Avant de commencer, déterminez l'environnement d'exécution — cela contrôle la manière dont le SQL est exécuté sur Snowflake à l'étape 7.
Comment détecter
Vérifiez la présence de la capacité native d'exécution de SQL Snowflake :
- Cortex Code (Snowflake Notebooks / Cortex Analyst) : l'exécution SQL native est disponible. Aucun outil supplémentaire requis.
- Claude Code, Cursor, ou tout IDE/agent externe : pas de connexion Snowflake native. Vous devez utiliser l'une des options CLI ci-dessous.
Heuristique de détection
Si l'agent peut exécuter une instruction SQL comme `SELECT CURRENT_USER()` directement sans aucune commande shell → vous êtes dans Cortex Code.
Sinon → vous êtes dans un environnement externe.
En cas de doute, demandez à l'utilisateur : « Exécutez-vous cela dans Snowflake (Cortex Code / Notebooks), ou dans un outil externe comme Claude Code ou Cursor ? »
Environnement externe : options de connectivité Snowflake
Pour Claude Code, Cursor, ou tout agent basé sur terminal, choisissez l'une des options suivantes. Vérifiez la disponibilité dans cet ordre :
Option A — Snowflake CLI (snow) ✅ Recommandé
# Vérifiez la disponibilité
command -v snow
# Configurez une connexion (première fois)
snow connection add
# Exécutez le SQL
snow sql -q "SELECT CURRENT_USER();" --connection <connection_name>
Définissez une connexion par défaut pour éviter de répéter --connection à chaque appel :
snow connection set-default <connection_name>
Option B — SnowSQL (CLI classique)
# Vérifiez la disponibilité
command -v snowsql
# Exécutez le SQL
snowsql -a <account> -u <user> -q "SELECT CURRENT_USER();"
Option C — Python (snowflake-connector-python)
pip install snowflake-connector-python
import snowflake.connector
conn = snowflake.connector.connect(
account="<account>",
user="<user>",
password="<password>", # ou utilisez key-pair / SSO
warehouse="<warehouse>",
database="<database>",
schema="<schema>",
)
conn.cursor().execute("<SQL here>")
✋ ARRÊTEZ — Confirmez la méthode de connexion Snowflake avec l'utilisateur avant de passer à l'étape 7. Enregistrez quelle méthode est utilisée afin que l'étape 7 génère la bonne commande d'exécution.
Flux de travail
Étape 1 — Recueillir les exigences
Demandez à l'utilisateur :
- Quel Topic veut-il convertir ?
- Quel nom doit avoir la Semantic View ?
- Où doit-elle être créée dans Snowflake (base de données et schéma) ?
- Quel rôle doit avoir accès à la Semantic View ?
⚠️ ARRÊTEZ — Confirmez les quatre réponses avant de continuer.
Étape 2 — Explorer le modèle Omni
2a. Trouvez l'ID du modèle
omni models list --modelkind SHARED
Identifiez le Shared Model et notez son id. Préférez toujours le Shared Model aux modèles Schema ou Workbook.
2b. Récupérez le fichier topic
omni models yaml-get <modelId> --filename <topic_name>.topic
2c. Récupérez le fichier relationships
omni models yaml-get <modelId> --filename relationships
2d. Récupérez chaque fichier view référencé dans le topic
Pour chaque view dans base_view et joins, récupérez son YAML :
omni models yaml-get <modelId> --filename <view_name>.view
Si une view est préfixée par
omni_dbt_, récupérez le fichier qui commence aussi paromni_dbt_(ex.omni_dbt_ecomm__order_items.view).
Étape 3 — Identifier les tables et les jointures
Mappage des noms de view aux tables Snowflake
La base_view du fichier topic est la table primaire. Convertissez les noms de view en références de table Snowflake :
base_view / valeur de join |
Table Snowflake |
|---|---|
ecomm__order_items |
ECOMM.ORDER_ITEMS |
omni_dbt_ecomm__order_items |
ECOMM.ORDER_ITEMS (supprimez omni_dbt_) |
Le séparateur __ correspond au schéma (gauche) et au nom de table (droite). Si le schéma n'existe pas dans Snowflake, ignorez entièrement cette table.
Lecture de la hiérarchie des jointures
Le paramètre joins du topic utilise l'indentation pour définir la chaîne de jointure — une table indentée sous une autre se joint à sa parente :
joins:
user_order_facts: {} # ignorer — c'est une CTE dérivée, pas une table physique
ecomm__users: {} # se joint à base_view (ORDER_ITEMS)
ecomm__inventory_items: # se joint à base_view (ORDER_ITEMS)
ecomm__products: # se joint à INVENTORY_ITEMS
demo__product_images: {} # se joint à PRODUCTS
ecomm__distribution_centers: {} # se joint à PRODUCTS
Ignorez toute view qui est une table dérivée (CTE définie en SQL dans Omni). Elles n'ont pas de table Snowflake physique à référencer.
Clés primaires
Dans chaque fichier view, trouvez la dimension avec primary_key: true — celle-ci devient la dimension unique: true dans la Semantic View.
✋ ARRÊTEZ — Confirmez la liste des tables avec l'utilisateur avant de continuer.
Étape 4 — Résoudre la liste des champs
Le paramètre fields du topic contrôle les champs des views inclus dans la Semantic View.
Règles de ciblage des champs
| Syntaxe | Signification |
|---|---|
(pas de paramètre fields) |
Inclure tous les champs de toutes les views |
all_views.* |
Inclure tous les champs de toutes les views |
view.* |
Inclure tous les champs dans la view nommée |
tag:<value> |
Inclure tous les champs marqués avec cette valeur |
view.field |
Inclure ce champ spécifique |
-view.field |
Exclure ce champ spécifique |
Comment appliquer correctement les exclusions
Les exclusions (préfixées par -) doivent être appliquées après que toutes les inclusions soient résolues. Le processus est :
- Commencez avec un ensemble d'inclusion vide
- Traitez chaque entrée dans
fieldsdans l'ordre :- Si c'est une règle d'inclusion (
view.*,view.field,tag:x) → ajoutez les champs correspondants à l'ensemble - Si c'est une règle d'exclusion (
-view.field) → supprimez ce champ de l'ensemble, même s'il a été ajouté par un wildcard
- Si c'est une règle d'inclusion (
- L'ensemble final est la liste complète des champs à inclure dans la Semantic View
Exemple :
fields:
- ecomm__order_items.* # inclure tous les champs order_items
- ecomm__users.country # inclure ce seul champ users
- -ecomm__order_items.cost # supprimer cost — exclu même si * a été utilisé ci-dessus
- -ecomm__order_items.raw_json # supprimer raw_json — même raison
Résultat : tous les champs order_items sauf cost et raw_json, plus users.country.
⚠️ Critique : Une exclusion
-gagne toujours. N'incluez jamais un champ qui a été explicitement exclu, peu importe ce que le wildcard a inclus.
Étape 5 — Construire les relations
En utilisant la hiérarchie des jointures de l'étape 3 et le relationships.yaml récupéré à l'étape 2c, mappez chaque jointure à une relation Snowflake Semantic View.
Chaque entrée dans relationships.yaml ressemble à :
- join_from_view: ecomm__order_items
join_to_view: ecomm__inventory_items
join_type: always_left
on_sql: ${ecomm__order_items.inventory_item_id} = ${ecomm__inventory_items.id}
relationship_type: assumed_many_to_one
Le champ on_sql vous indique les colonnes de jointure. Extrayez les noms de colonnes pour remplir relationship_columns dans la sortie.
Paramètres de relation disponibles :
| Paramètre | Description |
|---|---|
join_from_view |
Vue source d'où provient la jointure |
join_to_view |
Vue cible qui est jointe |
join_type |
Type de jointure SQL (ex. always_left) |
on_sql |
Condition SQL — extrayez les noms de colonnes de celle-ci |
relationship_type |
Cardinalité (ex. assumed_many_to_one) |
reversible |
Si la jointure est bidirectionnelle |
where_sql |
Clause WHERE supplémentaire quand la jointure est active |
Étape 6 — Mapper les dimensions et les mesures
Pour chaque view dans la liste des champs résolue, traduisez ses définitions de champ Omni en entrées Semantic View.
⚠️ Traduisez uniquement les champs qui ont survécu à la résolution inclusion/exclusion de l'étape 4. N'ajoutez pas les champs qui ont été exclus.
Dimensions → dimensions ou time_dimensions
Le nom du champ devient le nom de la dimension sauf si un label est défini. Portez description et synonyms directement.
Dimension standard :
# YAML de view Omni
city:
sql: '"CITY"'
label: City
description: Customer's city
# Sortie Semantic View
- name: city
expr: CITY
description: Customer's city
data_type: TEXT
Dimension date/timestamp → utilisez time_dimensions :
# Omni
created_at:
sql: '"CREATED_AT"'
type: time
label: Created At
# Sortie Semantic View
time_dimensions:
- name: created_at
expr: CREATED_AT
data_type: TIMESTAMP
Dimension de groupe → traduisez en expression CASE WHEN :
# Omni
device_type_groups:
sql: ${device_type}
label: Device Type Groups
groups:
- filter:
is: [ mobile, tablet ]
name: Handheld
- filter:
is: desktop
name: Desktop
else: Other
-- valeur expr
CASE
WHEN "DEVICE_TYPE" IN ('mobile', 'tablet') THEN 'Handheld'
WHEN "DEVICE_TYPE" = 'desktop' THEN 'Desktop'
ELSE 'Other'
END
Dimension bin → traduisez en expression CASE WHEN de plage :
# Omni
age_bin:
sql: ${age}
bin_boundaries: [ 18, 35, 50, 65 ]
-- valeur expr
CASE
WHEN "AGE" < 18 THEN 'below 18'
WHEN "AGE" >= 18 AND "AGE" < 35 THEN '>= 18 and < 35'
WHEN "AGE" >= 35 AND "AGE" < 50 THEN '>= 35 and < 50'
WHEN "AGE" >= 50 AND "AGE" < 65 THEN '>= 50 and < 65'
WHEN "AGE" >= 65 THEN '65 and above'
ELSE NULL
END
Dimension de durée → traduisez en expression TIMESTAMPDIFF :
# Omni
fulfillment_days:
duration:
sql_start: ${created_at[date]}
sql_end: ${delivered_at[date]}
intervals: [ days ]
-- valeur expr (days)
CASE
WHEN TIMESTAMPADD(DAY, 1 * TIMESTAMPDIFF(DAY, DATE_TRUNC('DAY', "CREATED_AT"), DATE_TRUNC('DAY', "DELIVERED_AT")), DATE_TRUNC('DAY', "CREATED_AT"))
<= DATE_TRUNC('DAY', "DELIVERED_AT")
THEN TIMESTAMPDIFF(DAY, DATE_TRUNC('DAY', "CREATED_AT"), DATE_TRUNC('DAY', "DELIVERED_AT"))
ELSE TIMESTAMPDIFF(DAY, DATE_TRUNC('DAY', "CREATED_AT"), DATE_TRUNC('DAY', "DELIVERED_AT")) - 1
END
Dimension booléenne → devient un filter nommé (pas une dimension) :
# Omni
is_returned:
sql: '"IS_RETURNED"'
description: Whether the item was returned
completed_orders:
sql: ${status} = 'Complete'
label: Completed Orders
# Sortie Semantic View — va dans filters, pas dimensions
filters:
- name: is_returned
expr: IS_RETURNED
description: Whether the item was returned
- name: completed_orders
expr: STATUS = 'Complete'
description: Completed Orders
Mesures → metrics
Le champ sql référence la colonne source et aggregate_type définit l'agrégation.
Mesure standard :
# Omni
total_sale_price:
sql: ${sale_price}
aggregate_type: sum
label: Total Sale Price
description: Total revenue of orders
synonyms: [ Total Revenue, Total Receipts ]
# Sortie Semantic View
metrics:
- name: total_sale_price
expr: COALESCE(SUM("SALE_PRICE"), 0)
description: Total revenue of orders
synonyms: [ Total Revenue, Total Receipts ]
Mesure dérivée (pas de aggregate_type) — référence d'autres mesures :
# Omni
gross_margin:
sql: ${total_sale_price} - ${total_cost}
label: Gross Margin
# Sortie Semantic View — métrique dérivée de haut niveau
metrics:
- name: gross_margin
expr: total_sale_price - total_cost
Mesure filtrée → enveloppe dans CASE WHEN :
# Omni
california_revenue:
sql: ${sale_price}
aggregate_type: sum
filters:
users.state:
is: California
-- valeur expr
COALESCE(SUM(CASE WHEN "users"."STATE" = 'California' THEN "SALE_PRICE" ELSE NULL END), 0)
Variantes de filtres array et booléen :
# is: [New York, New Jersey] → "STATE" IN ('New York', 'New Jersey')
# is: true → field IS TRUE
Contexte IA → module_custom_instructions
Si le topic a un paramètre ai_context, incluez-le comme :
module_custom_instructions:
question_categorization: <valeur ai_context>
Exemples de requêtes → verified_queries
Convertissez chaque entrée sous sample_queries en instruction SQL pour verified_queries. Utilisez le prompt comme question et la description comme contexte pour écrire le SQL.
✋ ARRÊTEZ — Examinez toutes les dimensions, mesures et relations avec l'utilisateur avant de générer la sortie finale.
Étape 7 — Générer et exécuter la Semantic View
Structure YAML de sortie
name: <name>
description: <string>
tables:
- name: <name>
description: <string>
base_table:
database: <database>
schema: <schema>
table: <table name>
dimensions:
- name: <name>
synonyms: [ <string>, ... ]
description: <string>
expr: <SQL expression>
data_type: <data type>
unique: <boolean>
time_dimensions:
- name: <name>
synonyms: [ <string>, ... ]
description: <string>
expr: <SQL expression>
data_type: <data type>
facts:
- name: <name>
synonyms: [ <string>, ... ]
description: <string>
expr: <SQL expression>
data_type: <data type>
metrics:
- name: <name>
synonyms: [ <string>, ... ]
description: <string>
expr: <SQL expression>
filters:
- name: <name>
synonyms: [ <string>, ... ]
description: <string>
expr: <SQL expression>
relationships:
- name: <string>
left_table: <table>
right_table: <table>
relationship_columns:
- left_column: <column>
right_column: <column>
metrics:
- name: <name>
synonyms: [ <string>, ... ]
description: <string>
expr: <SQL expression>
verified_queries:
- name: <string>
question: <string>
sql: <string>
use_as_onboarding_question: <boolean>
Clés de haut niveau valides uniquement :
name,description,tables,relationships,metrics,verified_queries,module_custom_instructions
Créez la Semantic View
Le SQL est identique quel que soit l'environnement. La différence est comment il est exécuté.
SQL à exécuter :
CALL SYSTEM$CREATE_SEMANTIC_VIEW_FROM_YAML('<database>.<schema>', $$
<contenu yaml ici>
$$);
Exécution par environnement :
| Environnement | Commande |
|---|---|
| Cortex Code | Exécutez le SQL directement dans la session Snowflake active |
snow CLI |
snow sql -q "CALL SYSTEM$CREATE_SEMANTIC_VIEW_FROM_YAML(...)" ou écrivez le YAML dans un fichier temporaire et dirigez-le |
| SnowSQL | snowsql -a <account> -u <user> -f <sql_file.sql> |
| Python | conn.cursor().execute("<sql>") |
Conseil pour les environnements CLI : Si le YAML est long, écrivez-le d'abord dans un fichier
.sqltemporaire et exécutez le fichier plutôt que de le passer en ligne — cela évite les problèmes d'échappement shell avec le dollar-quoting$$.
# Exemple snow CLI avec un fichier
snow sql -f /tmp/create_semantic_view.sql
Accordez l'accès
GRANT SELECT ON SEMANTIC VIEW <database>.<schema>.<name> TO ROLE <role>;
Exécutez cela de la même manière que l'appel CREATE ci-dessus, en utilisant la méthode de connexion établie pendant l'étape de détection de l'environnement d'exécution.