SQLAlchemy Values and SQLite

2026-07-15
⍝

Originally written back in 2022, while trying to add integrations tests against SQLite to an application that uses SQL Server in production.

It wasn’t my idea and it failed but was a good learning experience, so I am publishing this draft with its last section missing. I have since essentially stopped using SQLAlchemy constructs to build complex queries preferring to use raw SQL, as those complex queries are unlikely to be portable between DBMS1.

With version 1.4, SQLAlchemy added a new Values type to represent the VALUES clause. This is very useful for those SQL implementation that have VALUES as first class citizen like Microsoft SQL Server.

The simplest statement we can build is values constructor in a select

from sqlalchemy import column, select, values

labels = ["col1", "col2"]
data = [(1, "a"), (2, "b"), (3, "c")]

select(
    values(column("col1"), column("col2"), name="test")
    .data([(1, "a"), (2, "b"), (3, "c")])
)

which compiles to2

SELECT test.col1, test.col2
FROM (VALUES (1, 'a'), (2, 'b'), (3, 'c')) AS test (col1, col2)

But in SQLite, VALUES is not a full fledged table constructor, it is only part of other statements like SELECT or INSERT and does not allow aliasing with AS.

First attempt WITH CTE

My first attempt was to replace this statement with a Common Table Expression (CTE), the original ad-hoc table constructor.

WITH test (col1, col2)
AS (VALUES (1, 'a'), (2, 'b'), (3, 'c'))
SELECT test.col1, test.col2 FROM test

SQLAlchemy also offers a cte constructor.

from sqlalchemy import column, select, values

select(
    values(column("col1"), column("col2"), name="test")
    .data([(1, "a"), (2, "b"), (3, "c")])
    .cte()
)

But when I executed this, I only managed to raise the following exception.

AttributeError: 'Values' object has no attribute 'cte'

That’s because Values is a subclass of FromClause not SelectBase (or HasCTE). So we could wrap it in a select, but that takes us right back to the original select+values issue above…

Second attempt UNION ALL

So, second attempt, replace the statement with several UNION ALL in a subquery. While not as elegant, it is standard SQL and should also work with all DBMS implementations.

SELECT test.col1, test.col2
FROM (
	SELECT 1 AS col1, 'a' AS col2
	UNION ALL SELECT 2, 'b'
	UNION ALL SELECT 3, 'c'
) AS test

Which is again easy to build with the union_all constructor, the SelectBase.subquery method and two comprehensions (just one if you’re passing only one column)

from sqlalchemy import column, literal, select, union_all, values

labels = ["col1", "col2"]
data = [(1, "a"), (2, "b"), (3, "c")]

select(
    union_all(
        *(
            select(*(literal(v).label(l) for l, v in zip(labels, datum)))
            for datum in data
        )
    ).subquery("test")
)

and compiles to2

SELECT test.col1, test.col2
FROM (
	SELECT 1 AS col1, 'a' AS col2
	UNION ALL SELECT 2 AS col1, 'b' AS col2
	UNION ALL SELECT 3 AS col1, 'c' AS col2
) AS test
‼

It’s at this point our story stops, after poking around with this for not too long but still too long, and halfway through writing my own custom SQL construct, I stumbled on this comment by @zzzeek which proposes a custom SQL construct that solves this precise issue.


  1. After the pains of migrating a ORM heavy application from SQL Server to PostgreSQL requiring 0 loss 0 downtime, trust me on this. ↩︎

  2. compiled with compile_kwargs={"literal_binds": True} to render the literals. ↩︎ ↩︎