DDL features in SQLAlchemy¶
Snowflake SQLAlchemy extends SQLAlchemy’s DDL support with Snowflake table types and table options. The examples on this page assume
an engine created as described in Connecting to Snowflake with SQLAlchemy.
Snowflake table classes and options¶
Import these table classes from snowflake.sqlalchemy. Each one is a subclass of SQLAlchemy’s Table, so you can use it in SQLAlchemy Core
directly, or in a declarative ORM model by returning it from __table_cls__.
| Class | Statement generated | Options |
|---|---|---|
SnowflakeTable | CREATE TABLE | cluster_by, row_access_policy, as_query |
HybridTable | CREATE HYBRID TABLE | Requires a primary key. Supports Index. |
IcebergTable | CREATE ICEBERG TABLE | external_volume, base_location, as_query |
DynamicTable | CREATE DYNAMIC TABLE | target_lag, warehouse, refresh_mode, as_query |
You can pass option values as plain Python values, or as explicit option objects: AsQueryOption, ClusterByOption, IdentifierOption,
KeywordOption, LiteralOption, RowAccessPolicyOption, and TargetLagOption. Use the TimeUnit and SnowflakeKeyword enumerations
for time units and keywords such as DOWNSTREAM or FULL.
Note
Don’t pass Snowflake table options as snowflake_* keyword arguments on a plain Table. The only such argument that the dialect accepts is
snowflake_clusterby. Other snowflake_* table arguments raise an error when the CREATE TABLE statement is compiled.
Creating a table from a query¶
Pass a select() construct (or a SQL string) as as_query to generate CREATE TABLE ... AS SELECT. When you use select(), you don’t
need to define the columns:
CLUSTER BY support¶
Snowflake SQLAlchemy supports the CLUSTER BY clause for organizing table data. For a SQLAlchemy Core Table, use the snowflake_clusterby
argument. It accepts column names and text() expressions:
CLUSTER BY doesn’t change query semantics. You query a clustered table the same way as any other table.
Using CLUSTER BY with ORM (declarative)¶
For declarative models, return a SnowflakeTable from __table_cls__ and pass cluster_by in __table_args__:
Lowercase column names in CLUSTER BY¶
To cluster by a case-sensitive (lowercase) column, wrap the name in quoted_name(..., True) both in the column definition and in the
clustering key. Otherwise, Snowflake resolves the name as uppercase:
Row access policy support¶
Snowflake SQLAlchemy can attach an existing row access policy to a table when it creates the table. Use
SnowflakeTable and pass row_access_policy as a (policy_name, [columns]) tuple:
The policy must already exist in Snowflake. Snowflake SQLAlchemy doesn’t create it.
The policy name can be fully qualified. A string is parsed as [[database.]schema.]name. You can also pass a RowAccessPolicyOption
with an explicit FQN:
RELY constraint property¶
Snowflake doesn’t enforce PRIMARY KEY, UNIQUE, or FOREIGN KEY constraints. The RELY property tells the query optimizer that it can
trust a constraint when it rewrites queries. Set snowflake_rely=True on each constraint that should have the property:
Caution
Snowflake doesn’t verify RELY constraints against the data. If the data violates a RELY constraint (for example, it contains duplicate
keys or orphaned foreign keys), queries can return incorrect results. Only set RELY on constraints that your data loading process
guarantees.
Auto-incrementing primary keys¶
By default, an Integer primary key column renders as AUTOINCREMENT. To generate primary key values for ORM models, use a Sequence.
The ORM fetches the next value from the sequence before each insert, so the new object’s primary key is populated:
Don’t use Identity() on a primary key column with the ORM. For details, see
Identity columns as primary keys.
Iceberg table with Snowflake catalog support¶
Snowflake SQLAlchemy supports creating Apache Iceberg™ tables that use Snowflake as the catalog. Use
IcebergTable with an existing external volume:
Snowflake SQLAlchemy doesn’t validate the options on the client. Snowflake reports an error when the statement runs if, for example, the
external volume doesn’t exist. You can also pass as_query to create the table from a query.
For declarative models, return an IcebergTable from __table_cls__:
The MAP, OBJECT, and ARRAY structured types are intended for Iceberg tables. See
Structured data types.
Hybrid table support¶
Use HybridTable to create a hybrid table. A hybrid table requires a primary key, and it’s the only table
type that supports indexes. Snowflake SQLAlchemy creates each Index with a separate CREATE INDEX statement after it creates the table:
For declarative models, return a HybridTable from __table_cls__:
Dynamic tables support¶
Use DynamicTable to create a dynamic table with SQLAlchemy Core. Specify the target lag as a
(value, TimeUnit) tuple or as SnowflakeKeyword.DOWNSTREAM, and specify the warehouse that refreshes the table:
When as_query is a select() construct, you don’t need to define the columns:
Dynamic tables have these limitations in Snowflake SQLAlchemy:
- When
as_queryis a string, you must define the columns explicitly. - A dynamic table can’t have a primary key or foreign key. Defining one raises an error. Because declarative ORM models require a primary key, you can’t define a dynamic table with the declarative ORM.
- You can’t insert data directly into a dynamic table.
Stages and file formats¶
Use CreateStage to create an external stage that points to a cloud storage location. Describe the stage with ExternalStage, and the
location with AWSBucket, AzureContainer, or GCSBucket:
To create a named file format, use CreateFileFormat. For an example, see
Creating a named file format.
Notes¶
- Always test DDL features in a development environment before deploying to production.
- Some DDL features require specific Snowflake editions or privileges.
- For details about the SQL statements, see CREATE TABLE and the other linked reference pages.