{"id":270,"date":"2018-04-21T14:36:27","date_gmt":"2018-04-21T21:36:27","guid":{"rendered":"http:\/\/aklaver.org\/wordpress\/?p=270"},"modified":"2018-04-21T14:36:27","modified_gmt":"2018-04-21T21:36:27","slug":"building-dynamic-sql-using-psycopg2","status":"publish","type":"post","link":"https:\/\/aklaver.org\/wordpress\/2018\/04\/21\/building-dynamic-sql-using-psycopg2\/","title":{"rendered":"Building dynamic SQL using psycopg2"},"content":{"rendered":"<p>It has been awhile since I posted anything. Thought I would start back with a software<br \/>\npackage I use in many different situations, <a href=\"http:\/\/initd.org\/psycopg\/\">psycopg2<\/a> . Psycopg2 is a Python database adapter for Postgres that follows the Python DB API. It is feature rich and today I will introduce a feature new to the latest major release(2.7), namely the sql module for building complete SQL statements dynamically. I will give a quick run down of the basics below. For the official docs on the module see:<br \/>\n<a href=\"http:\/\/initd.org\/psycopg\/docs\/sql.html#module-psycopg2.sql\">http:\/\/initd.org\/psycopg\/docs\/sql.html#module-psycopg2.sql<\/a>.<br \/>\nFor example data I will be using the Pagila database (derived from the MySQL<br \/>\nSakila sample db) obtained from <a href=\"https:\/\/github.com\/devrimgunduz\/pagila\">here<\/a>.<\/p>\n<p>Setting up imports:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nfrom psycopg2 import connect, sql\r\nfrom psycopg2.extras import RealDictCursor\r\n<\/pre>\n<p>Create connection and cursor:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\ncon = connect(&quot;dbname=pagila host=localhost user=postgres&quot;, \r\n              cursor_factory=RealDictCursor)\r\ncur = con.cursor()\r\n<\/pre>\n<p>Build simple query string:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nqry_str = sql.SQL(&quot;SELECT {}, {} FROM {}&quot;).format(\r\nsql.Identifier('customer_id'),\r\nsql.Identifier(&quot;first_name&quot;),\r\nsql.Identifier(&quot;customer&quot;)\r\n)\r\nprint(qry_str.as_string(con))\r\nselect &quot;customer_id&quot;, &quot;first_name&quot; from &quot;customer&quot;\r\ncur.execute(qry_str)\r\nrs = cur.fetchone()\r\nrs\r\n{'customer_id': 1, 'first_name': 'MARY'}\r\n<\/pre>\n<p>Breaking the above down. SQL is a class used to build the string and as such has<br \/>\na format method that follows that for the Python str.format() method. See psycopg2<br \/>\ndocs above for exceptions. Identifier is a class that handles those strings to be used<br \/>\nas query identifiers e.g. column\/field names versus data values. Therefore in the<br \/>\nabove the &#8216;{}&#8217; are replaced in order by the Identifier values in the format().<br \/>\nTo verify the string you can use its as_string method provided you supply a connection<br \/>\nor cursor object to it. The query string is used in cur.execute() to fetch records<br \/>\nfrom the database. You can also build the query string directly in the execute()<br \/>\nto save a step.<\/p>\n<p>More compact\/flexible string construction:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nqry_str = sql.SQL(&quot;SELECT {} FROM {}&quot;).format(\r\nsql.SQL(&quot;,&quot;).join(&#x5B;sql.Identifier('customer_id'), \r\n                   sql.Identifier(&quot;first_name&quot;)]), \r\nsql.Identifier(&quot;customer&quot;)\r\n)\r\n<\/pre>\n<p>The above eliminates the need to provide a &#8216;{}&#8217; for each field in the SELECT<br \/>\nfield list. Instead the .join() method to SQL is used build a comma separated<br \/>\nlist of field names. Helpful if you may be receiving a variable number of field names<br \/>\nfor different iterations of the query. As example where the contents of fld_list<br \/>\nmay change:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nfld_list = &#x5B;&quot;customer_id&quot;, &quot;first_name&quot;, &quot;last_name&quot;, &quot;email&quot;]\r\nqry_str = sql.SQL(&quot;SELECT {} FROM {}&quot;).format(\r\nsql.SQL(&quot;,&quot;).join(map(sql.Identifier, fld_list)),\r\nsql.Identifier(&quot;customer&quot;)\r\n)\r\nprint(qry_str.as_string(con))\r\nselect \r\n&quot;customer_id&quot;,&quot;first_name&quot;,&quot;last_name&quot;,&quot;email&quot; \r\nfrom &quot;customer&quot;\r\n<\/pre>\n<p>Generating placeholders in the query. The sql module supports two types of placeholders,<br \/>\n%s and %(name)s. For %s placeholders a sequence of values need to be provided<br \/>\ne.g. a list or tuple. For named placeholders a dictionary is used to supply values.<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nplaceholder_str = sql.SQL(&quot;SELECT {} FROM {} WHERE first_name = {}&quot;).format(\r\nsql.SQL(&quot;,&quot;).join(map(sql.Identifier, fld_list)),\r\nsql.Identifier(&quot;customer&quot;),\r\nsql.Placeholder()\r\n)\r\nprint(placeholder_str.as_string(con))\r\nselect \r\n&quot;customer_id&quot;,&quot;first_name&quot;,&quot;last_name&quot;,&quot;email&quot; \r\nfrom &quot;customer&quot; where first_name = %s\r\ncur.execute(placeholder_str, &#x5B;&quot;MARY&quot;])\r\nrs = cur.fetchone()\r\nrs\r\n{'customer_id': 1,\r\n'email': 'MARY.SMITH@sakilacustomer.org',\r\n'first_name': 'MARY',\r\n'last_name': 'SMITH'}\r\n<\/pre>\n<p>In the above %s is created as the placeholder for the first_name field value in the<br \/>\nWHERE clause using the Placeholder class. To create a named placeholder the procedure is:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nplaceholder_str = sql.SQL(&quot;SELECT {} FROM {} WHERE first_name = {}&quot;).format(\r\nsql.SQL(&quot;,&quot;).join(map(sql.Identifier, fld_list)),\r\nsql.Identifier(&quot;customer&quot;),\r\nsql.Placeholder(name='first_name')\r\n)\r\nprint(placeholder_str.as_string(con))\r\nselect \r\n&quot;customer_id&quot;,&quot;first_name&quot;,&quot;last_name&quot;,&quot;email&quot; \r\nfrom &quot;customer&quot; where first_name = %(first_name)s\r\ncur.execute(placeholder_str, {&quot;first_name&quot;: &quot;MARY&quot;})\r\n<\/pre>\n<p>Where this leads to is a building a query string from data sourced from a Python dictionary:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nnew_customer_list = &#x5B;\r\n{&quot;store_id&quot;: 1, &quot;first_name&quot;: &quot;ADRIAN&quot; , &quot;last_name&quot;: &quot;KLAVER&quot;, \r\n&quot;email&quot;: &quot;ADRIAN.KLAVER@sakilacustomer.org&quot;, &quot;address_id&quot;: 67},\r\n{&quot;store_id&quot;: 2, &quot;first_name&quot;: &quot;JANE&quot; , &quot;last_name&quot;: &quot;DOE&quot;, \r\n&quot;email&quot;: &quot;JANE.DOE@sakilacustomer.org&quot;, &quot;address_id&quot;: 7},\r\n{&quot;store_id&quot;: 1, &quot;first_name&quot;: &quot;WHO&quot; , &quot;last_name&quot;: &quot;KNOWS&quot;, \r\n&quot;email&quot;: &quot;WHO.KNOWS@sakilacustomer.org&quot;, &quot;address_id&quot;: 189 }\r\n]\r\n\r\ninsert_flds = &#x5B;fld_name for fld_name in new_customer_list&#x5B;0].keys()]\r\n\r\ninsert_str = sql.SQL(&quot;INSERT INTO customer ({}) VALUES ({})&quot;).format(\r\nsql.SQL(&quot;,&quot;).join(map(sql.Identifier, insert_flds)),\r\nsql.SQL(&quot;,&quot;).join(map(sql.Placeholder, insert_flds))\r\n)\r\n\r\nfrom psycopg2.extras import execute_batch\r\n\r\nexecute_batch(cur, insert_str, new_customer_list)\r\n<\/pre>\n<p>I snuck in another new feature from psycopg2 2.7, execute_batch. This is way of batching statements for fewer round trips to the server, by default 100. For this example not really a win, but it is nice to know it is out there. For cases where I have a lot of data to transfer to a table I use psycopg2&#8217;s COPY features whenever possible. That is a subject for another day though.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It has been awhile since I posted anything. Thought I would start back with a software package I use in many different situations, psycopg2 . Psycopg2 is a Python database adapter for Postgres that follows the Python DB API. It &hellip; <a href=\"https:\/\/aklaver.org\/wordpress\/2018\/04\/21\/building-dynamic-sql-using-psycopg2\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[5],"tags":[],"class_list":["post-270","post","type-post","status-publish","format-standard","hentry","category-postgres"],"_links":{"self":[{"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/posts\/270","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/comments?post=270"}],"version-history":[{"count":26,"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/posts\/270\/revisions"}],"predecessor-version":[{"id":308,"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/posts\/270\/revisions\/308"}],"wp:attachment":[{"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/media?parent=270"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/categories?post=270"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/aklaver.org\/wordpress\/wp-json\/wp\/v2\/tags?post=270"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}