Operator Reference — SQLAlchemy 2.0.0b1 documentation
Operator Reference
This section details usage of the operators that are available to construct SQL expressions.
These methods are presented in terms of the _sql.Operators
and _sql.ColumnOperators
base classes. The methods are then available on descendants of these classes, including:
_schema.Column
objects_sql.ColumnElement
objects more generally, which are the root of all Core SQL Expression language column-level expressions_orm.InstrumentedAttribute
objects, which are ORM level mapped attributes.
The operators are first introduced in the tutorial sections, including:
- SQLAlchemy 2.0 Tutorial - unified tutorial in 2.0 style
- /orm/tutorial - ORM tutorial in 1.x style
- /core/tutorial - Core tutorial in 1.x style
Comparison Operators
Basic comparisons which apply to many datatypes, including numerics, strings, dates, and many others:
_sql.ColumnOperators.__eq__()
(Python “==
” operator):>>> print(column('x') == 5) x = :x_1
_sql.ColumnOperators.__ne__()
(Python “!=
” operator):>>> print(column('x') != 5) x != :x_1
_sql.ColumnOperators.__gt__()
(Python “>
” operator):>>> print(column('x') > 5) x > :x_1
_sql.ColumnOperators.__lt__()
(Python “<
” operator):>>> print(column('x') < 5) x < :x_1
_sql.ColumnOperators.__ge__()
(Python “>=
” operator):>>> print(column('x') >= 5) x >= :x_1
_sql.ColumnOperators.__le__()
(Python “<=
” operator):>>> print(column('x') <= 5) x <= :x_1
_sql.ColumnOperators.between()
:>>> print(column('x').between(5, 10)) x BETWEEN :x_1 AND :x_2
IN Comparisons
The SQL IN operator is a subject all its own in SQLAlchemy. As the IN operator is usually used against a list of fixed values, SQLAlchemy’s feature of bound parameter coercion makes use of a special form of SQL compilation that renders an interim SQL string for compilation that’s formed into the final list of bound parameters in a second step. In other words, “it just works”.
IN against a list of values
IN is available most typically by passing a list of values to the _sql.ColumnOperators.in_()
method:
>>> print(column('x').in_([1, 2, 3]))
x IN ([POSTCOMPILE_x_1])
The special bound form POSTCOMPILE
is rendered into individual parameters at execution time, illustrated below:
>>> stmt = select(User.id).where(User.id.in_([1, 2, 3]))
>>> result = conn.execute(stmt)
{opensql}SELECT user_account.id
FROM user_account
WHERE user_account.id IN (?, ?, ?)
[...] (1, 2, 3){stop}
Empty IN Expressions
SQLAlchemy produces a mathematically valid result for an empty IN expression by rendering a backend-specific subquery that returns no rows. Again in other words, “it just works”:
>>> stmt = select(User.id).where(User.id.in_([]))
>>> result = conn.execute(stmt)
{opensql}SELECT user_account.id
FROM user_account
WHERE user_account.id IN (SELECT 1 FROM (SELECT 1) WHERE 1!=1)
[...] ()
The “empty set” subquery above generalizes correctly and is also rendered in terms of the IN operator which remains in place.
NOT IN
“NOT IN” is available via the _sql.ColumnOperators.not_in()
operator:
>>> print(column('x').not_in([1, 2, 3]))
(x NOT IN ([POSTCOMPILE_x_1]))
This is typically more easily available by negating with the ~
operator:
>>> print(~column('x').in_([1, 2, 3]))
(x NOT IN ([POSTCOMPILE_x_1]))
Tuple IN Expressions
Comparison of tuples to tuples is common with IN, as among other use cases accommodates for the case when matching rows to a set of potential composite primary key values. The _sql.tuple_()
construct provides the basic building block for tuple comparisons. The _sql.Tuple.in_()
operator then receives a list of tuples:
>>> from sqlalchemy import tuple_
>>> tup = tuple_(column('x', Integer), column('y', Integer))
>>> expr = tup.in_([(1, 2), (3, 4)])
>>> print(expr)
(x, y) IN ([POSTCOMPILE_param_1])
To illustrate the parameters rendered:
>>> tup = tuple_(User.id, Address.id)
>>> stmt = select(User.name).join(Address).where(tup.in_([(1, 1), (2, 2)]))
>>> conn.execute(stmt).all()
{opensql}SELECT user_account.name
FROM user_account JOIN address ON user_account.id = address.user_id
WHERE (user_account.id, address.id) IN (VALUES (?, ?), (?, ?))
[...] (1, 1, 2, 2){stop}
[('spongebob',), ('sandy',)]
Subquery IN
Finally, the _sql.ColumnOperators.in_()
and _sql.ColumnOperators.not_in()
operators work with subqueries. The form provides that a _sql.Select
construct is passed in directly, without any explicit conversion to a named subquery:
>>> print(column('x').in_(select(user_table.c.id)))
x IN (SELECT user_account.id
FROM user_account)
Tuples work as expected:
>>> print(
... tuple_(column('x'), column('y')).in_(
... select(user_table.c.id, address_table.c.id).join(address_table)
... )
... )
(x, y) IN (SELECT user_account.id, address.id
FROM user_account JOIN address ON user_account.id = address.user_id)
Identity Comparisons
These operators involve testing for special SQL values such as NULL
, boolean constants such as true
or false
which some databases support:
_sql.ColumnOperators.is_()
:This operator will provide exactly the SQL for “x IS y”, most often seen as “ IS NULL”. The
NULL
constant is most easily acquired using regular PythonNone
:>>> print(column('x').is_(None)) x IS NULL
SQL NULL is also explicitly available, if needed, using the
_sql.null()
construct:>>> from sqlalchemy import null >>> print(column('x').is_(null())) x IS NULL
The
_sql.ColumnOperators.is_()
operator is automatically invoked when using the_sql.ColumnOperators.__eq__()
overloaded operator, i.e.==
, in conjunction with theNone
or_sql.null()
value. In this way, there’s typically not a need to use_sql.ColumnOperators.is_()
explicitly, paricularly when used with a dynamic value:>>> a = None >>> print(column('x') == a) x IS NULL
Note that the Python
is
operator is not overloaded. Even though Python provides hooks to overload operators such as==
and!=
, it does not provide any way to redefineis
._sql.ColumnOperators.is_not()
:Similar to
_sql.ColumnOperators.is_()
, produces “IS NOT”:>>> print(column('x').is_not(None)) x IS NOT NULL
Is similarly equivalent to
!= None
:>>> print(column('x') != None) x IS NOT NULL
_sql.ColumnOperators.is_distinct_from()
:Produces SQL IS DISTINCT FROM:
>>> print(column('x').is_distinct_from('some value')) x IS DISTINCT FROM :x_1
_sql.ColumnOperators.isnot_distinct_from()
:Produces SQL IS NOT DISTINCT FROM:
>>> print(column('x').isnot_distinct_from('some value')) x IS NOT DISTINCT FROM :x_1
String Comparisons
_sql.ColumnOperators.like()
:>>> print(column('x').like('word')) x LIKE :x_1
_sql.ColumnOperators.ilike()
:Case insensitive LIKE makes use of the SQL
lower()
function on a generic backend. On the PostgreSQL backend it will useILIKE
:>>> print(column('x').ilike('word')) lower(x) LIKE lower(:x_1)
_sql.ColumnOperators.notlike()
:>>> print(column('x').notlike('word')) x NOT LIKE :x_1
_sql.ColumnOperators.notilike()
:>>> print(column('x').notilike('word')) lower(x) NOT LIKE lower(:x_1)
String Containment
String containment operators are basically built as a combination of LIKE and the string concatenation operator, which is ||
on most backends or sometimes a function like concat()
:
_sql.ColumnOperators.startswith()
:The string containment operators >>> print(column('x').startswith('word')) x LIKE :x_1 || '%'
_sql.ColumnOperators.endswith()
:>>> print(column('x').endswith('word')) x LIKE '%' || :x_1
_sql.ColumnOperators.contains()
:>>> print(column('x').contains('word')) x LIKE '%' || :x_1 || '%'
String matching
Matching operators are always backend-specific and may provide different behaviors and results on different databases:
_sql.ColumnOperators.match()
:This is a dialect-specific operator that makes use of the MATCH feature of the underlying database, if available:
>>> print(column('x').match('word')) x MATCH :x_1
_sql.ColumnOperators.regexp_match()
:This operator is dialect specific. We can illustrate it in terms of for example the PostgreSQL dialect:
>>> from sqlalchemy.dialects import postgresql >>> print(column('x').regexp_match('word').compile(dialect=postgresql.dialect())) x ~ %(x_1)s
Or MySQL:
>>> from sqlalchemy.dialects import mysql >>> print(column('x').regexp_match('word').compile(dialect=mysql.dialect())) x REGEXP %s
String Alteration
_sql.ColumnOperators.concat()
:String concatenation:
>>> print(column('x').concat("some string")) x || :x_1
This operator is available via
_sql.ColumnOperators.__add__()
, that is, the Python+
operator, when working with a column expression that derives from_types.String
:>>> print(column('x', String) + "some string") x || :x_1
The operator will produce the appropriate database-specific construct, such as on MySQL it’s historically been the
concat()
SQL function:>>> print((column('x', String) + "some string").compile(dialect=mysql.dialect())) concat(x, %s)
_sql.ColumnOperators.regexp_replace()
:Complementary to
_sql.ColumnOperators.regexp()
this produces REGEXP REPLACE equivalent for the backends which support it:>>> print(column('x').regexp_replace('foo', 'bar').compile(dialect=postgresql.dialect())) REGEXP_REPLACE(x, %(x_1)s, %(x_2)s)
_sql.ColumnOperators.collate()
:Produces the COLLATE SQL operator which provides for specific collations at expression time:
>>> print((column('x').collate('latin1_german2_ci') == 'Müller').compile(dialect=mysql.dialect())) (x COLLATE latin1_german2_ci) = %s
To use COLLATE against a literal value, use the
_sql.literal()
construct:>>> from sqlalchemy import literal >>> print((literal('Müller').collate('latin1_german2_ci') == column('x')).compile(dialect=mysql.dialect())) (%s COLLATE latin1_german2_ci) = x
Arithmetic Operators
_sql.ColumnOperators.__add__()
,_sql.ColumnOperators.__radd__()
(Python “+
” operator):>>> print(column('x') + 5) x + :x_1 >>> print(5 + column('x')) :x_1 + x
Note that when the datatype of the expression is
_types.String
or similar, the_sql.ColumnOperators.__add__()
operator instead produces string concatenation._sql.ColumnOperators.__sub__()
,_sql.ColumnOperators.__rsub__()
(Python “-
” operator):>>> print(column('x') - 5) x - :x_1 >>> print(5 - column('x')) :x_1 - x
_sql.ColumnOperators.__mul__()
,_sql.ColumnOperators.__rmul__()
(Python “*
” operator):>>> print(column('x') * 5) x * :x_1 >>> print(5 * column('x')) :x_1 * x
_sql.ColumnOperators.__div__()
,_sql.ColumnOperators.__rdiv__()
(Python “/
” operator):>>> print(column('x') / 5) x / :x_1 >>> print(5 / column('x')) :x_1 / x
_sql.ColumnOperators.__mod__()
,_sql.ColumnOperators.__rmod__()
(Python “%
” operator):>>> print(column('x') % 5) x % :x_1 >>> print(5 % column('x')) :x_1 % x
Using Conjunctions and Negations
The most common conjunction, “AND”, is automatically applied if we make repeated use of the _sql.Select.where()
method, as well as similar methods such as _sql.Update.where()
and _sql.Delete.where()
:
>>> print(
... select(address_table.c.email_address).
... where(user_table.c.name == 'squidward').
... where(address_table.c.user_id == user_table.c.id)
... )
SELECT address.email_address
FROM address, user_account
WHERE user_account.name = :name_1 AND address.user_id = user_account.id
_sql.Select.where()
, _sql.Update.where()
and _sql.Delete.where()
also accept multiple expressions with the same effect:
>>> print(
... select(address_table.c.email_address).
... where(
... user_table.c.name == 'squidward',
... address_table.c.user_id == user_table.c.id
... )
... )
SELECT address.email_address
FROM address, user_account
WHERE user_account.name = :name_1 AND address.user_id = user_account.id
The “AND” conjunction, as well as its partner “OR”, are both available directly using the _sql.and_()
and _sql.or_()
functions:
>>> from sqlalchemy import and_, or_
>>> print(
... select(address_table.c.email_address).
... where(
... and_(
... or_(user_table.c.name == 'squidward', user_table.c.name == 'sandy'),
... address_table.c.user_id == user_table.c.id
... )
... )
... )
SELECT address.email_address
FROM address, user_account
WHERE (user_account.name = :name_1 OR user_account.name = :name_2)
AND address.user_id = user_account.id
A negation is available using the _sql.not_()
function. This will typically invert the operator in a boolean expression:
>>> from sqlalchemy import not_
>>> print(not_(column('x') == 5))
x != :x_1
It also may apply a keyword such as NOT
when appropriate:
>>> from sqlalchemy import Boolean
>>> print(not_(column('x', Boolean)))
NOT x
Conjunction Operators
The above conjunction functions _sql.and_()
, _sql.or_()
, _sql.not_()
are also available as overloaded Python operators:
Note
The Python &
, |
and ~
operators take high precedence in the language; as a result, parenthesis must usually be applied for operands that themselves contain expressions, as indicated in the examples below.
_sql.Operators.__and__()
(Python “&
” operator):The Python binary
&
operator is overloaded to behave the same as_sql.and_()
(note parenthesis around the two operands):>>> print((column('x') == 5) & (column('y') == 10)) x = :x_1 AND y = :y_1
_sql.Operators.__or__()
(Python “|
” operator):The Python binary
|
operator is overloaded to behave the same as_sql.or_()
(note parenthesis around the two operands):>>> print((column('x') == 5) | (column('y') == 10)) x = :x_1 OR y = :y_1
_sql.Operators.__invert__()
(Python “~
” operator):The Python binary
~
operator is overloaded to behave the same as_sql.not_()
, either inverting the existing operator, or applying theNOT
keyword to the expression as a whole:>>> print(~(column('x') == 5)) x != :x_1 >>> from sqlalchemy import Boolean >>> print(~column('x', Boolean)) NOT x
Operator Customization
TODO