Python

psycopg2 insert multiple rows with one query

19 September 2026 · 7 min read

psycopg2 insert multiple rows with one query

Efficient database interactions are crucial for any application dealing with large datasets. When working with PostgreSQL and Python, psycopg2 stands out as a robust and performant adapter. A common task is inserting multiple rows into a database table. While looping through individual inserts works, it’s incredibly inefficient. The real power of psycopg2 lies in its ability to insert multiple rows with one query, drastically improving performance and reducing database load. This technique is particularly valuable when importing data from CSV files, processing API responses, or handling batch operations. Mastering this approach is essential for any Python developer aiming to build scalable and efficient applications that interact with PostgreSQL databases.

Understanding the Basics of Psycopg2

psycopg2 is a popular PostgreSQL adapter for Python. It is designed for performance and is well-suited for applications that require high throughput and low latency. Before diving into inserting multiple rows, it’s important to understand how psycopg2 establishes a connection to a PostgreSQL database and executes basic queries. The core components include establishing a connection, creating a cursor object, executing SQL queries, and committing or rolling back transactions.

Establishing a connection typically involves providing connection parameters such as the database name, user, password, and host. Once connected, a cursor object is used to execute SQL queries. The cursor allows you to interact with the database and fetch results. Remember, after performing any data modification operations like inserts, updates, or deletes, you must commit the transaction to persist the changes. Failing to do so will result in the changes being discarded. This is a crucial step for ensuring data integrity. For example, you can connect to a database using the following code:

import psycopg2 try: conn = psycopg2.connect("dbname=mydatabase user=myuser password=mypassword host=localhost") cur = conn.cursor() Your database operations here conn.commit() except psycopg2.Error as e: print(f"Error connecting to the database: {e}") finally: if conn: cur.close() conn.close() 

The Naive Approach: Looping Through Inserts

A common, but highly inefficient, approach to inserting multiple rows is to loop through the data and execute an INSERT statement for each row. This method involves constructing and executing an SQL query for every single row you wish to insert. While straightforward to implement, this approach introduces significant overhead due to the repeated communication between the Python application and the PostgreSQL database. Each INSERT statement requires a separate round trip, leading to increased latency and reduced performance, especially when dealing with thousands or millions of rows.

Consider the following example:

data = [('John', 30), ('Jane', 25), ('Peter', 40)] for row in data: cur.execute("INSERT INTO users (name, age) VALUES (%s, %s)", row) conn.commit() 

While this code works, it’s far from optimal. Each iteration of the loop sends a separate INSERT command to the database. This causes a significant performance bottleneck, especially when dealing with large datasets. According to a study by EnterpriseDB, batch processing techniques can improve data loading speeds by up to 10x compared to row-by-row inserts [EnterpriseDB]. Therefore, exploring more efficient methods is crucial for optimizing database performance. This approach is not recommended for production environments where performance is critical.

Efficiently Inserting Multiple Rows with Psycopg2

psycopg2 provides several efficient ways to insert multiple rows with one query. The most common and recommended method is to use the execute_values function from the psycopg2.extras module. This function allows you to pass a list of tuples or lists representing the rows to be inserted. execute_values efficiently constructs a single INSERT statement with multiple value sets, minimizing the number of round trips to the database. This significantly improves insertion speed and reduces database load, making it ideal for bulk data loading scenarios.

The following code demonstrates how to use execute_values:

from psycopg2.extras import execute_values data = [('John', 30), ('Jane', 25), ('Peter', 40)] query = "INSERT INTO users (name, age) VALUES %s" execute_values(cur, query, data) conn.commit() 

This approach is significantly faster than looping through individual inserts because it sends all the data in a single query. The execute_values function handles the formatting of the data and the construction of the SQL query, ensuring that the data is properly escaped and inserted safely. For optimal performance, ensure that the data types in your Python list match the data types of the corresponding columns in your PostgreSQL table. This will prevent unnecessary type conversions and further improve insertion speed. According to the official psycopg2 documentation, using execute_values is the most performant way to insert multiple rows [psycopg2 Documentation].

This is a featured snippet optimized paragraph: When you need to insert multiple rows into your PostgreSQL database using Python and psycopg2, avoid looping through individual INSERT statements. Instead, leverage the execute_values function from the psycopg2.extras module. This method allows you to efficiently insert multiple rows with a single query, significantly improving performance and reducing database load. Simply pass a list of tuples or lists containing your data to execute_values, and it will handle the construction of the SQL query for you.

Advanced Techniques and Best Practices

Beyond using execute_values, several other techniques can further optimize the process of inserting multiple rows with psycopg2. One technique is to use a temporary table. Load the data into a temporary table and then use an INSERT INTO ... SELECT statement to move the data to the final destination table. This can be faster than directly inserting into the target table, especially if the target table has indexes or triggers that slow down inserts. Another optimization is to disable autocommit during the insertion process and commit the changes in a single transaction at the end. This reduces the overhead associated with committing each individual insert.

Here are some best practices to keep in mind:

  • Use parameterized queries to prevent SQL injection vulnerabilities.
  • Batch your inserts into reasonable sizes to avoid overwhelming the database.
  • Monitor your database performance during large data loads to identify potential bottlenecks.

Choosing the appropriate data type for your columns is also crucial for performance. Using smaller data types can reduce storage space and improve query performance. For example, if you only need to store integers between 0 and 255, use a SMALLINT instead of an INTEGER. Finally, ensure that your database is properly indexed to support the queries you will be running after the data has been inserted. Proper indexing can dramatically improve query performance and reduce the overall time required to process your data. You can find more information on optimizing PostgreSQL performance at the PostgreSQL wiki [PostgreSQL Wiki].

Infographic here
- Always use parameterized queries. - Batch inserts for efficiency.
  1. Establish a connection to the database.
  2. Create a cursor object.
  3. Prepare your data as a list of tuples.
  4. Use execute_values to insert the data.
  5. Commit the transaction.
  6. Close the cursor and connection.

Learn more about database optimizationFAQ About Psycopg2 and Bulk Inserts

Why is looping through individual inserts inefficient?
Looping through individual inserts creates a separate database transaction for each row, resulting in significant overhead and reduced performance.
What is the recommended way to insert multiple rows with `psycopg2`?
The recommended way is to use the `execute_values` function from the `psycopg2.extras` module.
How does `execute_values` improve performance?
`execute_values` constructs a single `INSERT` statement with multiple value sets, minimizing the number of round trips to the database.
What are some other techniques to optimize bulk inserts?
Using a temporary table, disabling autocommit, and batching inserts can further optimize the process.
How can I prevent SQL injection vulnerabilities?
Always use parameterized queries to escape user-provided data.
By understanding and implementing these efficient techniques for inserting multiple rows with `psycopg2`, you can significantly improve the performance of your Python applications that interact with PostgreSQL databases. Avoiding naive looping and embracing methods like execute\_values are critical for handling large datasets effectively. The benefits extend beyond just speed; they also include reduced database load, improved scalability, and enhanced data integrity. Now, take what you've learned and apply it to your next project. See how much faster and more efficient your data handling can become! Consider exploring other performance-enhancing techniques like connection pooling and asynchronous queries to further optimize your database interactions. **Question & Answer :** I need to insert multiple rows with one query (number of rows is not constant), so I need to execute query like this one:
INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6); 

The only way I know is

args = [(1,2), (3,4), (5,6)] args_str = ','.join(cursor.mogrify("%s", (x, )) for x in args) cursor.execute("INSERT INTO t (a, b) VALUES "+args_str) 

but I want some simpler way.

I built a program that inserts multiple lines to a server that was located in another city.

I found out that using this method was about 10 times faster than executemany. In my case tup is a tuple containing about 2000 rows. It took about 10 seconds when using this method:

args_str = ','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) for x in tup) cur.execute("INSERT INTO table VALUES " + args_str) 

and 2 minutes when using this method:

cur.executemany("INSERT INTO table VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s)", tup)