Updating MySQL table using Python loop and escape - python

I wrote a python script to update currency exchange rates using API calls. I successfully parsed the json results and extracted individual exchange rates as floats. I am however struggling with formatting/implementing an SQL table update loop.
Here is a code snippet that is tripping me up, assume that val = an actual exchange rate variable that is supplied from the API fetching/parsing section of the code:
mycursor = mydb.cursor()
sql = "UPDATE currencies SET coefficient = %s"
val = 0.03137
mycursor.execute(sql, val)
mydb.commit()
Running this gives me the following error:
Could not process parameters: float(0.03137), it must be of type list, tuple or dict:
I do not even know what to search for in order to reach an explanation that I understand and that helps me implement what I want correctly.

From: https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html
Note
In Python, a tuple containing a single value must include a comma. For
example, ('abc') is evaluated as a scalar while ('abc',) is evaluated
as a tuple.
Thus in my example the expression that works is:
val = (0.03137, )

Related

Python(Flask,JayDeBeApi) RuntimeError: No matching overloads found for prepareStatement

as mentioned in the title i get this error when i try to execute a prepared statement. The full error is:
RuntimeError: No matching overloads found for prepareStatement in find. at native\common\jp_method.cpp:127
As far as i can understand is, that propably that since i am trying to use a prepared statement, that the compiler can not find something to overload the ? placeholder.
Code snippet:
curs = self.dbconn.cursor()
sqlLogin = ("SELECT name,email FROM BENUTZER where name=? and email=?", ( benutzerObjekt.name,benutzerObjekt.email))
curs.execute(sqlLogin)
The error seems to happen at curs.execute(sqlLogin), which is shown to me in the traceback when debugging.
I'm trying to use the input of an html input, which is stored in benutzerObjekt.name and benutzerObjekt.email as input for the select SQL statement. So most probably something is wrong with either my SQL statement, or the execution of the statement, which is underlined when debugging. I am using db2.
Thanks in advance!
You need to pass parameters as second argument in cursor.execute. Right now your query is a nested tuple of two items with first being a string and second item being a tuple of two values.
Consider separating the arguments for function call:
curs = self.dbconn.cursor()
# SINGLE STRING VALUE
sqlLogin = "SELECT name,email FROM BENUTZER WHERE name=? AND email=?"
# TUPLE OF TWO VALUES
vals = (benutzerObjekt.name, benutzerObjekt.email)
# PASS SQL AND PARAMS SEPARATELY
curs.execute(sqlLogin, vals)
Alternatively, you can unpack your nested tuple using asterisk, *:
sqlLogin = (
"SELECT name,email FROM BENUTZER where name=? and email=?",
(benutzerObjekt.name, benutzerObjekt.email)
)
curs.execute(*sqlLogin)

Pass a Python Variable containing multiple ID Numbers into external BigQuery Script

I have created a python class, and one of my methods is meant to take in either a single ID number or a list of ID numbers. The function will then use the ID numbers to query from a table in BigQuery using a .sql script. Currently, the function works fine for a single ID number using the following:
def state_data(self, state, id_number):
if state == 'NY':
sql_script = self.sql_scripts['get_data_ny']
else:
sql_script = self.sql_scripts['get_data_rest']
sql_script = sql_script.replace('##id_number##', id_number)
I'm having issues with passing in multiple ID numbers at once. There are 3 different ways that I've tried without success:
The above method, passing in the multiple ID numbers as a tuple to use with WHERE ID_NUM IN('##id_number##'). This doesn't work, as when the .sql script gets called, a syntax error is returned, as parentheses and quotes are automatically added. For example, the SQL statement attempts to run as WHERE ID_NUM IN('('123', '124')'). This would run fine without one of the two sets of parentheses and quotes, but no matter what I try to pass in, they always get added.
The second technique I have tried is to create a table, populate it with the passed in ID numbers, and then join with the larger table in BQ. It goes as follows:
CREATE OR REPLACE TABLE ID_Numbers
(
ID_Number STRING
);
INSERT INTO ID_Numbers (ID_Number)
VALUES ('##id_number##');
-- rest of script is a simple left join of the above created table with the BQ table containing the data for each ID
This again works fine for single ID numbers, but passing in multiple VALUES (in this case ID Numbers) would require a ('##id_number##') per unique ID. One thing that I have not yet attempted - to assign a variable to each unique ID and pass each one in as a new VALUE. I am not sure if this technique will work.
The third technique I've tried is to include the full SQL query in the function, rather than calling a .sql script. The list of ID numbers get passed in as tuple, and the query goes as follows:
id_nums = tuple(id_number)
query = ("""SELECT * FROM `data_table`
WHERE ID_NUM IN{}""").format(id_nums)
This technique also does not work, as I get the following error:
AttributeError: 'QueryJob' object has no attribute 'format'.
I've attempted to look into this error but I cannot find anything that helps me out effectively.
Finally, I'll note that none of the posts asking the same or similar questions have solved my issues so far.
I am looking for any and all advice for a way that I can successfully pass a variable containing multiple ID numbers into my function that ultimately calls and runs a BQ query.
You should be able to use *args to get the id_numbers as a sequence and f-strings and str.join() to build the SQL query:
class MyClass:
def state_data(self, state, *id_numbers):
print(f"{state=}")
query = f"""
SELECT * FROM `data_table`
WHERE ID_NUM IN ({", ".join(str(id_number) for id_number in id_numbers)})
"""
print(query)
my_class = MyClass()
my_class.state_data("some state", 123)
my_class.state_data("some more state", 123, 124)
On my machine, this prints:
➜ sql python main.py
state='some state'
SELECT * FROM `data_table`
WHERE ID_NUM IN (123)
state='some more state'
SELECT * FROM `data_table`
WHERE ID_NUM IN (123, 124)

cx_Oracle execute/executemany error when updating table

I need conditionaly update Oracle table from my Python code. It's a simple piece of code, but I encountered cx_Oracle.DatabaseError: ORA-01036: illegal variable name/number with following attempts
id_as_list = ['id-1', 'id-2'] # list of row IDs in the DB table
id_as_list_of_tuples = [('id-1'), ('id-2')] # the same as list of tuples
sql_update = "update my_table set processed = 1 where object_id = :1"
# then when I tried any of following commands, result was "illegal variable name/number"
cursor.executemany(sql_update, id_as_list) # -> ends with error
cursor.executemany(sql_update, id_as_list_of_tuples) # -> ends with error
for id in id_as_list:
cursor.execute(sql_update, id) # -> ends with error
Correct solution was to use list of dictionaries and the key name in the SQL statement:
id_as_list_of_dicts = [{'id': 'id-1'}, {'id': 'id-2'}]
sql_update = "update my_table set processed = 1 where object_id = :id"
cursor.executemany(sql_update, id_as_list_of_dicts) # -> works
for id in id_as_list_of_dicts:
cursor.execute(sql_update, id) # -> also works
I've found some helps and tutorials like this and they all used ":1, :2,..." syntax (but on the other hand I haven't found any example with update and cx_Oracle). Although my issue has been solved with help of dictionaries I wonder if it's common way of update or if I do something wrong in the ":1, :2,..." syntax.
Oracle 12c, Python 3.7, cx_Oracle 7.2.1
You can indeed bind with dictionaries but the overhead of creating the dictionaries can be undesirable. You need to make sure you create a list of sequences when using executemany(). So in your case, you want something like this instead:
id_as_list = [['id-1'], ['id-2']] # list of row IDs in the DB table
id_as_list_of_tuples = [('id-1',), ('id-2',)] # the same as list of tuples
In the first instance you had a list of strings. Strings are sequences in their own right so in that case cx_Oracle was expecting 4 bind variables (the number of characters in each string).
In the second instance you had the same data as the first instance -- as you were simply including parentheses around the strings, not creating tuples! You need the trailing comma as shown in my example to create tuples as you thought you were creating!

How can Python do string substitution without %s?

Check out the following lines of Python 2.6 code I found:
key = 'hire_date'
update_dict['key'] = update_dict[key] #added e.g. {..., 'key': '12/31/1999'}
if key == 'hire_date':
query_string = "UPDATE employee SET " + key + "= CAST(%(key)s AS DATE) WHERE emp_id = '" + emp.employee + "'"
I've tested this code, and it works. It successfully updates the employee's hire_date field in the database to whatever date 'key''s value in the dictionary is.
I was in the middle of parameterizing it when I noticed the %(key)s somehow manages to get the value of the dictionary at 'key'. How does it do that? I always thought you had to add % dictionaryOrTupleOrWhatever after the string for this to work.
In the code that you pasted, no interpolation takes place. However, the variable could later be interpolated:
>>> x = "%(var)s" # no interpolation yet
>>> d = {'var': 88}
>>> x % d # interpolate into the stored string
'88'
Given that the code you posted looks like SQL, it could also be interpolated later via an SQL library call. Many SQL interface libraries provide a similar sort of string-substitution using % signs and encourage users to use these rather than the built-in string substitution, since the SQL library versions have various safeguards to prevent malicious injection attacks.
I bet you'll find later in the code that there is a DB API execute statement that takes update_dict as a parameter. The DB API then does the substitution instead of Python string formatting and thus properly handles binding.
Have a look at this: http://furius.ca/pubcode/pub/antiorm/lib/python/dbapiext.html#escaping-in-the-dbapi-2-0

Python: using pyodbc and replacing row field values

I'm trying to figure out if it's possible to replace record values in a Microsoft Access (either .accdb or .mdb) database using pyodbc. I've poured over the documentation and noted where it says that "Row Values Can Be Replaced" but I have not been able to make it work.
More specifically, I'm attempting to replace a row value from a python variable. I've tried:
setting the connection autocommit to "True"
made sure that it's not a data type issue
Here is a snippet of the code where I'm executing a SQL query, using fetchone() to grab just one record (I know with this script the query is only returning one record), then I am grabbing the existing value for a field (the field position integer is stored in the z variable), and then am getting the new value I want to write to the field by accessing it from an existing python dictionary created in the script.
pSQL = "SELECT * FROM %s WHERE %s = '%s'" % (reviewTBL, newID, basinID)
cursor.execute(pSQL)
record = cursor.fetchone()
if record:
oldVal = record[z]
val = codeCrosswalk[oldVal]
record[z] = val
I've tried everything I can think bit cannot get it to work. Am I just misunderstanding the help documentation?
The script runs successfully but the newly assigned value never seems to commit. I even tried putting "print str(record[z])this after the record[z] = val line to see if the field in the table has the new value and the new value would print like it worked...but then if I check in the table after the script has finished the old values are still in the table field.
Much appreciate any insight into this...I was hoping this would work like how using VBA in MS Access databases you can use an ADO Recordset to loop through records in a table and assign values to a field from a variable.
thanks,
Tom
The "Row values can be replaced" from the pyodbc documentation refers to the fact that you can modify the values on the returned row objects, for example to perform some cleanup or conversion before you start using them. It does not mean that these changes will automatically be persisted in the database. You will have to use sql UPDATE statements for that.

Categories

Resources