Thread: How to execute an UPDATE query without string concatenation/interpolation?
How to execute an UPDATE query without string concatenation/interpolation?
From
"W. Matthew Wilson"
Date:
I want to write a function that I can use like this >>> update_foo(foo_id=1, colA=11) and it do this internally: cursor.execute(""" update foo set colA = (%s) where foo_id (%s)""", [11, 1]) And I want to pass in more than just a single column to update, like this, too: >>> update_foo(foo_id=1, colA=11, colB=12, colC=13) and it should do this: cursor.execute(""" update foo set colA = (%s), colB = (%s), colC = (%s) where foo_id (%s)""", [11, 12, 13, 1]) I'm having a really hard time doing this without building up strings and then appending them together. Is there some better way? Thanks in advance. -- W. Matthew Wilson matt@tplus1.com http://tplus1.com
Re: How to execute an UPDATE query without string concatenation/interpolation?
From
Daniele Varrazzo
Date:
On Sat, Jan 8, 2011 at 5:33 PM, W. Matthew Wilson <matt@tplus1.com> wrote: > I want to write a function that I can use like this > >>>> update_foo(foo_id=1, colA=11) > > and it do this internally: > > cursor.execute(""" > update foo > set colA = (%s) > where foo_id (%s)""", [11, 1]) > > And I want to pass in more than just a single column to update, like this, too: > >>>> update_foo(foo_id=1, colA=11, colB=12, colC=13) > > and it should do this: > > cursor.execute(""" > update foo > set > colA = (%s), > colB = (%s), > colC = (%s) > > where foo_id (%s)""", [11, 12, 13, 1]) > > I'm having a really hard time doing this without building up strings > and then appending them together. Is there some better way? No, not at the adapter level. It isn't hard to do this kind of string operations, but it's admittedly annoying for many reasons: the difference between INSERT and UPDATE syntax, the different escaping rules of the identifiers, having extra placeholders to be defined as %%s for two levels of parameters passage ecc. If you want to deal gracefully with this kind of dynamic SQL generation I suggest you to use a higher level library: specifically SQLAlchemy allows you to generate select/insert/update statements without throwing in the ORM part. See this example <http://www.sqlalchemy.org/docs/core/tutorial.html#insert-expressions> for a taste of it. And in any moment you can access the underlying psycopg connection if needed. Something that would be handy to make dynamic SQL generation easier would be a way to pass an identifier (such a table or field name) to a query, with different escaping rules than the strings. This can be already done now with an customized adapter but wouldn't work with prepared statement... with we currently don't do, but which may be added at some point. I'll try to gather all the points and have a discussion about the topic. -- Daniele
Re: How to execute an UPDATE query without string concatenation/interpolation?
From
Adrian Klaver
Date:
On Saturday 08 January 2011 1:38:14 pm Daniele Varrazzo wrote: > On Sat, Jan 8, 2011 at 5:33 PM, W. Matthew Wilson <matt@tplus1.com> wrote: > > I want to write a function that I can use like this > > > >>>> update_foo(foo_id=1, colA=11) > > > > and it do this internally: > > > > cursor.execute(""" > > update foo > > set colA = (%s) > > where foo_id (%s)""", [11, 1]) > > > > And I want to pass in more than just a single column to update, like this, too: > >>>> update_foo(foo_id=1, colA=11, colB=12, colC=13) > > > > and it should do this: > > > > cursor.execute(""" > > update foo > > set > > colA = (%s), > > colB = (%s), > > colC = (%s) > > > > where foo_id (%s)""", [11, 12, 13, 1]) > > > > I'm having a really hard time doing this without building up strings > > and then appending them together. Is there some better way? > > No, not at the adapter level. It isn't hard to do this kind of string > operations, but it's admittedly annoying for many reasons: the > difference between INSERT and UPDATE syntax, the different escaping > rules of the identifiers, having extra placeholders to be defined as > %%s for two levels of parameters passage ecc. > > If you want to deal gracefully with this kind of dynamic SQL > generation I suggest you to use a higher level library: specifically > SQLAlchemy allows you to generate select/insert/update statements > without throwing in the ORM part. See this example > <http://www.sqlalchemy.org/docs/core/tutorial.html#insert-expressions> > for a taste of it. And in any moment you can access the underlying > psycopg connection if needed. > > Something that would be handy to make dynamic SQL generation easier > would be a way to pass an identifier (such a table or field name) to a > query, with different escaping rules than the strings. This can be > already done now with an customized adapter but wouldn't work with > prepared statement... with we currently don't do, but which may be > added at some point. I'll try to gather all the points and have a > discussion about the topic. > > > -- Daniele Ways I have dealt with it: First from Postgres 8.2 on the UPDATE command supports a column list syntax that is the same as INSERT: http://www.postgresql.org/docs/8.2/interactive/sql-update.html " Use the alternative column-list syntax to do the same update: UPDATE weather SET (temp_lo, temp_hi, prcp) = (temp_lo+1, temp_lo+15, DEFAULT) WHERE city = 'San Francisco' AND date = '2003-07-03'; " Second Psycopg supports pyformat for variables. Combine this with the DictCursor from psycopg2.extras and a lot of the work is already done for you:) A quick example: sql_update = 'UPDATE '+self.table+' SET('+db_fields+')=' sql_update += '('+pyformat_str+')'+' WHERE ' sql_update += self.table_code+'_id='+row['id'] cur_update=self.con.cursor() cur_update.execute(sql_update,row) Where row is returned from a DictCursor. db_fields and pyformat_str are constructed from the same field with db_fields being of form 'field_1,field_2,field_3' and pyformat_str of format '%(field_1)s, %(field_2)s,%(field_3)s' -- Adrian Klaver adrian.klaver@gmail.com