Schema reflection in SQLAlchemy¶
Auto-increment behavior¶
Snowflake SQLAlchemy supports two ways to generate primary key values automatically:
AUTOINCREMENTcolumns. The dialect renders a column asAUTOINCREMENTwhen it’s the table’s autoincrement column and has no other server default. SQLAlchemy’s standardautoincrement=Truecontrols this.Sequenceobjects. SQLAlchemy fetches the next value with a separateSELECT <sequence>.nextvalbefore theINSERT. This is the recommended approach for ORM primary keys, because the generated value is available to the ORM after the insert.
For why Identity() doesn’t work as an ORM primary key, see Known limitations: Identity columns as primary keys.
Object name case handling¶
Snowflake stores case-insensitive object names in uppercase. SQLAlchemy treats lowercase names as case-insensitive. Snowflake SQLAlchemy converts between the two during reflection and DDL, so use lowercase names on the SQLAlchemy side:
- Lowercase names are case-insensitive (for example,
my_tablerefers toMY_TABLE). - Uppercase and mixed-case names are treated as case-sensitive and are quoted. Use them only for identifiers that were created with quotes, such as
"MixedCase".
By default, a reflected mixed-case name such as "MixedCase" is returned as a plain str. If your code needs to distinguish case-sensitive names by type, set case_sensitive_identifiers=True. Reflected case-sensitive names are then returned as quoted_name(..., True):
Index support¶
Indexes are supported only for Hybrid Tables. Creating an index on a standard table raises an error.
Single-column indexes¶
Create a single-column index by setting index=True on the column, or by defining an Index object:
Multi-column indexes¶
Create a multi-column (composite) index by passing several columns to Index:
Snowflake builds only one index on a table at a time. If a table defines more than one index, metadata.create_all() can fail with Another index is being built on table. In that case, create the additional indexes after the first index is built.
Inspector methods¶
In addition to columns and constraints, the inspector supports the following methods:
get_table_names(),get_view_names(),get_temp_table_names(),get_sequence_names(),get_schema_names()get_view_definition()get_table_comment()get_indexes()andget_multi_indexes(), which return indexes for Hybrid Tables.get_check_constraints(), which returns an empty list because Snowflake doesn’t support check constraints
Reflected types include Snowflake’s resolved default sizes, such as VARCHAR(16777216) instead of VARCHAR. The type objects work the same way, but their str() output differs, so use isinstance() rather than string comparison:
The dialect’s reflection methods use SQLAlchemy’s @reflection.cache mechanism, so repeated calls with the same arguments on one Inspector object can reuse earlier results.
Cache column metadata¶
Column metadata is cached during reflection for performance. The caching behavior differs between single-table and multi-table reflection:
- Bulk reflection (
MetaData.reflect()): One schema-wide query per metadata type, cached for all tables - Single-table inspection (
inspector.get_columns()): OneDESC TABLEquery per table
Single-table vs multi-table reflection performance¶
For best performance, choose the appropriate reflection method:
Snowflake SQLAlchemy automatically routes each to the appropriate query path:
MetaData.reflect()andautoload_with=use bulk schema queriesinspector.get_columns()uses singleDESC TABLEqueries
Cross-database reflection¶
Reflect tables from multiple databases by specifying the schema parameter:
Reflecting large schemas (10,000+ objects)¶
Snowflake SHOW commands return at most 10,000 rows per call. Snowflake SQLAlchemy automatically pages through the object-listing commands, so the following methods work regardless of the number of objects, and so do MetaData.reflect() and autoload_with=, which call them:
get_table_names()(SHOW TABLES)get_view_names()(SHOW VIEWS)get_sequence_names()(SHOW SEQUENCES)get_schema_names()(SHOW SCHEMAS)get_temp_table_names()
The schema-wide constraint and index commands aren’t paged. When you use bulk reflection (MetaData.reflect()) on a schema with more than 10,000 rows for any of them, the following commands can fail:
SHOW PRIMARY KEYS IN SCHEMASHOW UNIQUE KEYS IN SCHEMASHOW IMPORTED KEYS IN SCHEMASHOW INDEXES IN SCHEMA
Reflecting a single table’s keys and indexes isn’t affected, because it uses the bounded SHOW ... IN TABLE form. For very large schemas, limit reflection to the tables you need:
When you reflect a subset of tables, also consider resolve_fks=False. By default, SQLAlchemy also reflects every table referenced by a foreign key, which can pull in tables outside your only list.