psycopg2: insert multiple rows with one query
Asked Answered
I

21

240

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.

Incase answered 15/11, 2011 at 10:9 Comment(0)
L
300

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)
Liard answered 13/4, 2012 at 19:53 Comment(15)
Still very relevant almost two years later. An experience today suggests that as the number of rows you want to push increases, the better it is to use the execute strategy. I saw speedup of around 100x thanks to this!Cocklebur
Interesting...I wonder how much this depends on the type of data being inserted, or if you have any roles or triggers on your tables. I'm inserting a mix of numeric, string, and date data into tables with roles performing foreign key checks, and for me, executemany() is 10 seconds faster. Curious. :/Maurer
Perhaps executemany runs a commit after each insert. If you instead wrap the whole thing in a transaction, maybe that would expedite things?Nitrosyl
Just confirmed this improvement myself. From what I've read psycopg2's executemany doesn't do anything optimal, just loops and does many execute statements. Using this method, a 700 row insert to a remote server went from 60s to <2s.Harragan
You would think executemany would do things more efficiently but looks like MySQL suffers from this executemany slowdown issue. Can a patch be submitted or executemany does this for a reason?Rampageous
If autocommit in the connection object is set to True, this might be slowing down cur.executemany() unnecessarily.Sudarium
Maybe I'm being paranoid, but concatenating the query with a + seems like it could open up to sql injection, I feel like @Clodoaldo Neto execute_values() solution is safer.Menides
Is this safe from SQL injection? What if my values come from an untrusted source?Grenada
Batch preparation of inserts is really so much faster, thanks a lot for this hint. Want to add: I had a Value Error for ','join because mogrify returns byte and not string (and I was storing results of mogrifiy in a list first). I used .decode on the mogrify result and this solved the problem.Juliettejulina
The docs initd.org/psycopg/docs/extras.html#fast-execution-helpers seem to say that executemany is not a great solution. execute_values or copy_from are better optionsGaw
in case someone encounters the following error: [TypeError: sequence item 0: expected str instance, bytes found] run this command instead [args_str = ','.join(cur.mogrify("(%s,%s)", x).decode("utf-8") for x in tup)]Sound
@WillMunn my GWAPT made me more paranoid but there's no site for SQLi there since all inputs are sanitized. You're right in the sense that it's best to practice good habits using the safe methods though.Tractor
Can you include an example of what your source data looks like? Is it really one single tuple, or a tuple of tuples? Does the number of '%s' instances need to match the number of columns in your row data? Thanks,Oriel
For anyone wondering about source data, number of columns etc., this is my variant of the above method for a dynamic number of columns when the data is a list of tuples (as the result from a cursor.fetchAll()): num_columns = len(columns) values_placeholder = ', '.join(["%s"]*num_columns) values_sql = b','.join(cur_staging.mogrify("(%s)" % values_placeholder, row) for row in values) cur_staging.execute("INSERT INTO {}.{} ({}) VALUES {}".format(schema, table, column_string, values_sql.decode('utf-8'))) (Sorry for horrible formatting)Paramo
better to use b",".join(cursor.mogrify(f"({s_strs})", r) for r in rs) then decodeVulpine
T
249

New execute_values method in Psycopg 2.7:

data = [(1,'x'), (2,'y')]
insert_query = 'insert into t (a, b) values %s'
psycopg2.extras.execute_values (
    cursor, insert_query, data, template=None, page_size=100
)

The pythonic way of doing it in Psycopg 2.6:

data = [(1,'x'), (2,'y')]
records_list_template = ','.join(['%s'] * len(data))
insert_query = 'insert into t (a, b) values {}'.format(records_list_template)
cursor.execute(insert_query, data)

Explanation: If the data to be inserted is given as a list of tuples like in

data = [(1,'x'), (2,'y')]

then it is already in the exact required format as

  1. the values syntax of the insert clause expects a list of records as in

    insert into t (a, b) values (1, 'x'),(2, 'y')

  2. Psycopg adapts a Python tuple to a Postgresql record.

The only necessary work is to provide a records list template to be filled by psycopg

# We use the data list to be sure of the template length
records_list_template = ','.join(['%s'] * len(data))

and place it in the insert query

insert_query = 'insert into t (a, b) values {}'.format(records_list_template)

Printing the insert_query outputs

insert into t (a, b) values %s,%s

Now to the usual Psycopg arguments substitution

cursor.execute(insert_query, data)

Or just testing what will be sent to the server

print (cursor.mogrify(insert_query, data).decode('utf8'))

Output:

insert into t (a, b) values (1, 'x'),(2, 'y')
Typewriter answered 22/6, 2015 at 16:50 Comment(8)
How does the performance of this method compare with cur.copy_from?Lamont
Here's a gist with a benchmark. copy_from scales to about 6.5X faster on my machine with 10M records.Batty
Looks nice - I think you have a stray , at the end of your initial definition of insert_query (unless you were trying to make it a tuple?) and are missing a s after the % for %s also in the initial definition of insert_query.Rusell
using execute_values I was able to get my system running at 1k records a minute up to 128k records a minuteUndercast
@Phillipp that's normal with every execute statement, unless you're in auto commmit mode.Submerge
Using execute_values we got results ~20% faster compared to the same page size using execute_batch.Neighborly
Does 'execute_values` use mogrify behind the scenes?Skeen
improved insertion of 30,000 records from 18 minutes to 2 minutes!!!!!Benham
S
116

Update with psycopg2 2.7:

The classic executemany() is about 60 times slower than @ant32 's implementation (called "folded") as explained in this thread: https://www.postgresql.org/message-id/20170130215151.GA7081%40deb76.aryehleib.com

This implementation was added to psycopg2 in version 2.7 and is called execute_values():

from psycopg2.extras import execute_values
execute_values(cur,
    "INSERT INTO test (id, v1, v2) VALUES %s",
    [(1, 2, 3), (4, 5, 6), (7, 8, 9)])

Previous Answer:

To insert multiple rows, using the multirow VALUES syntax with execute() is about 10x faster than using psycopg2 executemany(). Indeed, executemany() just runs many individual INSERT statements.

@ant32 's code works perfectly in Python 2. But in Python 3, cursor.mogrify() returns bytes, cursor.execute() takes either bytes or strings, and ','.join() expects str instance.

So in Python 3 you may need to modify @ant32 's code, by adding .decode('utf-8'):

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

Or by using bytes (with b'' or b"") only:

args_bytes = b','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) for x in tup)
cur.execute(b"INSERT INTO table VALUES " + args_bytes) 
Sosanna answered 19/8, 2016 at 8:48 Comment(3)
Thanks, the updated answer works good. Please, don't forget to conn.commit() to persist changes.Buckbuckaroo
So is execute_values() faster than @ant32?Barnardo
execute_many may no longer be the dog it once was: as of 3.1, it uses pycopg's pipeline mode to batch commands into a Postgresql single send/receive sequence.Biogenesis
B
38

cursor.copy_from is the fastest solution I've found for bulk inserts by far. Here's a gist I made containing a class named IteratorFile which allows an iterator yielding strings to be read like a file. We can convert each input record to a string using a generator expression. So the solution would be

args = [(1,2), (3,4), (5,6)]
f = IteratorFile(("{}\t{}".format(x[0], x[1]) for x in args))
cursor.copy_from(f, 'table_name', columns=('a', 'b'))

For this trivial size of args it won't make much of a speed difference, but I see big speedups when dealing with thousands+ of rows. It will also be more memory efficient than building a giant query string. An iterator would only ever hold one input record in memory at a time, where at some point you'll run out of memory in your Python process or in Postgres by building the query string.

Batty answered 9/6, 2015 at 1:6 Comment(5)
Here is a benchmark comparing copy_from/IteratorFile with a query builder solution. copy_from scales to about 6.5X faster on my machine with 10M records.Batty
do you have to dick around with escaping strings and timestamps etc?Cartwell
Yes, you'll have to make sure you have a well formed TSV records.Batty
Hi, still relevant after 5 years !! I am new to Python and Postgres and I am trying to find out the most efficient and fastest way to load data (extracted from XML) to Postgres. So I found @Joseph Sheedy's answer and want to try and test it. But I couldn't understand where or how should the function def readline(self): be used because I didn't find any reference to the same in the code ? Can someone give a complete usage example please. Pls excuse my naivety. Thanks.Wagonette
@marie20, readline is called implicitly by copy_from. Here's the docs: psycopg.org/docs/cursor.html#cursor.copy_fromBatty
E
33

A snippet from Psycopg2's tutorial page at Postgresql.org (see bottom):

A last item I would like to show you is how to insert multiple rows using a dictionary. If you had the following:

namedict = ({"first_name":"Joshua", "last_name":"Drake"},
            {"first_name":"Steven", "last_name":"Foo"},
            {"first_name":"David", "last_name":"Bar"})

You could easily insert all three rows within the dictionary by using:

cur = conn.cursor()
cur.executemany("""INSERT INTO bar(first_name,last_name) VALUES (%(first_name)s, %(last_name)s)""", namedict)

It doesn't save much code, but it definitively looks better.

Ethelethelbert answered 14/12, 2011 at 11:6 Comment(2)
This will run many individual INSERT statements. Useful, but not the same as a single multi-VALUEd insert.Wham
And in the same document it is written The cur.executemany statement will automatically iterate through the dictionary and execute the INSERT query for each row.Lohner
S
14

Security vulnerabilities

As of 2022-11-16, the answers by @Clodoaldo Neto (for Psycopg 2.6), @Joseph Sheedy, @J.J, @Bart Jonk, @kevo Njoki, @TKoutny and @Nihal Sharma contain SQL injection vulnerabilities and should not be used.

The fastest proposal so far (copy_from) should not be used either because it is difficult to escape the data correctly. This is easily apparent when trying to insert characters like ', ", \n, \, \t or \n.

The author of psycopg2 also recommends against copy_from:

copy_from() and copy_to() are really just ancient and incomplete methods

The fastest method

The fastest method is cursor.copy_expert, which can insert data straight from CSV files.

with open("mydata.csv") as f:
    cursor.copy_expert("COPY mytable (my_id, a, b) FROM STDIN WITH csv", f)

copy_expert is also the fastest method when generating the CSV file on-the-fly. For reference, see the following CSVFile class, which takes care to limit memory usage.

import io, csv

class CSVFile(io.TextIOBase):
    # Create a CSV file from rows. Can only be read once.
    def __init__(self, rows, size=8192):
        self.row_iter = iter(rows)
        self.buf = io.StringIO()
        self.available = 0
        self.size = size

    def read(self, n):
        # Buffer new CSV rows until enough data is available
        buf = self.buf
        writer = csv.writer(buf)
        while self.available < n:
            try:
                row_length = writer.writerow(next(self.row_iter))
                self.available += row_length
                self.size = max(self.size, row_length)
            except StopIteration:
                break

        # Read requested amount of data from buffer
        write_pos = buf.tell()
        read_pos = write_pos - self.available
        buf.seek(read_pos)
        data = buf.read(n)
        self.available -= len(data)

        # Shrink buffer if it grew very large
        if read_pos > 2 * self.size:
            remaining = buf.read()
            buf.seek(0)
            buf.write(remaining)
            buf.truncate()
        else:
            buf.seek(write_pos)

        return data

This class can then be used like:

rows = [(1, "a", "b"), (2, "c", "d")]
cursor.copy_expert("COPY mytable (my_id, a, b) FROM STDIN WITH csv", CSVFile(rows))

If all your data fits into memory, you can also generate the entire CSV data directly without the CSVFile class, but if you do not know how much data you are going to insert in the future, you probably should not do that.

f = io.StringIO()
writer = csv.writer(f)
for row in rows:
    writer.writerow(row)
f.seek(0)
cursor.copy_expert("COPY mytable (my_id, a, b) FROM STDIN WITH csv", f)

Benchmark results

  • 914 milliseconds - many calls to cursor.execute
  • 846 milliseconds - cursor.executemany
  • 362 milliseconds - psycopg2.extras.execute_batch
  • 346 milliseconds - execute_batch with page_size=1000
  • 265 milliseconds - execute_batch with prepared statement
  • 161 milliseconds - psycopg2.extras.execute_values
  • 127 milliseconds - cursor.execute with string-concatenated values
  • 39 milliseconds - copy_expert generating the entire CSV file at once
  • 32 milliseconds - copy_expert with CSVFile
Subastral answered 16/11, 2022 at 14:53 Comment(1)
Hi BlueSky. Could you tell what's the problem with execute_values wrt SQL injections?Goods
Q
8

All of these techniques are called 'Extended Inserts" in Postgres terminology, and as of the 24th of November 2016, it's still a ton faster than psychopg2's executemany() and all the other methods listed in this thread (which i tried before coming to this answer).

Here's some code which doesnt use cur.mogrify and is nice and simply to get your head around:

valueSQL = [ '%s', '%s', '%s', ... ] # as many as you have columns.
sqlrows = []
rowsPerInsert = 3 # more means faster, but with diminishing returns..
for row in getSomeData:
        # row == [1, 'a', 'yolo', ... ]
        sqlrows += row
        if ( len(sqlrows)/len(valueSQL) ) % rowsPerInsert == 0:
                # sqlrows == [ 1, 'a', 'yolo', 2, 'b', 'swag', 3, 'c', 'selfie' ]
                insertSQL = 'INSERT INTO "twitter" VALUES ' + ','.join(['(' + ','.join(valueSQL) + ')']*rowsPerInsert)
                cur.execute(insertSQL, sqlrows)
                con.commit()
                sqlrows = []
insertSQL = 'INSERT INTO "twitter" VALUES ' + ','.join(['(' + ','.join(valueSQL) + ')']*len(sqlrows))
cur.execute(insertSQL, sqlrows)
con.commit()

But it should be noted that if you can use copy_from(), you should use copy_from ;)

Quenby answered 4/5, 2015 at 15:28 Comment(2)
Bringing up from the dead, but what happens in the situation of the last few rows? I assume you actually run that final clause again on the last remaining rows, in the case you have an even number of rows?Dealer
Correct, sorry I must have forgotten to do that when i wrote the example - thats pretty stupid of me. Not doing so wouldn't have given people an error, which makes me worried how many people copy/pasted the solution and went about their business..... Anyway, very grateful mcpeterson - thank you!Quenby
F
4

I've been using ant32's answer above for several years. However I've found that is thorws an error in python 3 because mogrify returns a byte string.

Converting explicitly to bytse strings is a simple solution for making code python 3 compatible.

args_str = b','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) for x in tup) 
cur.execute(b"INSERT INTO table VALUES " + args_str)
Foltz answered 27/7, 2017 at 1:29 Comment(1)
An easier option would be to decode cur.mogrify()Sadiras
A
4

executemany accept array of tuples

https://www.postgresqltutorial.com/postgresql-python/insert/

    """ array of tuples """
    vendor_list = [(value1,)]

    """ insert multiple vendors into the vendors table  """
    sql = "INSERT INTO vendors(vendor_name) VALUES(%s)"
    conn = None
    try:
        # read database configuration
        params = config()
        # connect to the PostgreSQL database
        conn = psycopg2.connect(**params)
        # create a new cursor
        cur = conn.cursor()
        # execute the INSERT statement
        cur.executemany(sql,vendor_list)
        # commit the changes to the database
        conn.commit()
        # close communication with the database
        cur.close()
    except (Exception, psycopg2.DatabaseError) as error:
        print(error)
    finally:
        if conn is not None:
            conn.close()
Aerosol answered 22/5, 2020 at 15:19 Comment(0)
P
3

The cursor.copyfrom solution as provided by @jopseph.sheedy (https://stackoverflow.com/users/958118/joseph-sheedy) above (https://mcmap.net/q/116622/-psycopg2-insert-multiple-rows-with-one-query) is indeed lightning fast.

However, the example he gives are not generically usable for a record with any number of fields and it took me while to figure out how to use it correctly.

The IteratorFile needs to be instantiated with tab-separated fields like this (r is a list of dicts where each dict is a record):

    f = IteratorFile("{0}\t{1}\t{2}\t{3}\t{4}".format(r["id"],
        r["type"],
        r["item"],
        r["month"],
        r["revenue"]) for r in records)

To generalise for an arbitrary number of fields we will first create a line string with the correct amount of tabs and field placeholders : "{}\t{}\t{}....\t{}" and then use .format() to fill in the field values : *list(r.values())) for r in records:

        line = "\t".join(["{}"] * len(records[0]))

        f = IteratorFile(line.format(*list(r.values())) for r in records)

complete function in gist here.

Parisparish answered 3/12, 2019 at 13:29 Comment(0)
T
3

execute_batch has been added to psycopg2 since this question was posted.

It is faster than execute_values.

Tinhorn answered 22/2, 2020 at 14:59 Comment(1)
See other comments. psycopg2's method execute_values is faster than execute_batchRexferd
M
2

Another nice and efficient approach - is to pass rows for insertion as 1 argument, which is array of json objects.

E.g. you passing argument:

[ {id: 18, score: 1}, { id: 19, score: 5} ]

It is array, which may contain any amount of objects inside. Then your SQL looks like:

INSERT INTO links (parent_id, child_id, score) 
SELECT 123, (r->>'id')::int, (r->>'score')::int 
FROM unnest($1::json[]) as r 

Notice: Your postgress must be new enough, to support json

Margarita answered 18/10, 2015 at 11:4 Comment(0)
F
2

If you're using SQLAlchemy, you don't need to mess with hand-crafting the string because SQLAlchemy supports generating a multi-row VALUES clause for a single INSERT statement:

rows = []
for i, name in enumerate(rawdata):
    row = {
        'id': i,
        'name': name,
        'valid': True,
    }
    rows.append(row)
if len(rows) > 0:  # INSERT fails if no rows
    insert_query = SQLAlchemyModelName.__table__.insert().values(rows)
    session.execute(insert_query)
Fley answered 27/4, 2016 at 2:48 Comment(5)
Under the hood SQLAlchemy uses psychopg2's executemany() for calls like this and therefore this answer will have severe performance issues for large queries. See the execute method docs.sqlalchemy.org/en/latest/orm/session_api.html.Dorotheadorothee
I don't think that's the case. It's been a bit since I looked at this, but IIRC, this is actually building a single insertion statement in the insert_query line. Then, session.execute() is just calling psycopg2's execute() statement with a single massive string. So the "trick" is building the entire insertion statement object first. I am using this to insert 200,000 rows at a time and saw massive performance increases using this code compared to the normal executemany().Fley
The SQLAlchemy doc you linked to has a section that shows exactly how this works and even says: "It is essential to note that passing multiple values is NOT the same as using traditional executemany() form". So it is explicitly calling out that this works.Fley
I stand corrected. I didn't notice your usage of the values() method (without it SQLAlchemy just does executemany). I would say edit the answer to include a link to that doc so that I can change my vote, but obviously you've already included it. Perhaps mention that this is not the same thing as calling an insert() with execute() with a list of dicts?Dorotheadorothee
how does it perform compared to execute_values?Ganges
J
2

10 years later, an answer for Psycopg 3 and PostgreSQL 14 or newer is: use pipeline mode. Use ordinary INSERTs in execute/executemany statements in Psycopg 3's pipeline mode implementation to be fast and safe against SQL injection. Starting with Psycopg 3.1, executemany() makes use internally of the pipeline mode2.

Jollenta answered 31/10, 2023 at 8:42 Comment(1)
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewLactiferous
E
1

From @ant32

def myInsertManyTuples(connection, table, tuple_of_tuples):
    cursor = connection.cursor()
    try:
        insert_len = len(tuple_of_tuples[0])
        insert_template = "("
        for i in range(insert_len):
            insert_template += "%s,"
        insert_template = insert_template[:-1] + ")"

        args_str = ",".join(
            cursor.mogrify(insert_template, x).decode("utf-8")
            for x in tuple_of_tuples
        )
        cursor.execute("INSERT INTO " + table + " VALUES " + args_str)
        connection.commit()

    except psycopg2.Error as e:
        print(f"psycopg2.Error in myInsertMany = {e}")
        connection.rollback()
Ensoll answered 6/5, 2022 at 10:17 Comment(0)
D
1

psycopg2 2.9.3

data = "(1, 2), (3, 4), (5, 6)"
query = "INSERT INTO t (a, b) VALUES {0}".format(data)
cursor.execute(query)

or

data = [(1, 2), (3, 4), (5, 6)]
data = ",".join(map(str, data))
query = "INSERT INTO t (a, b) VALUES {0}".format(data)
cursor.execute(query)
Divorcement answered 30/9, 2022 at 7:45 Comment(0)
W
1

Psycopg 3 provides a simple way for using Postgres COPY, which is highly efficient.

records = [(1,2), (3,4), (5,6)]
with cursor.copy("COPY example_table (col_a, col_b) FROM STDIN") as copy:
    for record in records:
        copy.write_row(record)

Additional information is available in the official documentation.

Wrote answered 21/12, 2023 at 12:47 Comment(0)
J
0

If you want to insert multiple rows within one insert statemens (assuming you are not using ORM) the easiest way so far for me would be to use list of dictionaries. Here is an example:

 t = [{'id':1, 'start_date': '2015-07-19 00:00:00', 'end_date': '2015-07-20 00:00:00', 'campaignid': 6},
      {'id':2, 'start_date': '2015-07-19 00:00:00', 'end_date': '2015-07-20 00:00:00', 'campaignid': 7},
      {'id':3, 'start_date': '2015-07-19 00:00:00', 'end_date': '2015-07-20 00:00:00', 'campaignid': 8}]

conn.execute("insert into campaign_dates
             (id, start_date, end_date, campaignid) 
              values (%(id)s, %(start_date)s, %(end_date)s, %(campaignid)s);",
             t)

As you can see only one query will be executed:

INFO sqlalchemy.engine.base.Engine insert into campaign_dates (id, start_date, end_date, campaignid) values (%(id)s, %(start_date)s, %(end_date)s, %(campaignid)s);
INFO sqlalchemy.engine.base.Engine [{'campaignid': 6, 'id': 1, 'end_date': '2015-07-20 00:00:00', 'start_date': '2015-07-19 00:00:00'}, {'campaignid': 7, 'id': 2, 'end_date': '2015-07-20 00:00:00', 'start_date': '2015-07-19 00:00:00'}, {'campaignid': 8, 'id': 3, 'end_date': '2015-07-20 00:00:00', 'start_date': '2015-07-19 00:00:00'}]
INFO sqlalchemy.engine.base.Engine COMMIT
Juliettejulina answered 16/11, 2015 at 15:21 Comment(1)
Showing logging from the sqlalchemy engine is NOT a demonstration of only running a single query, it just means that the sqlalchemy engine ran one command. Under the hood this is using psychopg2's executemany which is very inefficient. See the execute method docs.sqlalchemy.org/en/latest/orm/session_api.html.Dorotheadorothee
F
0

The Solution am using can insert like 8000 records in 1 millisecond

curtime = datetime.datetime.now()
postData = dict()
postData["title"] = "This is Title Text"
postData["body"] = "This a Body Text it Can be Long Text"
postData['created_at'] = curtime.isoformat()
postData['updated_at'] = curtime.isoformat()
data = []
for x in range(8000):
    data.append(((postData)))
vals = []
for d in postData:
    vals.append(tuple(d.values())) #Here we extract the Values from the Dict
flds = ",".join(map(str, postData[0]))
tableFlds =  ",".join(map(str, vals))
sqlStr = f"INSERT INTO posts ({flds}) VALUES {tableFlds}"
db.execute(sqlStr)
connection.commit()
rowsAffected = db.rowcount
print(f'{rowsAffected} Rows Affected')
Faggot answered 3/10, 2022 at 12:27 Comment(2)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Locust
I don't see any protection against SQL injection in VALUES {tableFlds}.Straiten
F
-4

Finally in SQLalchemy1.2 version, this new implementation is added to use psycopg2.extras.execute_batch() instead of executemany when you initialize your engine with use_batch_mode=True like:

engine = create_engine(
    "postgresql+psycopg2://scott:tiger@host/dbname",
    use_batch_mode=True)

http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109

Then someone would have to use SQLalchmey won't bother to try different combinations of sqla and psycopg2 and direct SQL together..

Fundamental answered 2/1, 2018 at 2:1 Comment(0)
S
-5

Using aiopg - The snippet below works perfectly fine

    # items = [10, 11, 12, 13]
    # group = 1
    tup = [(gid, pid) for pid in items]
    args_str = ",".join([str(s) for s in tup])
    # insert into group values (1, 10), (1, 11), (1, 12), (1, 13)
    yield from cur.execute("INSERT INTO group VALUES " + args_str)
Selfexistent answered 16/7, 2015 at 10:41 Comment(1)

© 2022 - 2024 — McMap. All rights reserved.