⌘K ctrl+k
Search Shortcut cmd + k | ctrl + k
Executing SQL in Python

SQL queries can be executed using the duckdb.sql function.

import duckdb

duckdb.sql("SELECT 42").show()

By default this will create a relation object. The result can be converted to various formats using the result conversion functions. For example, the fetchall method can be used to convert the result to Python objects.

results = duckdb.sql("SELECT 42").fetchall()
print(results)
[(42,)]

Several other result objects exist. For example, you can use df to convert the result to a Pandas DataFrame.

results = duckdb.sql("SELECT 42").df()
print(results)
   42
0  42

By default, a global in-memory connection will be used. Any data stored in it will be lost after shutting down the program. A connection to a persistent database can be created using the connect function.

After connecting, SQL queries can be executed using the sql command.

con = duckdb.connect("file.db")
con.sql("CREATE TABLE integers (i INTEGER)")
con.sql("INSERT INTO integers VALUES (42)")
con.sql("SELECT * FROM integers").show()

A statement that changes data or the schema, such as CREATE TABLE or INSERT, runs when sql is called, and the relation it returns holds the statement's result. A SELECT query that only reads runs each time its relation is read, for example by show() or fetchall().

Query Parameters

To parameterize a query, pass the values separately from the SQL string using the execute method. Use ? for positional parameters, supplying the values as a list:

con.execute("SELECT * FROM integers WHERE i = ?", [42]).fetchall()

Named parameters use the $name syntax, with the values supplied as a dictionary:

con.execute("SELECT * FROM integers WHERE i = $value", {"value": 42}).fetchall()

con.sql and duckdb.sql take the values through their params keyword argument:

con.sql(
    "SELECT * FROM integers WHERE i = $value",
    params={"value": 42},
).fetchall()

The params list or dictionary is copied when sql is called, together with any list, tuple or dictionary inside it. Other values are converted each time the relation is read, so a bytearray changed in place afterwards is read changed.

To run the same parameterized statement for many sets of values, for example when inserting multiple rows, use executemany:

con.executemany("INSERT INTO integers VALUES (?)", [[1], [2], [3]])

Afterwards, con.rowcount holds the number of inserted rows, here 3.

© 2026 DuckDB Foundation, Amsterdam NL
DuckDB Home Code of Conduct Trademark Use Blog