skip to content

In BigQuery, when should data stay in an external table on Cloud Storage instead of being loaded?

level: middleimportance: should knowfreq 48%

answer

  1. no copy, no load, metadata only
  2. the files were written by someone else
  3. directories are the only pruning you get
  4. read-only, and no clustering
  5. repeat queries pay the parsing every time

basics

~20 s

Keep data external when other engines own the files, the data is queried rarely, or you are still exploring it. Load it into BigQuery once queries repeat, because native storage gives clustering, DML and predictable performance that an external table cannot.

solid answer

~50 s

An external table is a schema definition over files you do not manage — `CREATE EXTERNAL TABLE ... OPTIONS(format='PARQUET', uris=['gs://bucket/path/*'])`. BigQuery reads the objects at query time, so there is no copy and no load step, and other engines can keep writing the same files. BigLake tables add a connection whose service account does the reading, so you can enforce BigQuery-level access control without granting anyone Cloud Storage permissions. `EXTERNAL_QUERY()` does the same job for a live database, pushing SQL down to Cloud SQL or Spanner and returning rows. The cost is performance. There is no clustering, no native columnar layout on Colossus, no DML, and result caching is not something you can count on. Pruning only happens if the files are laid out in Hive-style partition directories and you declare that in the table's options. So: external for landing zones, archives, shared lake data and exploration; loaded for anything queried repeatedly by dashboards.

code

sql · 9 lines
sql
-- Hive-partitioned Parquet in the lake, with pruning and a required filter
CREATE OR REPLACE EXTERNAL TABLE `analytics.events_ext`
WITH PARTITION COLUMNS
OPTIONS (
  format = 'PARQUET',
  uris = ['gs://lake/events/*'],
  hive_partition_uri_prefix = 'gs://lake/events',
  require_hive_partition_filter = true
);

go deeper

for a junior

Know that an external table describes files that stay in Cloud Storage rather than loading them, and that BigQuery reads those files fresh on every query.

for a middle

Be ready to compare external and native storage on layout, pruning, caching and DML, and to explain what Hive-style partition directories buy you.

for a senior

Show the production call: which data earns a copy into native tables, how BigLake connections move access control out of the bucket, and how to keep federated queries from dragging large result sets back.

for a principal

Own the lake-versus-warehouse boundary for the estate — which layer is the system of record, what duplication is acceptable, and how the choice affects governance, cost and engine lock-in.

## What an external table actually is A BigQuery external table stores metadata only — a schema, a format and a set of URIs. Nothing is copied. When a query touches it, BigQuery opens the underlying objects, parses them, and feeds rows into the same execution engine that serves native tables. The files stay wherever they are, owned by whoever writes them. The supported sources include Cloud Storage (CSV, JSON, Avro, Parquet, ORC), Google Drive, and Bigtable. For live relational sources there is federation instead: `EXTERNAL_QUERY('project.region.connection', 'SELECT ...')` sends the inner SQL string to a Cloud SQL or Spanner instance through a connection resource and streams the result back for BigQuery to join against warehouse tables. ```sql SELECT o.id, o.status, c.segment FROM EXTERNAL_QUERY('my-project.us.orders-conn', 'SELECT id, status, customer_id FROM orders') AS o JOIN `analytics.customers` AS c ON c.id = o.customer_id; ``` ## BigLake and the security story A plain external table requires each querying user to hold Cloud Storage read permission on the underlying bucket, which pushes your access control down into object storage where it is hard to reason about. A **BigLake** table routes the read through a connection's service account instead: the service account can read the bucket, users cannot, and access is granted in BigQuery. That is what makes fine-grained controls — row-level and column-level — meaningful over lake files rather than stopping at the bucket boundary. ## Why external is slower Several advantages of native storage simply do not exist for arbitrary files. **Layout.** Loaded data is stored in BigQuery's own columnar format on Colossus, laid out and encoded by the system. External files were written by someone else, in whatever row-group sizes and compression their writer chose. Parquet and ORC at least preserve columnar reads and let BigQuery skip columns; CSV and JSON must be parsed in full, every row, every query. **Metadata.** Native tables carry statistics and clustering information the engine uses to skip data. An external table has whatever the file footers offer and no clustering at all. **Pruning.** The only structural pruning available is directory-level. If the objects are laid out as `gs://lake/events/dt=2024-05-01/...`, declare Hive partitioning in the DDL and the partition keys become real columns that filters can eliminate on. Without that, a filter on date reads every file. Setting `require_hive_partition_filter = true` refuses queries that forget the predicate — a cheap guard against a full-lake scan. **Caching.** Result caching cannot be relied upon for external data, because BigQuery does not control when the files change. **Mutability.** External tables are read-only: no `UPDATE`, no `DELETE`, no streaming inserts. Correcting data means rewriting files with whatever engine owns them. ## The decision Leave data external when at least one of these is true: - Another engine owns the files and must keep writing them; the lake is the system of record. - The data is queried rarely — compliance archives, raw landing zones, one-off backfills. - You are exploring before committing to a schema; an external table costs nothing to define and nothing to drop. - Duplication is unacceptable for governance or size reasons. Load it when: - The same tables back dashboards or scheduled jobs, so the per-query parsing overhead is paid over and over. - You need partitioning plus clustering to make interactive filters cheap. - You need DML, streaming ingestion, or transactional correction of rows. - You want predictable, boring performance that does not depend on how upstream wrote its files. The pragmatic middle is a two-tier design: keep the lake as the durable raw layer with external tables over it, and materialise the curated, frequently-queried subset into native BigQuery tables that are partitioned and clustered for the access pattern. The external tables serve discovery and backfill; the native tables serve the business. ## Cost framing Querying an external table still bills the query, and you additionally pay object-storage costs and any egress the layout implies. There is no scenario where an external table is free; the saving is that you skipped a copy, and the price is that every query re-does work a load would have done once. Formats matter enormously here — the same data as compressed Parquet versus raw CSV is not a small difference in bytes read, it is the difference between reading three columns and reading the whole record.

  • What does declaring Hive partitioning on a BigQuery external table change at query time?
    The path segments such as dt=2024-05-01 become real columns, so a predicate on them eliminates whole directories before any file is opened. Without it, a date filter still reads every object and discards rows afterwards. Setting require_hive_partition_filter = true makes BigQuery reject queries that omit the predicate, which prevents an accidental full-lake scan.
  • How does a BigLake table differ from a plain external table over the same Cloud Storage files?
    A plain external table requires every querying user to hold read permission on the bucket, so access control lives in object storage. A BigLake table reads through a connection's service account instead: the service account holds the bucket permission and users are granted only in BigQuery. That is what makes row-level and column-level controls meaningful over lake files.
  • You must join a small, constantly-changing operational table with a large BigQuery fact table. External table or federated query?
    A federated EXTERNAL_QUERY against the operational database, because it reads the live state without a pipeline and pushes its own filters down. Keep the pushed-down query small and selective — the rows come back over the wire into BigQuery, so a federated scan of a large table is far worse than loading a periodic snapshot.

An external table is a library card for someone else's shelves: you can read the books where they stand, but you cannot re-index them, annotate them, or count on finding them in the same order tomorrow.

saying these in an interview costs you the question

  • Claims external tables perform like native BigQuery storage
  • Expects UPDATE or DELETE to work on an external table
  • Assumes a date filter prunes files with no partition layout declared
  • Thinks external tables avoid query cost because there is no load
  • Treats a federated EXTERNAL_QUERY as safe for huge result sets

context