CertSafari
    Databricks Certified Associate Developer for Apache Spark· Lessons

    Domain 2 · Lesson 9/32

    Spark SQL on Files: read_files, Path Queries and Save Modes

    Execute SQL queries directly on files, including ORC Files, JSON Files, CSV Files, Text Files, and Delta Files, and understand the different save modes for outputting data in Spark SQL.

    15 min read
    3.12% of exam
    11 sources
    Published 3 Oct 2026
    Docs as of 1 Oct 2026

    What you will be able to do

    • Query ORC, JSON, CSV, text and Parquet files from SQL with read_files, and say what schema each format returns
    • Query a file or Delta table by path with format.path syntax, and say what you lose compared with read_files or a temporary view
    • Pick the DataFrameWriter save mode (append, overwrite, error/errorifexists, ignore) that matches what you want when the target already has data
    • Tell INSERT INTO apart from INSERT OVERWRITE when writing results with SQL

    Key concept

    Querying files without a table — Spark SQL can read files where they sit. You name a path and a format, and the files come back as a table you can query, with no table created first. Which method you use, read_files, a format.path reference or a temporary view, controls whether you can also pass a schema and reader options.

    1.read_files: one SQL function for many file formats

    Normally SQL reads from tables. On Databricks you can also run SQL against files in storage, and the main tool for that is the read_files table-valued function. It is available in Databricks SQL and Databricks Runtime 13.3 LTS and above. Pass it a path, plus options given as named parameters (option => value), and it returns the file contents as a table. One function handles most of the formats this objective lists. It also covers Parquet, XML, Avro and raw binary files.

    What read_files returns for each format
    FormatSchema of the result
    TEXTFixed: a single value column of type STRING
    BINARYFILEFixed: path, modificationTime, length, content
    JSON, CSV, XML, PARQUET, AVRO, ORCInferred from the file contents, or given explicitly with the schema option

    The path can point to a single file or to a directory. For a directory, read_files finds every file underneath it recursively unless you supply a glob such as *, ?, [a-z] or {ab,cd} to narrow the search. If you don't give a schema, the function infers one schema that fits all the files it found, and that means reading all of them unless the query has a LIMIT. The notebook and the SQL editor add a LIMIT to SELECT queries automatically when you leave it out. Hive-style directories such as /column_name=column_value/ become partition columns. If a column name appears both in the directory path and inside the data, the directory value is used.

    No. read_files provides a hidden _metadata column with file_path, file_name, file_size, file_modification_time and block details, but SELECT * does not include it. You have to ask for it by name:

    Selecting the hidden _metadata column explicitly (binary file example)sql
    SELECT * EXCEPT (content), _metadata
    FROM read_files('/Volumes/my_catalog/my_schema/my_volume', format => 'binaryFile');

    Checkpoint 1 of 9· Check yourself

    A colleague says SELECT * FROM read_files('/Volumes/c/s/v/logs', format => 'json') already includes the source file name for every row. What is correct?

    Sources1

    2.ORC, JSON, CSV and text files from SQL

    The call looks the same for every format. Only the format value and a few format-specific options change. ORC is a columnar format that uses built-in indexes and statistics to skip data it doesn't need. Querying it takes nothing more than a path and format => 'orc':

    Querying ORC files in placesql
    SELECT * FROM read_files(
      '/Volumes/<catalog>/<schema>/<volume>/reviews_orc',
      format => 'orc'
    )

    JSON is read in single-line mode by default, where each line holds one complete JSON object. When a single record spans several lines, turn on multiLine:

    Reading multi-line JSON with read_filessql
    SELECT * FROM read_files(
      '/Volumes/<catalog>/<schema>/<volume>/reviews_json',
      format => 'json',
      multiLine => true)

    For CSV, read_files is the method Databricks recommends to SQL users. The usual options are header and a parser mode. PERMISSIVE is the default and fills unparseable fields with nulls. DROPMALFORMED drops the bad lines, and FAILFAST stops the read at the first malformed line. Be careful with partial schemas: CSV has no column names stored in the file, so a schema that doesn't match the file layout puts values into the wrong columns.

    Reading CSV with a header and a strict parser modesql
    SELECT * FROM read_files(
      's3://<bucket>/<path>/<file>.csv',
      format => 'csv',
      header => true,
      mode => 'FAILFAST')

    Text is the simplest format. Each line of each file becomes one row, held in a single value column of type StringType, which suits log parsing and raw ingestion.

    Querying text files: each line becomes one row in the value columnsql
    SELECT * FROM read_files(
      '/Volumes/<catalog>/<schema>/<volume>/review_comments',
      format => 'text'
    )

    Checkpoint 2 of 9· Match them up

    Match each read_files format to how it behaves

    Tap a term, then the definition that fits it.

    Sources2345

    3.Format.path references and temporary views

    read_files is not the only way to query a file from SQL. You can also put the path in the FROM clause, using the format name as a qualifier and the path in backticks. The SQL table-reference docs show this with a CSV file:

    Querying a CSV file by path with format.`path` syntaxsql
    -- Return a data set from a storage location using a credential.
    > SELECT * FROM `csv`.`spreadsheets/data.csv` WITH(CREDENTIAL some_credential);

    The path form is short, but for CSV you pay for that: the docs list two limits when you skip read_files and temporary views. You can't specify data source options, and you can't specify the schema. If you need options and still want a name you can query repeatedly, create a temporary view with USING <format> and put the path and reader options in OPTIONS. The view exists only for the current session and is dropped when the session ends.

    A session-scoped temporary view over a CSV file, with reader optionssql
    CREATE TEMPORARY VIEW diamonds
    USING CSV
    OPTIONS (path "/databricks-datasets/Rdatasets/data-001/csv/ggplot2/diamonds.csv", header "true", mode "FAILFAST");
    
    SELECT * FROM diamonds;

    JSON works the same way with USING json. Databricks still prefers read_files over USING JSON, because read_files lets you specify a schema and more file-processing options.

    Checkpoint 3 of 9· Check yourself

    You need SQL over CSV files with header => true and an explicit schema, and you don't want to create a persistent table. Which approach do the docs recommend?

    Checkpoint 4 of 9· Exam question

    A data engineer has a directory of newline-delimited JSON files at `/mnt/raw/events/` and wants to inspect the data without first creating a table or DataFrame. They run: ```sql SELECT count(*) FROM json.`/mnt/raw/events/` ``` The directory contains 3 files, each holding 100 valid JSON records that share a consistent schema. What does this query return?

    Sources643

    4.Delta files: query the table by its path

    Delta is not in read_files' format list. The list covers JSON, CSV, XML, TEXT, BINARYFILE, PARQUET, AVRO and ORC. Delta is still the format you will query most often, because it is the default on Databricks, while Apache Spark defaults to Parquet. A Delta table directory isn't a loose collection of files. You query it either by its name in the metastore or by its storage path using the delta. prefix:

    The same Delta table queried by name and by pathsql
    SELECT * FROM default.people10m -- query table in the metastore
    SELECT * FROM delta.`/tmp/delta/people10m` -- query table by path

    Checkpoint 5 of 9· Check yourself

    Your Delta table lives at /tmp/delta/people10m and has no entry in the metastore. Which statement queries it by its storage path?

    Time travel on a Delta table addressed by pathsql
    SELECT * FROM delta.`/tmp/delta/people10m` VERSION AS OF 123

    Sources789

    5.Save modes: what happens when the target already exists

    Reading files is half of the objective. The other half is writing results out, and the key question is what Spark does when data is already at the target. DataFrameWriter.mode() controls this, and it accepts four behaviours, with two names for one of them.

    mode(saveMode) values and their behaviour when data already exists
    saveMode valueBehaviour if data exists
    'append'Append to existing data
    'overwrite'Overwrite existing data
    'error' or 'errorifexists'Throw an exception
    'ignore'Silently skip the write

    The reference example writes one row with overwrite, which replaces whatever was in the directory, then adds a second row with append. Reading the directory back returns both rows. Note that the mode is set on the writer before save(), and that format() and mode() can be chained in either order.

    Overwrite, then append, to the same Parquet pathpython
    import tempfile
    with tempfile.TemporaryDirectory(prefix="mode") as d:
        # Overwrite the path with a new Parquet file
        spark.createDataFrame(
            [{"age": 100, "name": "Alice"}]
        ).write.mode("overwrite").format("parquet").save(d)
    
        # Append another DataFrame into the Parquet file
        spark.createDataFrame(
            [{"age": 120, "name": "Sue"}]
        ).write.mode("append").format("parquet").save(d)

    Checkpoint 6 of 9· Fill the gap

    This ORC write should replace whatever is already at the path. Which mode completes it?

    df.write.format("orc").mode(" ? ").save("/Volumes/<catalog>/<schema>/<volume>/reviews_orc")

    Checkpoint 7 of 9· Exam question

    A engineer receives a CSV export where the first line holds column names and numeric columns should not be read as strings. They write: ```python df = spark.read.csv("/mnt/raw/sales.csv") df.createOrReplaceTempView("sales") spark.sql("SELECT avg(amount) FROM sales") ``` The query fails because `amount` is read as a string and the header row `product,amount,region` appears as a data row. Which change fixes both problems?

    SQL has its own version of the append/overwrite choice for tables, set by the keyword after INSERT. INSERT INTO is additive: the new rows go in alongside the existing ones. INSERT OVERWRITE with no PARTITION clause truncates the whole table before inserting the first row. With a partition spec, only the matching partitions are truncated. Dynamic-partition INSERT OVERWRITE requires the target to be a Delta Lake table.

    Checkpoint 8 of 9· Check yourself

    INSERT OVERWRITE sales SELECT * FROM staging_sales runs with no PARTITION clause. What happens to rows in sales that have no match in staging_sales?

    Checkpoint 9 of 9· Exam question

    A team stores historical clickstream data as ORC files under `/mnt/archive/clicks/` and wants a quick row count for a specific `event_date` partition column without creating a table. Which SQL statement queries the ORC files directly and filters on that column?

    Sources1011

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.SELECT * over read_files already includes the source file path and name for each row.Why is that wrong?

      The file-level fields are in a separate _metadata column, which SELECT * leaves out. You have to select _metadata by name.

      Covered in read_files: one SQL function for many file formats

    2. 2.Querying a CSV file directly by path in SQL still lets you set header, delimiter or a schema.Why is that wrong?

      Reading CSV with SQL directly, without read_files or a temporary view, allows neither data source options nor a schema.

      Covered in Format.path references and temporary views

    3. 3.mode('ignore') raises an error when the target already contains data.Why is that wrong?

      ignore skips the write silently. Only 'error' / 'errorifexists' throws an exception when data exists.

      Covered in Save modes: what happens when the target already exists

    4. 4.INSERT OVERWRITE with no PARTITION clause replaces only the rows that match the incoming data.Why is that wrong?

      Without a partition spec, the whole table is truncated before the first row is inserted.

      Covered in Save modes: what happens when the target already exists

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Supports reading JSON, CSV, XML, TEXT, BINARYFILE, PARQUET, AVRO, and ORC file formats.”
      ↩︎ read_files: one SQL function for many file formats
      “read_files discovers all files under the provided directory recursively unless a glob is provided”
      ↩︎ read_files: one SQL function for many file formats
      “read_files attempts to infer a unified schema across the discovered files, which requires reading all the files unless a LIMIT statement is used.”
      ↩︎ read_files: one SQL function for many file formats
      “the value that is read from the partition value is used instead of the data value.”
      ↩︎ read_files: one SQL function for many file formats
      “This column is not included in SELECT * results and must be explicitly selected.”
      ↩︎ Exam trap 1
      “This column is not included in SELECT * results and must be explicitly selected.”
      ↩︎ Checkpoint
      “TEXT: Returns a fixed schema with a single value (STRING) column.”
      ↩︎ Checkpoint
    2. 2.
      “Use read_files to query ORC files directly from cloud storage using SQL without creating a table.”
      ↩︎ ORC, JSON, CSV and text files from SQL
      “Use read_files to query ORC files directly from cloud storage using SQL without creating a table.”
      ↩︎ Key concept
    3. 3.
      “In single-line mode (the default), each line of the output contains one complete JSON object.”
      ↩︎ ORC, JSON, CSV and text files from SQL
      “Databricks recommends using read_files instead of USING JSON because read_files allows the specification of schema and additional file processing options.”
      ↩︎ Format.path references and temporary views
    4. 4.
      “CSV has no column-name metadata, so Spark maps schema fields to columns by position”
      ↩︎ ORC, JSON, CSV and text files from SQL
      “If you use SQL to read CSV data directly without using temporary views or read_files, the following limitations apply:”
      ↩︎ Format.path references and temporary views
      “The view is visible only to the current session and is dropped when the session ends.”
      ↩︎ Format.path references and temporary views
      “You can't specify the schema for the data.”
      ↩︎ Exam trap 2
      “Databricks recommends the read_files table-valued function for SQL users to read CSV files.”
      ↩︎ Checkpoint
    5. 5.
      “The text format reads each line of a text file as a row in a DataFrame with a single value column of type StringType.”
      ↩︎ ORC, JSON, CSV and text files from SQL
    6. 6.
    7. 7.
      “Databricks uses Delta Lake as the default protocol for reading and writing data and tables, whereas Apache Spark uses Parquet.”
      ↩︎ Delta files: query the table by its path
    8. 8.
      “SELECT * FROM delta.`/tmp/delta/people10m` -- query table by path”
      ↩︎ Delta files: query the table by its path
    9. 9.
      “For all data registered as a table, Databricks recommends querying using the table name.”
      ↩︎ Delta files: query the table by its path
    10. 10.
      “Specifies the behavior when data or table already exists.”
      ↩︎ Save modes: what happens when the target already exists
      “'error' or 'errorifexists' (throw an exception if data exists)”
      ↩︎ Exam trap 3
      “'ignore' (silently skip if data exists)”
      ↩︎ Prediction
    11. 11.
      “If you specify INTO all rows inserted are additive to the existing rows.”
      ↩︎ Save modes: what happens when the target already exists
      “Without a partition_spec the table is truncated before inserting the first row.”
      ↩︎ Save modes: what happens when the target already exists
      “Without a partition_spec the table is truncated before inserting the first row.”
      ↩︎ Exam trap 4

    Spotted a mistake, or was something unclear? Tell us.