Select

Hint

Follow along by installing Piccolo and running piccolo playground run - see Playground.

Columns

To get all columns:

>>> await Band.select()
[{'id': 1, 'name': 'Pythonistas', 'manager': 1, 'popularity': 1000},
 {'id': 2, 'name': 'Rustaceans', 'manager': 2, 'popularity': 500}]

To get certain columns:

>>> await Band.select(Band.name)
[{'name': 'Rustaceans'}, {'name': 'Pythonistas'}]

Or use an alias to make it shorter:

>>> b = Band
>>> await b.select(b.name)
[{'name': 'Rustaceans'}, {'name': 'Pythonistas'}]

Hint

All of these examples also work synchronously using run_sync - see Sync and Async.


as_alias

By using as_alias, the name of the row can be overriden in the response.

>>> await Band.select(Band.name.as_alias('title'))
[{'title': 'Rustaceans'}, {'title': 'Pythonistas'}]

This is equivalent to SELECT name AS title FROM band in SQL.


Joins

One of the most powerful things about select is it’s support for joins.

>>> await Band.select(Band.name, Band.manager.name)
[
    {'name': 'Pythonistas', 'manager.name': 'Guido'},
    {'name': 'Rustaceans', 'manager.name': 'Graydon'}
]

The joins can go several layers deep.

>>> await Concert.select(Concert.id, Concert.band_1.manager.name)
[{'id': 1, 'band_1.manager.name': 'Guido'}]

all_columns

If you want all of the columns from a related table you can use all_columns, which is a useful shortcut which saves you from typing them all out:

>>> await Band.select(Band.name, Band.manager.all_columns())
[
    {'name': 'Pythonistas', 'manager.id': 1, 'manager.name': 'Guido'},
    {'name': 'Rustaceans', 'manager.id': 2, 'manager.name': 'Graydon'}
]

In Piccolo < 0.41.0 you had to explicitly unpack all_columns. This is equivalent to the code above:

>>> await Band.select(Band.name, *Band.manager.all_columns())

You can exclude some columns if you like:

>>> await Band.select(
...     Band.name,
...     Band.manager.all_columns(exclude=[Band.manager.id])
... )
[
    {'name': 'Pythonistas', 'manager.name': 'Guido'},
    {'name': 'Rustaceans', 'manager.name': 'Graydon'}
]

Strings are supported too if you prefer:

>>> await Band.select(
...     Band.name,
...     Band.manager.all_columns(exclude=['id'])
... )
[
    {'name': 'Pythonistas', 'manager.name': 'Guido'},
    {'name': 'Rustaceans', 'manager.name': 'Graydon'}
]

You can also use all_columns on the root table, which saves you time if you have lots of columns. It works identically to related tables:

>>> await Band.select(
...     Band.all_columns(exclude=[Band.id]),
...     Band.manager.all_columns(exclude=[Band.manager.id])
... )
[
    {'name': 'Pythonistas', 'popularity': 1000, 'manager.name': 'Guido'},
    {'name': 'Rustaceans', 'popularity': 500, 'manager.name': 'Graydon'}
]

Nested

You can also get the response as nested dictionaries, which can be very useful:

>>> await Band.select(Band.name, Band.manager.all_columns()).output(nested=True)
[
    {'name': 'Pythonistas', 'manager': {'id': 1, 'name': 'Guido'}},
    {'name': 'Rustaceans', 'manager': {'id': 2, 'manager.name': 'Graydon'}}
]

String syntax

You can specify the column names using a string if you prefer. The disadvantage is you won’t have tab completion, but sometimes it’s more convenient.

await Band.select('name')

# For joins:
await Band.select('manager.name')

String functions

Piccolo has lots of string functions built-in. See piccolo/query/functions/string.py. Here’s an example using Upper, to convert values to uppercase:

from piccolo.query.functions.string import Upper

>> await Band.select(Upper(Band.name, alias='name'))
[{'name': 'PYTHONISTAS'}, ...]

You can also use these within where clauses:

from piccolo.query.functions.string import Upper

>> await Band.select(Band.name).where(Upper(Band.manager.name) == 'GUIDO')
[{'name': 'Pythonistas'}]

Aggregate functions

Note

These can all be used in conjunction with the group_by clause.

Count

Hint

You can use the count query as a quick way of getting the number of rows in a table.

Returns the number of matching rows.

from piccolo.query.functions.aggregate import Count

>> await Band.select(Count()).where(Band.popularity > 100)
[{'count': 3}]

To find out more about the options available, see Count.

Avg

Returns the average for a given column:

>>> from piccolo.query.functions.aggregate import Avg
>>> response = await Band.select(Avg(Band.popularity)).first()
>>> response["avg"]
750.0

Sum

Returns the sum for a given column:

>>> from piccolo.query.functions.aggregate import Sum
>>> response = await Band.select(Sum(Band.popularity)).first()
>>> response["sum"]
1500

Max

Returns the maximum for a given column:

>>> from piccolo.query.functions.aggregate import Max
>>> response = await Band.select(Max(Band.popularity)).first()
>>> response["max"]
1000

Min

Returns the minimum for a given column:

>>> from piccolo.query.functions.aggregate import Min
>>> response = await Band.select(Min(Band.popularity)).first()
>>> response["min"]
500

Additional features

You also can have multiple different aggregate functions in one query:

>>> from piccolo.query.functions.aggregate import Avg, Sum
>>> response = await Band.select(
...     Avg(Band.popularity),
...     Sum(Band.popularity)
... ).first()
>>> response
{"avg": 750.0, "sum": 1500}

And can use aliases for aggregate functions like this:

# Alternatively, you can use the `as_alias` method.
>>> response = await Band.select(
...     Avg(Band.popularity).as_alias("popularity_avg")
... ).first()
>>> response["popularity_avg"]
750.0

Advanced

In certain situations you may want to have raw SQL in your select query.

For example, if there’s a Postgres function which you want to access, which isn’t supported by Piccolo:

from piccolo.querystring import QueryString

>>> await Band.select(
...     Band.name,
...     QueryString("log(popularity) AS log_popularity")
... )
[{'name': 'Pythonistas', 'log_popularity': 3.0}]

Warning

Only use SQL that you trust.

Note

We used to use SelectRaw for this, which still works, but you can just pass QueryString in directly now.


Query clauses

batch

See batch.

callback

See callback.

columns

By default all columns are returned from the queried table.

# Equivalent to SELECT * from band
await Band.select()

To restrict the returned columns, either pass in the columns into the select method, or use the columns method.

# Equivalent to SELECT name from band
await Band.select(Band.name)

# Or alternatively:
await Band.select().columns(Band.name)

The columns method is additive, meaning you can chain it to add additional columns.

await Band.select().columns(Band.name).columns(Band.manager)

# Or just define it one go:
await Band.select().columns(Band.name, Band.manager)

distinct

See distinct.

first

See first.

group_by

See group_by.

limit

See limit.

lock_rows

See lock_rows.

offset

See offset.

order_by

See order_by.

output

See output.

where

See where.