<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>article Schema Management and Drift Scenarios via Databricks Auto Loader in Technical Blog</title>
    <link>https://community.databricks.com/t5/technical-blog/schema-management-and-drift-scenarios-via-databricks-auto-loader/ba-p/63393</link>
    <description>&lt;P&gt;&lt;SPAN&gt;Data lakes notoriously have had challenges with managing incremental data processing at scale without integrating open table storage format frameworks (i.e. Delta Lake, Apache Iceberg, Apache Hudi). In addition, schema management is difficult with schema-less data and schema-on-read methods. With the power of Databricks Lakehouse, Delta Lake and Apache Spark provide the essential technologies integrated with Databricks Auto Loader (AL) to consistently and reliably stream and process raw data formats incrementally, while maintaining stellar performance and data governance.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Auto Loader features&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL is a boost over Spark Structured Streaming, supporting several additional benefits and solutions including:&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Databricks Runtime only Structured Streaming &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;cloudFiles &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;source&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Schema drift, dynamic inference, and evolution support&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Ingests data via JSON, CSV, PARQUET, AVRO, ORC, TEXT, and BINARYFILE input file formats&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Integration with cloud file notification services (i.e. Amazon SQS/SNS)&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Optimizes directory list mode scanning performance to discover new files in cloud storage (i.e. AWS, Azure, GCP, DBFS)&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN&gt;For further information please visit the official &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/en/ingestion/auto-loader/index.html#what-is-auto-loader" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Databricks Auto Loader&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; documentation.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Schema change scenarios&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;In this blog I will showcase a few examples of how AL handles schema management and drift scenarios using a public IoT sample dataset with schema modifications to showcase solutions. Schema 1 will contain an IoT sample dataset schema with all expected columns and expected data types. Schema 2 will contain unexpected changes to the IoT sample dataset schema with new columns and changed data types. The following variables and paths will be used for this demonstration along with &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/en/notebooks/widgets.html#databricks-widgets" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Databricks Widgets&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; to set your username folder.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
dbutils.widgets.text("dbfs_user_dir", "your_user_name") // widget for account email

val userName = dbutils.widgets.get("dbfs_user_dir")
val rawBasePath = s"dbfs:/user/$userName/raw/"
val repoBasePath = s"dbfs:/user/$userName/repo/"

val jsonSchema1Path = rawBasePath + "iot-schema-1.json"
val jsonSchema2Path = rawBasePath + "iot-schema-2.json"
val repoSchemaPath = repoBasePath + "iot-ddl.json"

dbutils.fs.rm(repoSchemaPath, true) // remove schema repo for demos&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;H5&gt;&lt;SPAN&gt;Schema 1&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
spark.read.json(jsonSchema1Path).printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(spark.read.json(jsonSchema1Path).limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;H5&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture3.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15013i6A5AC44F4AADEEC5/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture3.png" alt="Picture3.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/H5&gt;
&lt;H5&gt;&amp;nbsp;&lt;SPAN&gt;Schema 2&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="height: 102px; border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%" height="102px"&gt;&lt;LI-CODE lang="java"&gt;%scala
// NEW =&amp;gt; device_serial_number_device_type, location
spark.read.json(jsonSchema2Path).printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number_device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: double (nullable = true)
 |-- ip: string (nullable = true)
 |-- latitude: double (nullable = true)
 |-- location: struct (nullable = true)
 |    |-- cca2: string (nullable = true)
 |    |-- cca3: string (nullable = true)
 |    |-- cn: string (nullable = true)
 |-- longitude: double (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(spark.read.json(jsonSchema2Path).limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture4.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15014i311B367800AB6E88/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture4.png" alt="Picture4.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;&lt;STRONG&gt;&lt;STRONG style="color: inherit; font-size: 24px;"&gt;Example 1: Schema Tracking/Management&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL tracks schema versions, metadata, and changes to input data over time via specifying a location directory path. These features are incredibly useful for tracking history of data lineage, and are tightly integrated with the Delta Lake transactional log &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/delta-describe-history.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;DESCRIBE HISTORY&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; and time travel.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val rawAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath) // schema history tracking
.load(jsonSchema1Path)
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
rawAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: string (nullable = true)
 |-- c02_level: string (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: string (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: string (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: string (nullable = true)
 |-- humidity: string (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: string (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(rawAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;By default (for JSON, CSV, and XML file format) AL infers all column data types as strings, including nested fields.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture5.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15015i80CBF8BC27CFD9B2/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture5.png" alt="Picture5.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Here is the directory structure where AL stores schema versions. These files can be read via &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/en/reference/spark.html#apache-spark-api-reference" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Spark DataFrame API&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H5&gt;&lt;SPAN&gt;Schema Repository&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(dbutils.fs.ls(repoSchemaPath + "/_schemas"))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture6.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15022i44090F67D6FFCDF4/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture6.png" alt="Picture6.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H5&gt;Schema Metadata&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(spark.read.json(repoSchemaPath + "/_schemas"))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture7.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15029iFBDFE57AC97D605B/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture7.png" alt="Picture7.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Example 2: &lt;/STRONG&gt;&lt;A href="https://docs.databricks.com/en/ingestion/auto-loader/schema.html#override-schema-inference-with-schema-hints" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;Schema Hints&lt;/STRONG&gt;&lt;/A&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL provides hint logic using SQL DDL syntax to enforce and override dynamic schema inference on known single data types, as well as semi-structured complex data types.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val hintAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.schemaHints", "coordinates STRUCT&amp;lt;latitude:DOUBLE, longitude:DOUBLE&amp;gt;, humidity LONG, temp DOUBLE") // schema ddl hints
.load(jsonSchema1Path)
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
hintAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: string (nullable = true)
 |-- c02_level: string (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: string (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: string (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;The schema hints specified in the AL options perform the data type mappings on the respective columns. Hints are useful for applying schema enforcement on portions of the schema where data types are known while in tandem with dynamic schema inference covered in Example 3.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(hintAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;STRONG&gt;&lt;STRONG style="color: inherit; font-size: 24px;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture8.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15024i3B9ECF20B0F79CDA/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture8.png" alt="Picture8.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;&lt;STRONG&gt;&lt;STRONG style="color: inherit; font-size: 24px;"&gt;Example 3: &lt;/STRONG&gt;&lt;A style="font-size: 24px; font-weight: bold; background-color: #ffffff;" href="https://docs.databricks.com/en/ingestion/auto-loader/schema.html#how-does-auto-loader-schema-inference-work" target="_blank" rel="noopener"&gt;Dynamic Schema Inference&lt;/A&gt;&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL dynamically searches a sample of the dataset to determine nested structure. This avoids costly and slow full dataset scans to infer schema. The following configurations are available to adjust the amount of sample data used on read to discover initial schema:&lt;/SPAN&gt;&lt;/P&gt;
&lt;OL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;&lt;I&gt;spark.databricks.cloudFiles.schemaInference.sampleSize.numBytes &lt;/I&gt;&lt;/STRONG&gt;&lt;SPAN&gt;(default 50 GB)&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;&lt;I&gt;spark.databricks.cloudFiles.schemaInference.sampleSize.numFiles &lt;/I&gt;&lt;/STRONG&gt;&lt;SPAN&gt;(default 1000 files)&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val inferAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.inferColumnTypes", true) // schema inference
.load(jsonSchema1Path)
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
inferAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;AL saves the initial schema to the schema location path provided. This schema serves as the base version for the stream during incremental processing. Dynamic schema inference is an automated approach to applying schema changes over time.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(inferAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture9.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15019iF32FDE69E0CE08F9/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture9.png" alt="Picture9.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;&lt;STRONG style="color: inherit; font-size: 24px;"&gt;Example 4: Static User-Defined Schema&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL also supports static custom schemas just like Spark Structured Streaming. This eliminates the need for dynamic schema-on-read inference scans, which trigger additional Spark jobs and schema versions. The schema can be retrieved as a DDL string or a JSON payload.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H5&gt;&lt;SPAN&gt;DDL&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
inferAlDf.schema.toDDL&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;String = alarm_status STRING,battery_level BIGINT,c02_level BIGINT,cca2 STRING,cca3 STRING,cn STRING,coordinates STRUCT&amp;lt;latitude: DOUBLE, longitude: DOUBLE&amp;gt;,date STRING,device_id BIGINT,device_serial_number STRING,device_type STRING,epoch_time_miliseconds BIGINT,humidity BIGINT,ip STRING,scale STRING,temp DOUBLE,timestamp STRING,_rescued_data STRING&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;H5&gt;&lt;SPAN&gt;JSON&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
spark.read.json(repoSchemaPath + "/_schemas").select("dataSchemaJson").where("dataSchemaJson is not null").first()&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;org.apache.spark.sql.Row = [{"type":"struct","fields":[{"name":"alarm_status","type":"string","nullable":true,"metadata":{}},{"name":"battery_level","type":"long","nullable":true,"metadata":{}},{"name":"c02_level","type":"long","nullable":true,"metadata":{}},{"name":"cca2","type":"string","nullable":true,"metadata":{}},{"name":"cca3","type":"string","nullable":true,"metadata":{}},{"name":"cn","type":"string","nullable":true,"metadata":{}},{"name":"coordinates","type":{"type":"struct","fields":[{"name":"latitude","type":"double","nullable":true,"metadata":{}},{"name":"longitude","type":"double","nullable":true,"metadata":{}}]},"nullable":true,"metadata":{}},{"name":"date","type":"string","nullable":true,"metadata":{}},{"name":"device_id","type":"long","nullable":true,"metadata":{}},{"name":"device_serial_number","type":"string","nullable":true,"metadata":{}},{"name":"device_type","type":"string","nullable":true,"metadata":{}},{"name":"epoch_time_miliseconds","type":"long","nullable":true,"metadata":{}},{"name":"humidity","type":"long","nullable":true,"metadata":{}},{"name":"ip","type":"string","nullable":true,"metadata":{}},{"name":"scale","type":"string","nullable":true,"metadata":{}},{"name":"temp","type":"double","nullable":true,"metadata":{}},{"name":"timestamp","type":"string","nullable":true,"metadata":{}}]}]&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;Here’s an example of how to generate a user-defined &lt;/SPAN&gt;&lt;A href="https://api-docs.databricks.com/scala/spark/latest/org/apache/spark/sql/types/StructType$.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;StructType (Scala)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; | &lt;/SPAN&gt;&lt;A href="https://api-docs.databricks.com/python/pyspark/latest/pyspark.sql/api/pyspark.sql.types.StructType.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;StructType (Python)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; via DDL dataframe command or JSON queried from AL schema repository.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
import org.apache.spark.sql.types.{DataType, StructType}

val ddl = """alarm_status STRING,battery_level BIGINT,c02_level BIGINT,cca2 STRING,cca3 STRING,cn STRING,coordinates STRUCT&amp;lt;latitude: DOUBLE, longitude: DOUBLE&amp;gt;,date STRING,device_id BIGINT,device_serial_number STRING,device_type STRING,epoch_time_miliseconds BIGINT,humidity BIGINT,ip STRING,scale STRING,temp DOUBLE,timestamp STRING,_rescued_data STRING"""

val ddlSchema = StructType.fromDDL(ddl)

val json = """{"type":"struct","fields":[{"name":"alarm_status","type":"string","nullable":true,"metadata":{}},{"name":"battery_level","type":"long","nullable":true,"metadata":{}},{"name":"c02_level","type":"long","nullable":true,"metadata":{}},{"name":"cca2","type":"string","nullable":true,"metadata":{}},{"name":"cca3","type":"string","nullable":true,"metadata":{}},{"name":"cn","type":"string","nullable":true,"metadata":{}},{"name":"coordinates","type":{"type":"struct","fields":[{"name":"latitude","type":"double","nullable":true,"metadata":{}},{"name":"longitude","type":"double","nullable":true,"metadata":{}}]},"nullable":true,"metadata":{}},{"name":"date","type":"string","nullable":true,"metadata":{}},{"name":"device_id","type":"long","nullable":true,"metadata":{}},{"name":"device_serial_number","type":"string","nullable":true,"metadata":{}},{"name":"device_type","type":"string","nullable":true,"metadata":{}},{"name":"epoch_time_miliseconds","type":"long","nullable":true,"metadata":{}},{"name":"humidity","type":"long","nullable":true,"metadata":{}},{"name":"ip","type":"string","nullable":true,"metadata":{}},{"name":"scale","type":"string","nullable":true,"metadata":{}},{"name":"temp","type":"double","nullable":true,"metadata":{}},{"name":"timestamp","type":"string","nullable":true,"metadata":{}}]}"""

val jsonSchema = DataType.fromJson(json).asInstanceOf[StructType]&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val schemaAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.schema(jsonSchema) // schema structtype definition
.load(jsonSchema1Path)
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
schemaAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;Passing in the schema definition will enforce the stream. AL also provides a schema enforcement option achieving basically the same results as providing a static &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;StructType&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; schema-on-read. This method will be covered in Example 7.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(schemaAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture10.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15020i36A0A7230A002E1A/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture10.png" alt="Picture10.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Example 5: &lt;/STRONG&gt;&lt;A href="https://docs.databricks.com/en/ingestion/auto-loader/schema.html#what-is-the-rescued-data-column" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;Schema Drift&lt;/STRONG&gt;&lt;/A&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL stores new columns and data types via the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column. This column captures schema changes-on-read. The stream &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;does not&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; fail when schema and data type mismatches are discovered. This is a very impressive feature!&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val driftAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.inferColumnTypes", true)
.option("cloudFiles.schemaEvolutionMode", "rescue") // schema drift tracking
.load(rawBasePath + "/*.json")
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
driftAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;The &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column preserves schema drift such as newly appended columns and or different data types via a JSON string payload. This payload can be parsed via Spark DataFrame or DataSet APIs to analyze schema drift scenarios. The source file path for each individual row is also available in the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column to investigate the root cause.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%" height="636px"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(driftAlDf.where("_rescued_data is not null").limit(10))&lt;/LI-CODE&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture11.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15021i44CADBB306DEEB05/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture11.png" alt="Picture11.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;H2&gt;&lt;STRONG&gt;Example 6: &lt;/STRONG&gt;&lt;A href="https://docs.databricks.com/en/ingestion/auto-loader/schema.html#how-does-auto-loader-schema-evolution-work" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;Schema Evolution&lt;/STRONG&gt;&lt;/A&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL merges schemas as new columns arrive via schema evolution mode. New schema JSON will be updated and stored as a new version in the specified schema repository location.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val evolveAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.inferColumnTypes", true)
.option("cloudFiles.schemaEvolutionMode", "addNewColumns") // schema evolution
.load(rawBasePath + "/*.json")
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
evolveAlDf.printSchema // original schema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(evolveAlDf.limit(10)) // # stream will fail&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;AL purposely fails the stream with &lt;/SPAN&gt;&lt;SPAN&gt;an &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;UnknownFieldException&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; error &lt;/SPAN&gt;&lt;SPAN&gt;when it detects a schema change via dynamic schema inference. The updated schema instance is created as a new version and metadata file in the schema repository location, and will be used against the input data after restarting the stream.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val evolveAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.inferColumnTypes", true)
.option("cloudFiles.schemaHints", "humidity DOUBLE")
.option("cloudFiles.schemaEvolutionMode", "addNewColumns") // schema evolution
.load(rawBasePath + "/*.json")
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
evolveAlDf.printSchema // evolved schema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: double (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- device_serial_number_device_type: string (nullable = true)
 |-- latitude: double (nullable = true)
 |-- location: struct (nullable = true)
 |    |-- cca2: string (nullable = true)
 |    |-- cca3: string (nullable = true)
 |    |-- cn: string (nullable = true)
 |-- longitude: double (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;AL has evolved the schema to merge the newly acquired data fields.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(evolveAlDf.where("device_serial_number_device_type is not null").limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture12.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15025iB6D10D0320FD2F05/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture12.png" alt="Picture12.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;The newly merged schema transformed by AL is stored in the original schema repository path as version 1 along with the base version 0 schema. This history is valuable for tracking changes to schema over time, as well as, quickly retrieving DDL on the fly for schema enforcement.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H5&gt;&lt;SPAN&gt;Schema Repository&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(dbutils.fs.ls(repoSchemaPath + "/_schemas"))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture13.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15026i7BF58F96BEE11D00/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture13.png" alt="Picture13.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H5&gt;&lt;SPAN&gt;Schema Metadata&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(spark.read.json(repoSchemaPath + "/_schemas"))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture14.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15027i5FCFFE215B8223A4/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture14.png" alt="Picture14.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Schema evolution can be a messy problem if frequent. With AL and Delta Lake it becomes easier and simpler to manage. Adding new columns is relatively straightforward as AL combined with Delta Lake uses schema evolution to append them to the existing schema. Note, the values for these columns will be NULL for data already processed. The greater challenge occurs when the data types change because there will be a type mismatch against the data already processed. Currently, the ‘safest’ approach is to perform a complete overwrite of the target delta table to refresh all data with the changed data type(s). Depending on the data volume this operation is also relatively straight forward if infrequent. However, if data types are changing daily/weekly then this operation is going to be very costly to re-process large data volumes. This can be an indication that the business needs to improve their data strategy.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Constantly changing schemas can be a sign of a weak data governance strategy and lack of communication with the data business owners. Ideally, organizations should have some kind of SLA for data acquisition and know the expected schema. Raw data stored in the landing zone should also follow some kind of pre-ETL strategy (i.e. ontology, taxonomy, partitioning) for better incremental loading performance into the Lakehouse. Skipping these steps can cause a plethora of data management issues that will negatively impact downstream consumers building data analytics, BI, and AI/ML pipelines &amp;amp; applications. If upstream schema and formatting issues are never addressed, downstream pipelines will consistently break and result in increased cloud storage and compute costs. Garbage in, garbage out.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Example 7: Schema Enforcement&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL validates data against the linked schema version stored in repository location via schema enforcement mode. Schema enforcement is a schema-on-write operation, and &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;only&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; ingested data matching the target Delta Lake schema will be written to output. Any future input schema changes will be ignored, and AL streams will continue working without failure. Schema enforcement is a very powerful feature of AL and Delta Lake. It ensures only clean and trusted data will be inserted into downstream Silver/Gold datasets used for data analytics, BI, and AI/ML pipelines &amp;amp; applications.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val enforceAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.schemaEvolutionMode", "none") // schema enforcement
.schema(jsonSchema)
.load(rawBasePath + "/*.json")
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
enforceAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;Please note the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column is no longer available in this example because schema enforcement has been enabled. However, a &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column can still be configured separately as an AL option if desired via &lt;STRONG&gt;&lt;EM&gt;.option("cloudFiles.rescuedDataColum", "_rescued_data")&lt;/EM&gt;&lt;/STRONG&gt;. In fact, it would be wise to harvest records that do not fit the provided schema in a &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column for tracking purposes. This approach avoids dropping data by quarantining them at the bronze layer! In addition, schema enforcement mode uses the latest schema version in the repository to enforce incoming data. For older versions, set a user-defined schema as explained in Example 4.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(enforceAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture15.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15028i2E1999FF36BC90BD/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture15.png" alt="Picture15.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Conclusion&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;At the end of the day, data issues are inevitable. However, the key is to limit data pollution as much as possible and have methods to detect discrepancies, changes, and history via schema management. Databricks Auto Loader provides many solutions for schema management, as illustrated by the examples in this blog. Having a solidified data governance and landing zone strategy will make ingestion and streaming easier and more efficient for loading data into the Lakehouse. Whether it is simply converting raw JSON data incrementally to the Bronze layer as Delta Lake format, or having a repository to store schema metadata, AL makes your job easier. It acts as an anchor to building a resilient Lakehouse architecture that provides reusable, consistent, reliable, and performant data throughout the Data+AI lifecycle.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Thank you for reading this blog. DBC file notebooks (Spark Scala &amp;amp; Spark Python) with code and zipped sample datasets can be found @ GitHub repo &lt;/SPAN&gt;&lt;A href="https://github.com/grp-db/databricks-technical-blogs/tree/main/1" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;here&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;. &lt;/SPAN&gt;&lt;STRIKE&gt;&lt;I&gt;Disclaimer: Currently at the time of this blog, Databricks Community Edition, does not support Databricks Auto Loader.&lt;/I&gt;&lt;/STRIKE&gt;&lt;/P&gt;
&lt;H5&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/H5&gt;</description>
    <pubDate>Mon, 24 Feb 2025 08:47:37 GMT</pubDate>
    <dc:creator>grpeternel</dc:creator>
    <dc:date>2025-02-24T08:47:37Z</dc:date>
    <item>
      <title>Schema Management and Drift Scenarios via Databricks Auto Loader</title>
      <link>https://community.databricks.com/t5/technical-blog/schema-management-and-drift-scenarios-via-databricks-auto-loader/ba-p/63393</link>
      <description>&lt;P&gt;&lt;SPAN&gt;Data lakes notoriously have had challenges with managing incremental data processing at scale without integrating open table storage format frameworks (i.e. Delta Lake, Apache Iceberg, Apache Hudi). In addition, schema management is difficult with schema-less data and schema-on-read methods. With the power of Databricks Lakehouse, Delta Lake and Apache Spark provide the essential technologies integrated with Databricks Auto Loader (AL) to consistently and reliably stream and process raw data formats incrementally, while maintaining stellar performance and data governance.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Auto Loader features&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL is a boost over Spark Structured Streaming, supporting several additional benefits and solutions including:&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Databricks Runtime only Structured Streaming &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;cloudFiles &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;source&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Schema drift, dynamic inference, and evolution support&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Ingests data via JSON, CSV, PARQUET, AVRO, ORC, TEXT, and BINARYFILE input file formats&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Integration with cloud file notification services (i.e. Amazon SQS/SNS)&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;SPAN&gt;Optimizes directory list mode scanning performance to discover new files in cloud storage (i.e. AWS, Azure, GCP, DBFS)&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN&gt;For further information please visit the official &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/en/ingestion/auto-loader/index.html#what-is-auto-loader" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Databricks Auto Loader&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; documentation.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Schema change scenarios&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;In this blog I will showcase a few examples of how AL handles schema management and drift scenarios using a public IoT sample dataset with schema modifications to showcase solutions. Schema 1 will contain an IoT sample dataset schema with all expected columns and expected data types. Schema 2 will contain unexpected changes to the IoT sample dataset schema with new columns and changed data types. The following variables and paths will be used for this demonstration along with &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/en/notebooks/widgets.html#databricks-widgets" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Databricks Widgets&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; to set your username folder.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
dbutils.widgets.text("dbfs_user_dir", "your_user_name") // widget for account email

val userName = dbutils.widgets.get("dbfs_user_dir")
val rawBasePath = s"dbfs:/user/$userName/raw/"
val repoBasePath = s"dbfs:/user/$userName/repo/"

val jsonSchema1Path = rawBasePath + "iot-schema-1.json"
val jsonSchema2Path = rawBasePath + "iot-schema-2.json"
val repoSchemaPath = repoBasePath + "iot-ddl.json"

dbutils.fs.rm(repoSchemaPath, true) // remove schema repo for demos&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;H5&gt;&lt;SPAN&gt;Schema 1&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
spark.read.json(jsonSchema1Path).printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(spark.read.json(jsonSchema1Path).limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;H5&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture3.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15013i6A5AC44F4AADEEC5/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture3.png" alt="Picture3.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/H5&gt;
&lt;H5&gt;&amp;nbsp;&lt;SPAN&gt;Schema 2&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="height: 102px; border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%" height="102px"&gt;&lt;LI-CODE lang="java"&gt;%scala
// NEW =&amp;gt; device_serial_number_device_type, location
spark.read.json(jsonSchema2Path).printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number_device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: double (nullable = true)
 |-- ip: string (nullable = true)
 |-- latitude: double (nullable = true)
 |-- location: struct (nullable = true)
 |    |-- cca2: string (nullable = true)
 |    |-- cca3: string (nullable = true)
 |    |-- cn: string (nullable = true)
 |-- longitude: double (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(spark.read.json(jsonSchema2Path).limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture4.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15014i311B367800AB6E88/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture4.png" alt="Picture4.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;SPAN&gt;&lt;STRONG&gt;&lt;STRONG style="color: inherit; font-size: 24px;"&gt;Example 1: Schema Tracking/Management&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL tracks schema versions, metadata, and changes to input data over time via specifying a location directory path. These features are incredibly useful for tracking history of data lineage, and are tightly integrated with the Delta Lake transactional log &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/delta-describe-history.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;DESCRIBE HISTORY&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; and time travel.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val rawAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath) // schema history tracking
.load(jsonSchema1Path)
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
rawAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: string (nullable = true)
 |-- c02_level: string (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: string (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: string (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: string (nullable = true)
 |-- humidity: string (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: string (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(rawAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;By default (for JSON, CSV, and XML file format) AL infers all column data types as strings, including nested fields.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture5.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15015i80CBF8BC27CFD9B2/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture5.png" alt="Picture5.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Here is the directory structure where AL stores schema versions. These files can be read via &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/en/reference/spark.html#apache-spark-api-reference" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Spark DataFrame API&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H5&gt;&lt;SPAN&gt;Schema Repository&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(dbutils.fs.ls(repoSchemaPath + "/_schemas"))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture6.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15022i44090F67D6FFCDF4/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture6.png" alt="Picture6.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H5&gt;Schema Metadata&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(spark.read.json(repoSchemaPath + "/_schemas"))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture7.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15029iFBDFE57AC97D605B/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture7.png" alt="Picture7.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Example 2: &lt;/STRONG&gt;&lt;A href="https://docs.databricks.com/en/ingestion/auto-loader/schema.html#override-schema-inference-with-schema-hints" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;Schema Hints&lt;/STRONG&gt;&lt;/A&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL provides hint logic using SQL DDL syntax to enforce and override dynamic schema inference on known single data types, as well as semi-structured complex data types.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val hintAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.schemaHints", "coordinates STRUCT&amp;lt;latitude:DOUBLE, longitude:DOUBLE&amp;gt;, humidity LONG, temp DOUBLE") // schema ddl hints
.load(jsonSchema1Path)
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
hintAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: string (nullable = true)
 |-- c02_level: string (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: string (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: string (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;The schema hints specified in the AL options perform the data type mappings on the respective columns. Hints are useful for applying schema enforcement on portions of the schema where data types are known while in tandem with dynamic schema inference covered in Example 3.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(hintAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;STRONG&gt;&lt;STRONG style="color: inherit; font-size: 24px;"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture8.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15024i3B9ECF20B0F79CDA/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture8.png" alt="Picture8.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;&lt;STRONG&gt;&lt;STRONG style="color: inherit; font-size: 24px;"&gt;Example 3: &lt;/STRONG&gt;&lt;A style="font-size: 24px; font-weight: bold; background-color: #ffffff;" href="https://docs.databricks.com/en/ingestion/auto-loader/schema.html#how-does-auto-loader-schema-inference-work" target="_blank" rel="noopener"&gt;Dynamic Schema Inference&lt;/A&gt;&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL dynamically searches a sample of the dataset to determine nested structure. This avoids costly and slow full dataset scans to infer schema. The following configurations are available to adjust the amount of sample data used on read to discover initial schema:&lt;/SPAN&gt;&lt;/P&gt;
&lt;OL&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;&lt;I&gt;spark.databricks.cloudFiles.schemaInference.sampleSize.numBytes &lt;/I&gt;&lt;/STRONG&gt;&lt;SPAN&gt;(default 50 GB)&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI style="font-weight: 400;" aria-level="1"&gt;&lt;STRONG&gt;&lt;I&gt;spark.databricks.cloudFiles.schemaInference.sampleSize.numFiles &lt;/I&gt;&lt;/STRONG&gt;&lt;SPAN&gt;(default 1000 files)&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val inferAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.inferColumnTypes", true) // schema inference
.load(jsonSchema1Path)
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
inferAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;AL saves the initial schema to the schema location path provided. This schema serves as the base version for the stream during incremental processing. Dynamic schema inference is an automated approach to applying schema changes over time.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(inferAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture9.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15019iF32FDE69E0CE08F9/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture9.png" alt="Picture9.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;&lt;STRONG style="color: inherit; font-size: 24px;"&gt;Example 4: Static User-Defined Schema&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL also supports static custom schemas just like Spark Structured Streaming. This eliminates the need for dynamic schema-on-read inference scans, which trigger additional Spark jobs and schema versions. The schema can be retrieved as a DDL string or a JSON payload.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H5&gt;&lt;SPAN&gt;DDL&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
inferAlDf.schema.toDDL&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;String = alarm_status STRING,battery_level BIGINT,c02_level BIGINT,cca2 STRING,cca3 STRING,cn STRING,coordinates STRUCT&amp;lt;latitude: DOUBLE, longitude: DOUBLE&amp;gt;,date STRING,device_id BIGINT,device_serial_number STRING,device_type STRING,epoch_time_miliseconds BIGINT,humidity BIGINT,ip STRING,scale STRING,temp DOUBLE,timestamp STRING,_rescued_data STRING&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;H5&gt;&lt;SPAN&gt;JSON&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
spark.read.json(repoSchemaPath + "/_schemas").select("dataSchemaJson").where("dataSchemaJson is not null").first()&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;org.apache.spark.sql.Row = [{"type":"struct","fields":[{"name":"alarm_status","type":"string","nullable":true,"metadata":{}},{"name":"battery_level","type":"long","nullable":true,"metadata":{}},{"name":"c02_level","type":"long","nullable":true,"metadata":{}},{"name":"cca2","type":"string","nullable":true,"metadata":{}},{"name":"cca3","type":"string","nullable":true,"metadata":{}},{"name":"cn","type":"string","nullable":true,"metadata":{}},{"name":"coordinates","type":{"type":"struct","fields":[{"name":"latitude","type":"double","nullable":true,"metadata":{}},{"name":"longitude","type":"double","nullable":true,"metadata":{}}]},"nullable":true,"metadata":{}},{"name":"date","type":"string","nullable":true,"metadata":{}},{"name":"device_id","type":"long","nullable":true,"metadata":{}},{"name":"device_serial_number","type":"string","nullable":true,"metadata":{}},{"name":"device_type","type":"string","nullable":true,"metadata":{}},{"name":"epoch_time_miliseconds","type":"long","nullable":true,"metadata":{}},{"name":"humidity","type":"long","nullable":true,"metadata":{}},{"name":"ip","type":"string","nullable":true,"metadata":{}},{"name":"scale","type":"string","nullable":true,"metadata":{}},{"name":"temp","type":"double","nullable":true,"metadata":{}},{"name":"timestamp","type":"string","nullable":true,"metadata":{}}]}]&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;Here’s an example of how to generate a user-defined &lt;/SPAN&gt;&lt;A href="https://api-docs.databricks.com/scala/spark/latest/org/apache/spark/sql/types/StructType$.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;StructType (Scala)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; | &lt;/SPAN&gt;&lt;A href="https://api-docs.databricks.com/python/pyspark/latest/pyspark.sql/api/pyspark.sql.types.StructType.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;StructType (Python)&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; via DDL dataframe command or JSON queried from AL schema repository.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
import org.apache.spark.sql.types.{DataType, StructType}

val ddl = """alarm_status STRING,battery_level BIGINT,c02_level BIGINT,cca2 STRING,cca3 STRING,cn STRING,coordinates STRUCT&amp;lt;latitude: DOUBLE, longitude: DOUBLE&amp;gt;,date STRING,device_id BIGINT,device_serial_number STRING,device_type STRING,epoch_time_miliseconds BIGINT,humidity BIGINT,ip STRING,scale STRING,temp DOUBLE,timestamp STRING,_rescued_data STRING"""

val ddlSchema = StructType.fromDDL(ddl)

val json = """{"type":"struct","fields":[{"name":"alarm_status","type":"string","nullable":true,"metadata":{}},{"name":"battery_level","type":"long","nullable":true,"metadata":{}},{"name":"c02_level","type":"long","nullable":true,"metadata":{}},{"name":"cca2","type":"string","nullable":true,"metadata":{}},{"name":"cca3","type":"string","nullable":true,"metadata":{}},{"name":"cn","type":"string","nullable":true,"metadata":{}},{"name":"coordinates","type":{"type":"struct","fields":[{"name":"latitude","type":"double","nullable":true,"metadata":{}},{"name":"longitude","type":"double","nullable":true,"metadata":{}}]},"nullable":true,"metadata":{}},{"name":"date","type":"string","nullable":true,"metadata":{}},{"name":"device_id","type":"long","nullable":true,"metadata":{}},{"name":"device_serial_number","type":"string","nullable":true,"metadata":{}},{"name":"device_type","type":"string","nullable":true,"metadata":{}},{"name":"epoch_time_miliseconds","type":"long","nullable":true,"metadata":{}},{"name":"humidity","type":"long","nullable":true,"metadata":{}},{"name":"ip","type":"string","nullable":true,"metadata":{}},{"name":"scale","type":"string","nullable":true,"metadata":{}},{"name":"temp","type":"double","nullable":true,"metadata":{}},{"name":"timestamp","type":"string","nullable":true,"metadata":{}}]}"""

val jsonSchema = DataType.fromJson(json).asInstanceOf[StructType]&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val schemaAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.schema(jsonSchema) // schema structtype definition
.load(jsonSchema1Path)
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
schemaAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;Passing in the schema definition will enforce the stream. AL also provides a schema enforcement option achieving basically the same results as providing a static &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;StructType&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; schema-on-read. This method will be covered in Example 7.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(schemaAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture10.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15020i36A0A7230A002E1A/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture10.png" alt="Picture10.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Example 5: &lt;/STRONG&gt;&lt;A href="https://docs.databricks.com/en/ingestion/auto-loader/schema.html#what-is-the-rescued-data-column" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;Schema Drift&lt;/STRONG&gt;&lt;/A&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL stores new columns and data types via the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column. This column captures schema changes-on-read. The stream &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;does not&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; fail when schema and data type mismatches are discovered. This is a very impressive feature!&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val driftAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.inferColumnTypes", true)
.option("cloudFiles.schemaEvolutionMode", "rescue") // schema drift tracking
.load(rawBasePath + "/*.json")
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
driftAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;The &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column preserves schema drift such as newly appended columns and or different data types via a JSON string payload. This payload can be parsed via Spark DataFrame or DataSet APIs to analyze schema drift scenarios. The source file path for each individual row is also available in the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column to investigate the root cause.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%" height="636px"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(driftAlDf.where("_rescued_data is not null").limit(10))&lt;/LI-CODE&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture11.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15021i44CADBB306DEEB05/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture11.png" alt="Picture11.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;H2&gt;&lt;STRONG&gt;Example 6: &lt;/STRONG&gt;&lt;A href="https://docs.databricks.com/en/ingestion/auto-loader/schema.html#how-does-auto-loader-schema-evolution-work" target="_blank" rel="noopener"&gt;&lt;STRONG&gt;Schema Evolution&lt;/STRONG&gt;&lt;/A&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL merges schemas as new columns arrive via schema evolution mode. New schema JSON will be updated and stored as a new version in the specified schema repository location.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val evolveAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.inferColumnTypes", true)
.option("cloudFiles.schemaEvolutionMode", "addNewColumns") // schema evolution
.load(rawBasePath + "/*.json")
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
evolveAlDf.printSchema // original schema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(evolveAlDf.limit(10)) // # stream will fail&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;AL purposely fails the stream with &lt;/SPAN&gt;&lt;SPAN&gt;an &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;UnknownFieldException&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; error &lt;/SPAN&gt;&lt;SPAN&gt;when it detects a schema change via dynamic schema inference. The updated schema instance is created as a new version and metadata file in the schema repository location, and will be used against the input data after restarting the stream.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val evolveAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.inferColumnTypes", true)
.option("cloudFiles.schemaHints", "humidity DOUBLE")
.option("cloudFiles.schemaEvolutionMode", "addNewColumns") // schema evolution
.load(rawBasePath + "/*.json")
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
evolveAlDf.printSchema // evolved schema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: double (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)
 |-- device_serial_number_device_type: string (nullable = true)
 |-- latitude: double (nullable = true)
 |-- location: struct (nullable = true)
 |    |-- cca2: string (nullable = true)
 |    |-- cca3: string (nullable = true)
 |    |-- cn: string (nullable = true)
 |-- longitude: double (nullable = true)
 |-- _rescued_data: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;AL has evolved the schema to merge the newly acquired data fields.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(evolveAlDf.where("device_serial_number_device_type is not null").limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture12.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15025iB6D10D0320FD2F05/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture12.png" alt="Picture12.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;The newly merged schema transformed by AL is stored in the original schema repository path as version 1 along with the base version 0 schema. This history is valuable for tracking changes to schema over time, as well as, quickly retrieving DDL on the fly for schema enforcement.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H5&gt;&lt;SPAN&gt;Schema Repository&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(dbutils.fs.ls(repoSchemaPath + "/_schemas"))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture13.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15026i7BF58F96BEE11D00/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture13.png" alt="Picture13.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H5&gt;&lt;SPAN&gt;Schema Metadata&lt;/SPAN&gt;&lt;/H5&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(spark.read.json(repoSchemaPath + "/_schemas"))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture14.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15027i5FCFFE215B8223A4/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture14.png" alt="Picture14.png" /&gt;&lt;/span&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Schema evolution can be a messy problem if frequent. With AL and Delta Lake it becomes easier and simpler to manage. Adding new columns is relatively straightforward as AL combined with Delta Lake uses schema evolution to append them to the existing schema. Note, the values for these columns will be NULL for data already processed. The greater challenge occurs when the data types change because there will be a type mismatch against the data already processed. Currently, the ‘safest’ approach is to perform a complete overwrite of the target delta table to refresh all data with the changed data type(s). Depending on the data volume this operation is also relatively straight forward if infrequent. However, if data types are changing daily/weekly then this operation is going to be very costly to re-process large data volumes. This can be an indication that the business needs to improve their data strategy.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Constantly changing schemas can be a sign of a weak data governance strategy and lack of communication with the data business owners. Ideally, organizations should have some kind of SLA for data acquisition and know the expected schema. Raw data stored in the landing zone should also follow some kind of pre-ETL strategy (i.e. ontology, taxonomy, partitioning) for better incremental loading performance into the Lakehouse. Skipping these steps can cause a plethora of data management issues that will negatively impact downstream consumers building data analytics, BI, and AI/ML pipelines &amp;amp; applications. If upstream schema and formatting issues are never addressed, downstream pipelines will consistently break and result in increased cloud storage and compute costs. Garbage in, garbage out.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Example 7: Schema Enforcement&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;AL validates data against the linked schema version stored in repository location via schema enforcement mode. Schema enforcement is a schema-on-write operation, and &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;only&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; ingested data matching the target Delta Lake schema will be written to output. Any future input schema changes will be ignored, and AL streams will continue working without failure. Schema enforcement is a very powerful feature of AL and Delta Lake. It ensures only clean and trusted data will be inserted into downstream Silver/Gold datasets used for data analytics, BI, and AI/ML pipelines &amp;amp; applications.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
val enforceAlDf = (spark
.readStream.format("cloudfiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", repoSchemaPath)
.option("cloudFiles.schemaEvolutionMode", "none") // schema enforcement
.schema(jsonSchema)
.load(rawBasePath + "/*.json")
)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
enforceAlDf.printSchema&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;root
 |-- alarm_status: string (nullable = true)
 |-- battery_level: long (nullable = true)
 |-- c02_level: long (nullable = true)
 |-- cca2: string (nullable = true)
 |-- cca3: string (nullable = true)
 |-- cn: string (nullable = true)
 |-- coordinates: struct (nullable = true)
 |    |-- latitude: double (nullable = true)
 |    |-- longitude: double (nullable = true)
 |-- date: string (nullable = true)
 |-- device_id: long (nullable = true)
 |-- device_serial_number: string (nullable = true)
 |-- device_type: string (nullable = true)
 |-- epoch_time_miliseconds: long (nullable = true)
 |-- humidity: long (nullable = true)
 |-- ip: string (nullable = true)
 |-- scale: string (nullable = true)
 |-- temp: double (nullable = true)
 |-- timestamp: string (nullable = true)&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;SPAN&gt;Please note the &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column is no longer available in this example because schema enforcement has been enabled. However, a &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column can still be configured separately as an AL option if desired via &lt;STRONG&gt;&lt;EM&gt;.option("cloudFiles.rescuedDataColum", "_rescued_data")&lt;/EM&gt;&lt;/STRONG&gt;. In fact, it would be wise to harvest records that do not fit the provided schema in a &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;rescue&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column for tracking purposes. This approach avoids dropping data by quarantining them at the bronze layer! In addition, schema enforcement mode uses the latest schema version in the repository to enforce incoming data. For older versions, set a user-defined schema as explained in Example 4.&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE style="border-style: hidden; width: 100%;" border="1" width="100%"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width="100%"&gt;&lt;LI-CODE lang="java"&gt;%scala
display(enforceAlDf.limit(10))&lt;/LI-CODE&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Picture15.png" style="width: 936px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/15028i2E1999FF36BC90BD/image-size/large?v=v2&amp;amp;px=999" role="button" title="Picture15.png" alt="Picture15.png" /&gt;&lt;/span&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;&lt;STRONG&gt;Conclusion&lt;/STRONG&gt;&lt;/H2&gt;
&lt;P&gt;&lt;SPAN&gt;At the end of the day, data issues are inevitable. However, the key is to limit data pollution as much as possible and have methods to detect discrepancies, changes, and history via schema management. Databricks Auto Loader provides many solutions for schema management, as illustrated by the examples in this blog. Having a solidified data governance and landing zone strategy will make ingestion and streaming easier and more efficient for loading data into the Lakehouse. Whether it is simply converting raw JSON data incrementally to the Bronze layer as Delta Lake format, or having a repository to store schema metadata, AL makes your job easier. It acts as an anchor to building a resilient Lakehouse architecture that provides reusable, consistent, reliable, and performant data throughout the Data+AI lifecycle.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Thank you for reading this blog. DBC file notebooks (Spark Scala &amp;amp; Spark Python) with code and zipped sample datasets can be found @ GitHub repo &lt;/SPAN&gt;&lt;A href="https://github.com/grp-db/databricks-technical-blogs/tree/main/1" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;here&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;. &lt;/SPAN&gt;&lt;STRIKE&gt;&lt;I&gt;Disclaimer: Currently at the time of this blog, Databricks Community Edition, does not support Databricks Auto Loader.&lt;/I&gt;&lt;/STRIKE&gt;&lt;/P&gt;
&lt;H5&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/H5&gt;</description>
      <pubDate>Mon, 24 Feb 2025 08:47:37 GMT</pubDate>
      <guid>https://community.databricks.com/t5/technical-blog/schema-management-and-drift-scenarios-via-databricks-auto-loader/ba-p/63393</guid>
      <dc:creator>grpeternel</dc:creator>
      <dc:date>2025-02-24T08:47:37Z</dc:date>
    </item>
  </channel>
</rss>

