Aller au contenu principal

Mettre à jour les schémas de table avec l'évolution des schémas

Les tables prennent en charge l'évolution des schémas, permettant des modifications de la structure des tables à mesure que les exigences en matière de données évoluent. Les types de modifications suivants sont pris en charge :

Apportez ces modifications explicitement en utilisant DDL ou implicitement en utilisant DML.

important

Les mises à jour de schéma entrent en conflit avec toutes les opérations d'écriture concurrentes. Databricks recommande de coordonner les modifications de schéma afin d’éviter les conflits d’écriture.

La mise à jour d'un schéma de table met fin à tous les Streams qui lisent cette table. Pour continuer le traitement, redémarrez le stream en utilisant les méthodes décrites dans Considérations de production pour le Structured Streaming.

Modifications manuelles du schéma

Utilisez des instructions ALTER TABLE pour modifier explicitement le schéma d'une table sans écrire de nouvelles données.

Ajouter des colonnes

Utilisez ALTER TABLE ... ADD COLUMNS pour ajouter une ou plusieurs colonnes à une table existante, en spécifiant éventuellement la position et un commentaire :

SQL
ALTER TABLE table_name ADD COLUMNS (col_name data_type [COMMENT col_comment] [FIRST|AFTER colA_name], ...)

Par défaut, la nullabilité est true.

Exemple : Ajouter des champs imbriqués

L'ajout de colonnes imbriquées est pris en charge uniquement pour les structs. Les tableaux et les cartes ne sont pas pris en charge.

Pour ajouter une colonne à un champ imbriqué, utilisez :

SQL
ALTER TABLE table_name ADD COLUMNS (col_name.nested_col_name data_type [COMMENT col_comment] [FIRST|AFTER colA_name], ...)

Par exemple, si le schéma avant d'exécuter ALTER TABLE boxes ADD COLUMNS (colB.nested STRING AFTER field1) est :

- root
| - colA
| - colB
| +-field1
| +-field2

le schéma est le suivant :

- root
| - colA
| - colB
| +-field1
| +-nested
| +-field2

Modifier les commentaires et l'ordre des colonnes

Utilisez ALTER TABLE ... ALTER COLUMN pour mettre à jour le commentaire d'une colonne ou pour la réorganiser par rapport aux autres colonnes :

SQL
ALTER TABLE table_name ALTER [COLUMN] col_name (COMMENT col_comment | FIRST | AFTER colA_name)

Exemple : modifier les champs imbriqués

Pour modifier une colonne dans un champ imbriqué, utilisez :

SQL
ALTER TABLE table_name ALTER [COLUMN] col_name.nested_col_name (COMMENT col_comment | FIRST | AFTER colA_name)

Par exemple, si le schéma avant d'exécuter ALTER TABLE boxes ALTER COLUMN colB.field2 FIRST est :

- root
| - colA
| - colB
| +-field1
| +-field2

le schéma est le suivant :

- root
| - colA
| - colB
| +-field2
| +-field1

Remplacer les colonnes

Utilisez ALTER TABLE ... REPLACE COLUMNS pour redéfinir la liste complète des colonnes d'une table, y compris l'ajout, la suppression, le réordonnancement ou le renommage de colonnes en une seule opération :

SQL
ALTER TABLE table_name REPLACE COLUMNS (col_name1 col_type1 [COMMENT col_comment1], ...)

Exemple : remplacer les champs imbriqués

Par exemple, lors de l'exécution du DDL suivant :

SQL
ALTER TABLE boxes REPLACE COLUMNS (colC STRING, colB STRUCT<field2:STRING, nested:STRING, field1:STRING>, colA STRING)

si le schéma précédent est :

- root
| - colA
| - colB
| +-field1
| +-field2

le schéma est le suivant :

- root
| - colC
| - colB
| +-field2
| +-nested
| +-field1
| - colA

Renommer les colonnes

Pour renommer des colonnes sans réécrire les données existantes des colonnes, vous devez activer le mappage des colonnes pour la table. Consultez Renommer et supprimer des colonnes avec le mappage de colonnes Delta Lake.

Pour renommer une colonne :

SQL
ALTER TABLE table_name RENAME COLUMN old_col_name TO new_col_name

Exemple : Renommer les champs imbriqués

Renommer un champ imbriqué :

SQL
ALTER TABLE table_name RENAME COLUMN col_name.old_nested_field TO new_nested_field

Par exemple, lorsque vous exécutez la commande suivante :

SQL
ALTER TABLE boxes RENAME COLUMN colB.field1 TO field001

Si le schéma précédent est :

- root
| - colA
| - colB
| +-field1
| +-field2

Ensuite le schéma après est :

- root
| - colA
| - colB
| +-field001
| +-field2

Voir Renommer et supprimer des colonnes avec le mappage de colonnes Delta Lake.

Supprimer des colonnes

Pour supprimer des colonnes en tant qu'opération de métadonnées uniquement sans réécrire les fichiers de données, vous devez activer le mappage des colonnes pour la table. Consultez Renommer et supprimer des colonnes avec le mappage de colonnes Delta Lake.

remarque

La suppression d'une colonne des métadonnées ne supprime pas les données sous-jacentes de la colonne dans les fichiers. Pour purger les données de colonne supprimées :

  • Utilisez REORG TABLE pour réécrire les fichiers.
  • Utilisez ensuite VACUUM pour supprimer physiquement les fichiers qui contiennent les données des colonnes supprimées.

Pour supprimer une colonne :

SQL
ALTER TABLE table_name DROP COLUMN col_name

Pour supprimer plusieurs colonnes :

SQL
ALTER TABLE table_name DROP COLUMNS (col_name_1, col_name_2)

Modifier le type ou le nom de la colonne

Vous pouvez modifier le type ou le nom d'une colonne ou supprimer une colonne en réécrivant la table. Pour ce faire, utilisez l'option overwriteSchema.

L'exemple suivant montre le changement d'un type de colonne :

Python
(spark.read.table(...)
.withColumn("birthDate", col("birthDate").cast("date"))
.write
.mode("overwrite")
.option("overwriteSchema", "true")
.saveAsTable(...)
)

L'exemple suivant présente la modification d'un nom de colonne :

Python
(spark.read.table(...)
.withColumnRenamed("dateOfBirth", "birthDate")
.write
.mode("overwrite")
.option("overwriteSchema", "true")
.saveAsTable(...)
)

Activer l'évolution des schémas

Utilisez WITH SCHEMA EVOLUTION ou définissez mergeSchema sur true pour apporter des modifications de schéma en fonction du schéma des données que vous souhaitez INSERT ou MERGE dans une table existante.

Activer l'évolution des schémas en utilisant l'une des méthodes suivantes :

Databricks recommande d'activer l'évolution des schémas pour chaque opération d'écriture en utilisant la syntaxe WITH SCHEMA EVOLUTION ou l'option mergeSchema plutôt que de définir une configuration Spark.

Lorsque vous utilisez des options ou une syntaxe pour activer l'évolution des schémas dans une opération d'écriture, cela prévaut sur la configuration Spark.

Activez l'évolution des schémas pour les écritures afin d'ajouter de nouvelles colonnes

Lorsque l'évolution des schémas est activée, les colonnes présentes dans la query source mais absentes de la table cible sont automatiquement ajoutées dans le cadre d'une transaction d'écriture. Consultez Activer l'évolution des schémas.

Considérons les éléments suivants :

  • La casse est conservée lors de l'ajout d'une nouvelle colonne.
  • Les nouvelles colonnes sont ajoutées à la fin du schéma de la table.
  • Si les colonnes supplémentaires se trouvent dans une structure, elles sont ajoutées à la fin de la structure dans la table cible.

INSERT avec évolution des schémas à l'aide de SQL

Utilisez la clause WITH SCHEMA EVOLUTION dans les instructions INSERT pour activer l'évolution des schémas :

SQL
INSERT WITH SCHEMA EVOLUTION INTO target_table
SELECT * FROM source_table

Si la query sur source_table renvoie des colonnes qui n'existent pas dans la table cible, ces colonnes sont automatiquement ajoutées au schéma target_table. Les lignes existantes reçoivent NULL valeurs pour les nouvelles colonnes.

INSERT avec évolution des schémas à l'aide de l'API DataFrame

L'exemple suivant illustre l'utilisation de l'option mergeSchema avec une opération d'écriture par batch :

Python
(spark.read
.table("source_table")
.write
.option("mergeSchema", "true")
.mode("append")
.saveAsTable("target_table")
)

INSERT avec évolution des schémas avec Structured Streaming

L’exemple suivant montre comment utiliser l’option mergeSchema avec Auto Loader pour Structured Streaming. Consultez Qu’est-ce qu’Auto Loader ?.

Python
(spark.readStream
.format("cloudFiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", "<path-to-schema-location>")
.load("<path-to-source-data>")
.writeStream
.option("mergeSchema", "true")
.option("checkpointLocation", "<path-to-checkpoint>")
.trigger(availableNow=True)
.toTable("table_name")
)

Évolution automatique des schémas pour le merge

Pour MERGE, l’évolution des schémas vous permet de résoudre les non-concordances de schémas entre la table cible et la table source. Il gère les deux cas suivants :

  1. Une colonne existe dans la table source mais pas dans la table cible, et est spécifiée par son nom dans une affectation d'actions d'insertion ou de mise à jour. Alternativement, une action UPDATE SET * ou INSERT * est présente.

    Cette colonne sera ajoutée au schéma cible, et ses valeurs seront renseignées à partir de la colonne correspondante dans la source.

    • Ceci ne s'applique que lorsque le nom de la colonne et la structure dans la source de Merge correspondent exactement à l'affectation cible.

    • La nouvelle colonne doit être présente dans le schéma source. L'affectation de la nouvelle colonne dans la clause d'action ne définit pas cette colonne.

    Ces exemples permettent l'évolution des schémas :

    SQL
    -- The column newcol is present in the source but not in the target. It will be added to the target.
    UPDATE SET target.newcol = source.newcol

    -- The field newfield doesn't exist in struct column somestruct of the target. It will be added to that struct column.
    UPDATE SET target.somestruct.newfield = source.somestruct.newfield

    -- The column newcol is present in the source but not in the target.
    -- It will be added to the target.
    UPDATE SET target.newcol = source.newcol + 1

    -- Any columns and nested fields in the source that don't exist in target will be added to the target.
    UPDATE SET *
    INSERT *

    Ces exemples ne trigger pas l'évolution des schémas si la colonne newcol n'est pas présente dans le schéma source :

    SQL
    UPDATE SET target.newcol = source.someothercol
    UPDATE SET target.newcol = source.x + source.y
    UPDATE SET target.newcol = source.output.newcol
  2. Une colonne existe dans la table cible mais pas dans la table source.

    Le schéma cible n'est pas modifié. Ces colonnes :

    • Sont laissés inchangés pour UPDATE SET *.

    • Sont définis sur NULL pour INSERT *.

    • Pourrait toujours être explicitement modifié s’il est attribué dans la clause d’action.

    Par exemple :

    SQL
    UPDATE SET *  -- The target columns that are not in the source are left unchanged.
    INSERT * -- The target columns that are not in the source are set to NULL.
    UPDATE SET target.onlyintarget = 5 -- The target column is explicitly updated.
    UPDATE SET target.onlyintarget = source.someothercol -- The target column is explicitly updated from some other source column.

Vous devez activer manuellement l'évolution automatique des schémas. Consultez Activer l'évolution des schémas.

remarque

Dans Databricks Runtime 11.3 LTS et versions antérieures, seules les actions INSERT * ou UPDATE SET * peuvent être utilisées pour l'évolution des schémas avec Merge.

Dans Databricks Runtime 12.2 LTS et versions ultérieures, les colonnes et les champs struct présents dans la table source peuvent être spécifiés par leur nom dans les actions d'insertion ou de mise à jour.

Dans Databricks Runtime 13.3 LTS et versions ultérieures, vous pouvez utiliser l'évolution des schémas avec des structs imbriquées dans des maps, telles que map<int, struct<a: int, b: int>>.

MERGE avec évolution des schémas utilisant SQL, Python et Scala

Dans Databricks Runtime 15.4 LTS et versions ultérieures, vous pouvez spécifier l'évolution des schémas dans une instruction de Merge en utilisant SQL ou les APIs de table :

SQL
MERGE WITH SCHEMA EVOLUTION INTO target
USING source
ON source.key = target.key
WHEN MATCHED THEN
UPDATE SET *
WHEN NOT MATCHED THEN
INSERT *
WHEN NOT MATCHED BY SOURCE THEN
DELETE

Exemple d'opérations de MERGE avec évolution des schémas

Voici quelques exemples des effets de l'opération MERGE avec et sans évolution des schémas.

Colonnes

query (en SQL)

Comportement sans évolution des schémas (default)

Comportement avec l'évolution des schémas

Colonnes cibles : key, value

Colonnes source: key, value, new_value

SQL
MERGE INTO target_table t
USING source_table s
ON t.key = s.key
WHEN MATCHED
THEN UPDATE SET *
WHEN NOT MATCHED
THEN INSERT *

Le schéma de la table reste inchangé ; seules les colonnes key, value sont mises à jour/insérées.

Le schéma de la table est modifié en (key, value, new_value). Les enregistrements existants avec correspondances sont mis à jour avec les value et new_value dans la source. De nouvelles lignes sont insérées avec le schéma (key, value, new_value).

Colonnes cibles : key, old_value

Colonnes source: key, new_value

SQL
MERGE INTO target_table t
USING source_table s
ON t.key = s.key
WHEN MATCHED
THEN UPDATE SET *
WHEN NOT MATCHED
THEN INSERT *

UPDATE et les actions INSERT génèrent une erreur car la colonne cible old_value n'est pas dans la source.

Le schéma de la table est modifié en (key, old_value, new_value). Les enregistrements existants avec des correspondances sont mis à jour avec le new_value dans la source, laissant le old_value inchangé. Les nouveaux enregistrements sont insérés avec les key, new_value et NULL spécifiés pour le old_value.

Colonnes cibles : key, old_value

Colonnes source: key, new_value

SQL
MERGE INTO target_table t
USING source_table s
ON t.key = s.key
WHEN MATCHED
THEN UPDATE SET new_value = s.new_value

UPDATE renvoie une erreur, car la colonne new_value n'existe pas dans la table cible.

Le schéma de la table est modifié en (key, old_value, new_value). Les enregistrements existants avec des correspondances sont mis à jour avec le new_value dans la source, laissant old_value inchangé(s), et les enregistrements sans correspondance ont NULL saisi pour new_value. Voir la note (1).

Colonnes cibles : key, old_value

Colonnes source: key, new_value

SQL
MERGE INTO target_table t
USING source_table s
ON t.key = s.key
WHEN NOT MATCHED
THEN INSERT (key, new_value) VALUES (s.key, s.new_value)

INSERT renvoie une erreur, car la colonne new_value n'existe pas dans la table cible.

Le schéma de la table est modifié en (key, old_value, new_value). De nouveaux enregistrements sont insérés avec les key, new_value et NULL spécifiés pour le old_value. Les enregistrements existants ont NULL saisi pour new_value, laissant old_value inchangé. Voir note (1).

Colonnes

query (en SQL)

Comportement sans évolution des schémas (default)

Comportement avec l'évolution des schémas

Colonnes cibles : key, value

Colonnes source: key, value, new_value

SQL
MERGE INTO target_table t
USING source_table s
ON t.key = s.key
WHEN MATCHED
THEN UPDATE SET *
WHEN NOT MATCHED
THEN INSERT *

Le schéma de la table reste inchangé ; seules les colonnes key, value sont mises à jour/insérées.

Le schéma de la table est modifié en (key, value, new_value). Les enregistrements existants avec correspondances sont mis à jour avec les value et new_value dans la source. De nouvelles lignes sont insérées avec le schéma (key, value, new_value).

Colonnes cibles : key, old_value

Colonnes source: key, new_value

SQL
MERGE INTO target_table t
USING source_table s
ON t.key = s.key
WHEN MATCHED
THEN UPDATE SET *
WHEN NOT MATCHED
THEN INSERT *

UPDATE et les actions INSERT génèrent une erreur car la colonne cible old_value n'est pas dans la source.

Le schéma de la table est modifié en (key, old_value, new_value). Les enregistrements existants avec des correspondances sont mis à jour avec le new_value dans la source, laissant le old_value inchangé. Les nouveaux enregistrements sont insérés avec les key, new_value et NULL spécifiés pour le old_value.

Colonnes cibles : key, old_value

Colonnes source: key, new_value

SQL
MERGE INTO target_table t
USING source_table s
ON t.key = s.key
WHEN MATCHED
THEN UPDATE SET new_value = s.new_value

UPDATE renvoie une erreur, car la colonne new_value n'existe pas dans la table cible.

Le schéma de la table est modifié en (key, old_value, new_value). Les enregistrements existants avec des correspondances sont mis à jour avec le new_value dans la source, laissant old_value inchangé(s), et les enregistrements sans correspondance ont NULL saisi pour new_value. Voir la note (1).

Colonnes cibles : key, old_value

Colonnes source: key, new_value

SQL
MERGE INTO target_table t
USING source_table s
ON t.key = s.key
WHEN NOT MATCHED
THEN INSERT (key, new_value) VALUES (s.key, s.new_value)

INSERT renvoie une erreur, car la colonne new_value n'existe pas dans la table cible.

Le schéma de la table est modifié en (key, old_value, new_value). De nouveaux enregistrements sont insérés avec les key, new_value et NULL spécifiés pour le old_value. Les enregistrements existants ont NULL saisi pour new_value, laissant old_value inchangé. Voir note (1).

(1) Ce comportement est disponible dans Databricks Runtime 12.2 LTS et versions ultérieures ; Databricks Runtime 11.3 LTS et versions antérieures génèrent une erreur dans cette condition.

Exclure des colonnes avec Merge

Dans Databricks Runtime 12.2 LTS et versions ultérieures, vous pouvez utiliser les clauses EXCEPT dans les conditions Merge pour exclure explicitement des colonnes. Le comportement du mot-clé EXCEPT varie selon que l'évolution des schémas est activée ou non.

Lorsque l'évolution des schémas est désactivée, le mot-clé EXCEPT s'applique à la liste des colonnes de la table cible et permet d'exclure des colonnes des actions UPDATE ou INSERT. Les colonnes exclues sont définies sur null.

Avec l’évolution des schémas activée, le mot-clé EXCEPT s’applique à la liste des colonnes dans la table source et permet d’exclure des colonnes de l’évolution des schémas. Une nouvelle colonne dans la source, non présente dans la table cible, n'est pas ajoutée au schéma cible si elle est listée dans la clause EXCEPT. Les colonnes exclues qui sont déjà présentes dans la cible sont définies sur null.

Exemples de EXCLUDE avec MERGE

Les exemples suivants illustrent cette syntaxe :

Colonnes

query (en SQL)

Comportement sans évolution des schémas (default)

Comportement avec l'évolution des schémas

Colonnes cibles : id, title, last_updated

Colonnes source: id, title, review, last_updated

SQL
MERGE INTO target t
USING source s
ON t.id = s.id
WHEN MATCHED
THEN UPDATE SET last_updated = current_date()
WHEN NOT MATCHED
THEN INSERT * EXCEPT (last_updated)

Les lignes correspondantes sont mises à jour en définissant le champ last_updated à la date actuelle. De nouvelles lignes sont insérées en utilisant les valeurs pour id et title. Le champ exclu last_updated est défini sur null. Le champ review est ignoré car il n'est pas dans la cible.

Les lignes correspondantes sont mises à jour en définissant le champ last_updated à la date actuelle. Le schéma est fait évoluer pour ajouter le champ review. De nouvelles lignes sont insérées à l'aide de tous les champs sources, à l'exception de last_updated qui est défini sur null.

Colonnes cibles : id, title, last_updated

Colonnes source: id, title, review, internal_count

SQL
MERGE INTO target t
USING source s
ON t.id = s.id
WHEN MATCHED
THEN UPDATE SET last_updated = current_date()
WHEN NOT MATCHED
THEN INSERT * EXCEPT (last_updated, internal_count)

INSERT renvoie une erreur, car la colonne internal_count n'existe pas dans la table cible.

Les lignes correspondantes sont mises à jour en définissant le champ last_updated à la date actuelle. Le champ review est ajouté à la table cible, mais le champ internal_count est ignoré. Les nouvelles lignes insérées ont last_updated défini sur null.

Colonnes

query (en SQL)

Comportement sans évolution des schémas (default)

Comportement avec l'évolution des schémas

Colonnes cibles : id, title, last_updated

Colonnes source: id, title, review, last_updated

SQL
MERGE INTO target t
USING source s
ON t.id = s.id
WHEN MATCHED
THEN UPDATE SET last_updated = current_date()
WHEN NOT MATCHED
THEN INSERT * EXCEPT (last_updated)

Les lignes correspondantes sont mises à jour en définissant le champ last_updated à la date actuelle. De nouvelles lignes sont insérées en utilisant les valeurs pour id et title. Le champ exclu last_updated est défini sur null. Le champ review est ignoré car il n'est pas dans la cible.

Les lignes correspondantes sont mises à jour en définissant le champ last_updated à la date actuelle. Le schéma est fait évoluer pour ajouter le champ review. De nouvelles lignes sont insérées à l'aide de tous les champs sources, à l'exception de last_updated qui est défini sur null.

Colonnes cibles : id, title, last_updated

Colonnes source: id, title, review, internal_count

SQL
MERGE INTO target t
USING source s
ON t.id = s.id
WHEN MATCHED
THEN UPDATE SET last_updated = current_date()
WHEN NOT MATCHED
THEN INSERT * EXCEPT (last_updated, internal_count)

INSERT renvoie une erreur, car la colonne internal_count n'existe pas dans la table cible.

Les lignes correspondantes sont mises à jour en définissant le champ last_updated à la date actuelle. Le champ review est ajouté à la table cible, mais le champ internal_count est ignoré. Les nouvelles lignes insérées ont last_updated défini sur null.

Activer l'évolution des schémas avec la configuration Spark (hérité)

Vous pouvez définir la configuration Spark spark.databricks.delta.schema.autoMerge.enabled sur true pour activer l'évolution des schémas pour toutes les opérations d'écriture dans la SparkSession actuelle :

Python
spark.conf.set("spark.databricks.delta.schema.autoMerge.enabled", True)
remarque

Databricks ne recommande pas cette approche pour la production. La définition d'une configuration à l'échelle de la session pourrait entraîner des modifications de schéma involontaires à travers de multiples opérations et rend plus difficile de déterminer quelles opérations font évoluer le schéma.

Au lieu de cela, activez l'évolution des schémas pour chaque opération d'écriture :

Lorsque vous utilisez des options ou une syntaxe pour activer l'évolution des schémas dans une opération d'écriture, cela prévaut sur la configuration Spark.

Remplacer le schéma de table

By default, l'écrasement des données d'une table n'écrase pas le schéma. Lorsque vous remplacez une table en utilisant mode("overwrite") sans replaceWhere, vous pourriez toujours vouloir remplacer le schéma des données en cours d'écriture.

Pour remplacer le schéma et le partitionnement de la table, définissez l'option overwriteSchema sur true:

Python
df.write.option("overwriteSchema", "true")
remarque

Vous ne pouvez pas spécifier overwriteSchema comme true lorsque vous utilisez l'écrasement dynamique de partition. Consultez écrasements dynamiques de partition avec partitionOverwriteMode (hérité).