I am trying to write a dynamic SQL command using Python / Postgres. In my where clause I want to use a query parameter (which is user defined) that has to look for NULL values within a code column (varchar), but in other cases also for specific numbers.
If I have to check for a certain value I use this:
cursor.execute(""" select z.code as code from s_order as o LEFT JOIN s_code as z ON o.id = z.id where z.code = %(own_id)s; """, { 'own_id': entry })
However if I have to check for NULL values the SQL and more specifically the WHERE clause would have to be
select z.code as code from s_order as o LEFT JOIN s_code as z ON o.id = z.id WHERE z.code IS NULL;
The query parameter as used in the first statement does not work for the second since it can only replace the value on the right side of the equation, but not the operator. I have read here (https://realpython.com/prevent-python-sql-injection/#using-query-parameters-in-sql) that table names can also be substituted using SQL identifiers provided by psycopg2, but could not find out how to replace a whole WHERE clause or at least the operator.
Unfortunately I cannot change the NULL values in the code column (e.g. using a default value) since these NULL values are created through the JOIN operation.
My only option at the moment would be to have different SQL queries based on the input value, but since the SQL query is quite long (I shortened it for this question) and I have many similar queries it would result in a lot of similar code…So how can I make this WHERE clause dynamic?
EDIT: In addition to the answer marked as correct, I want to add my own solution, which is not so elegant, but might be helpful in more complicated scenarios, as the NULL fields are replaced with the ‘default’ value of COALESCE:
create view orsc as select coalesce(z.code), 'default') as code from s_order as o LEFT JOIN s_code as z ON o.id = z.id; SELECT orsc.code as code2 from orsc WHERE code2 = 'default'; //or any other value
EDIT2: See comments of marked answer why a view is probably not necessary at all.
EDIT3: This question is not helpful since it asks only for checking for NULL values. Besides this an IF statement as shown in the answer would substantially increase my whole codebase (each query is quite long and is used often in slightly adapted ways).
Advertisement
Answer
Consider COALESCE
to give NULL
a default value. Below assumes z.code
is a varchar or text. If a integer/numeric, change 'default'
to a number value (e.g., 9999).
sql = """SELECT z.code as code FROM s_order as o LEFT JOIN s_code as z ON o.id = z.id WHERE COALESCE(z.code, 'default') = %(own_id)s; """ cursor.execute(sql, {'own_id': entry}) cursor.execute(sql, {'own_id': 'default'}) # RETURNS NULLs IN z.code