Passing a Date variable to a sql query with python - python

I´m trying to write a sql query in python, where I want the user to pass a date, saved in a variable. Then I want this to be processed in the query. While it´s pretty easy to do so in R, I´m curious if there´s an easy approach in python too.
The Date should be passed where 2022-12-12 is.
date = input("this format YYYY-MM-DD")
query_new_Limits = """
SELECT #######
, a.RULE_NAME
, a.RESULT_VALUE
, a.SUMMARY_DETAIL
, a.REF_DATE
FROM ######## a ################### b
ON b.T_PORTFOLIO_ID = a.PORTFOLIO_ID
WHERE a.REF_DATE = TO_DATE('2022-12-12','YYYY-MM-DD')
AND REGEXP_LIKE(a.PORTFOLIO_CODE,'^[[:digit:]]+$')
AND NOT b.POR_INVESTMENT_TYPE IN #######
AND b.REF_TO_DATE > TO_DATE('2022-12-12','YYYY-MM-DD')
AND a.RESULT_STATE_ID > 12
"""

Just a quick advice:
avoid "date" as variable name as it's a reserved word
maybe the underlying driver is not using parameters "bind by name" - try passing an array of parameters as ( '2022-12-31', '2022-12-31') or such, just to rule out the parameter name issue

From the docs
SQL and PL/SQL statements that pass data to and from Oracle Database should use placeholders in SQL and PL/SQL statements that mark where data is supplied or returned. These placeholders are referred to as bind variables or bind parameters. A bind variable is a colon-prefixed identifier or numeral. For example, there are two bind variables (dept_id and dept_name) in this SQL statement:
sql = """insert into departments (department_id, department_name)
values (:dept_id, :dept_name)"""
cursor.execute(sql, [280, "Facility"])
[or named binding]
cursor.execute("""
insert into departments (department_id, department_name)
values (:dept_id, :dept_name)""", dept_id=280,
dept_name="Facility")
Which applied to your example gives:
date = input("this format YYYY-MM-DD")
query_new_Limits = """
SELECT #######
, a.RULE_NAME
, a.RESULT_VALUE
, a.SUMMARY_DETAIL
, a.REF_DATE
FROM ######## a ################### b
ON b.T_PORTFOLIO_ID = a.PORTFOLIO_ID
WHERE a.REF_DATE = TO_DATE(:date,'YYYY-MM-DD')
AND REGEXP_LIKE(a.PORTFOLIO_CODE,'^[[:digit:]]+$')
AND NOT b.POR_INVESTMENT_TYPE IN #######
AND b.REF_TO_DATE > TO_DATE(:date,'YYYY-MM-DD')
AND a.RESULT_STATE_ID > 12
"""
cursor.execute(query_new_Limits, date=date) # using kwarg binding

Related

How to use pandas list as variable in SQL query using python?

I am using the Pandas library on Python and was wondering if there is a way to use a list variable (or perhaps series is better?), let's say uID_list, in an SQL query that is also executed within the same Python code. For example:
dict = {'a': 1, 'b': 2, 'c':3}
uID_series = pd.Series(data=dict, index=['a','b','c'])
uID_list = uID_series.toList()
and let's assume this uID_list can be changed down the road, so it does not always consist of 1, 2, 3.
How can I "plug in" this uID_list variable in the following SQL query?
sql =
f'''
CREATE PROCEDURE SearchForUsers(#uIDinput AS INT(11))
AS
BEGIN
SELECT username
FROM users_table
WHERE uID in #uIDinput
END
EXECUTE SearchForUsers {uID_list}
'''
Note that creating a new table in the database is not an option for me, as it is work related and my supervisor is strict on keeping the database clean.
What I'm essentially trying to do is see a selection of usernames in users_table table, where the uIDs in users_table match a varying collection of uIDs stored, which is given by uID_list. And uID_list depends on the rest of the python code where it can get changed and manipulated.
Are you just trying to pull the data? Will be easier outside of a stored procedure
l1 = ['ad', 'dfgdf', 'htbgf', 'dtghyt']
l1_str = "('" + "', '".join([str(item) for item in l1]) + "')"
sql =
f'''
SELECT username
FROM users_table
WHERE uID in {l1_str}
'''
You can start by converting your list to a tuple then pass it as a parameter of pandas.read_sql_query.
Assuming cnx is the name of your connection, try this :
uID_tuple = tuple(uID_list)
sql =
'''
CREATE PROCEDURE SearchForUsers(#uIDinput AS INT(11))
AS
BEGIN
SELECT username
FROM users_table
WHERE uID in #uIDinput
END
EXECUTE SearchForUsers ?
'''
pd.read_sql_query(sql, cnx, params=[uID_tuple])

%s variable in Query Execution Python 3.8 (pymssql)

I have a python script with a basic GUI that logs into a DB and executes a query.
The Python script also asks for 1 parameter called "collection Name" which is taken from the tkinter .get function and is added as a %s inside the Query text. The result is that each time I can execute a query with a different "Collection name". This works and it is fine
Now, I want to add a larger string of Collection Names into my .get function so I can do cursor.execute a query with multiple collection names to get more complex data. But I am having issues with inputing multiple "collection names" into my app.
Below is a piece of my Query1, which has the %s variable that it then gets from the input to tkinter.
From #Session1
Join vGSMRxLevRxQual On(#Session1.SessionId = vGSMRxLevRxQual.SessionId)
Where vGSMRxLevRxQual.RxLevSub<0 and vGSMRxLevRxQual.RxLevSub>-190
and #Session1.CollectionName in (%s)
Group by
#Session1.Operator
Order by #Session1.Operator ASC
IF OBJECT_ID('tempdb..#SelectedSession1') IS NOT NULL DROP TABLE #SelectedSession1
IF OBJECT_ID('tempdb..#Session1') IS NOT NULL DROP TABLE #Session1
Here, is where I try to execute the query
if Query == "GSMUERxLevelSub" :
result = cursor.execute(GSMUERxLevelSub, (CollectionName,))
output = cursor.fetchmany
df = DataFrame(cursor.fetchall())
filename = "2021_H1 WEEK CDF GRAPHS().xlsx"
df1 = DataFrame.transpose(df, copy=False)
Lastly, here is where I get the value for the Collection name:
CollectionName = f_CollectionName.get()
enter image description here
enter code here
Your issues are due to a list/collection being a invalid parameter.
You'll need to transform collectionName
collection_name: list[str] = ['collection1', 'collection2']
new_collection_name = ','.join(f'"{c}"' for c in collection_name)
cursor.execute(sql, (new_collection_name,))
Not sure if this approach will be susceptible to SQL injection if that's a concern.
Edit:
Forgot the DBAPI would put another set of quotes around the parameters. If you can do something like:
CollectionName = ["foo", "bar"]
sql = f"""
From #Session1
Join vGSMRxLevRxQual On(#Session1.SessionId = vGSMRxLevRxQual.SessionId)
Where vGSMRxLevRxQual.RxLevSub<0 and vGSMRxLevRxQual.RxLevSub>-190
and #Session1.CollectionName in ({",".join(["%s"] * len(CollectionName))})
"""
sql += """
Group by
#Session1.Operator
Order by #Session1.Operator ASC
"""
cursor.execute(sql, (CollectionName,))
EDIT: Update to F-string

Avoid python interpreting % as a placeholder in like mysql clause

I'm trying to create a DataFrame through a sql query with pandas read_sql_query method. The query has a where clause that includes a like operation but it also includes a = operation that depends on a variable. The issue is that python is interpreting the % in the like operation as a place holder, just like in the = variable operation which is something I DO want.
Here's an example of it:
sql_string = """ SELECT a,b from table WHERE a = %(variable)s
AND b like '%fixed_chars%' """
params = {'variable':'AA'}
df = pandas.read_sql_query(sql_string, params=params, con=connection)
The error that I get is TypeError: not enough arguments for format string since it interprets the % you usually use as wildcard in mysql as the place holder in python.
In this case, you'll have to use two % for those not being formatting placeholders:
sql_string = "SELECT a,b from table WHERE a = %(variable)s AND \
b like '%%fixed_chars%%'"
Hope this helps!

Column Name as Variable in PostgreSql Query

i was trying to update a table row by row in postgresql using python.
the code used was
cursor.execute("UPDATE im_entry.pr_table
SET selected_entry = im_entry.usr_table.",entryn,"
FROM im_entry.usr_table
WHERE im_entry.pr_table.image_1d = ",idn,"")
...where entryn and idn are two string variables ( entry1,entry2.. id1,id2..etc)
I am getting an error
TypeError: function takes at most 3 arguments (5 given)
My table is
image_1d | entry1 | entry2 | entry3 | entry4 | entry5
----------+--------+--------+--------+--------+--------
How can I solve this?
Try:
cursor.execute(
'''UPDATE im_entry.pr_table
SET selected_entry = im_entry.usr_table.{0}
FROM im_entry.usr_table
WHERE im_entry.pr_table.image_1d = ?'''.format(entryn),[idn])
Normally, you would want to call cursor.execute(sql,args), where sql is a parametrized sql query, and args is a list or tuple of values to be substituted for the parameter placeholders.
For Postgresql, the usual db driver, pyscopg, uses question marks for the parameter placeholders.
Thus, usually, you'd want to use something like
sql='''UPDATE im_entry.pr_table
SET selected_entry = ?
FROM im_entry.usr_table
WHERE im_entry.pr_table.image_1d = ?'''
but in your case, you aren't trying to set selected_entry to a specific value, but rather to a column name. Is that correct? In that case, you unfortunately can't use a parameter placeholder. Instead, you have to use string formatting, which is what I suggested above.
You can't bind table or column names, only the values associated with them.

Python sqlite parameter extension problem

I have a table with three columns, cell, trx and type.
This is the query I'm trying to run:
db.execute("SELECT cell,trx FROM tchdrop").fetchall()
It gives the correct output.
However when I try a = ("cell", "trx") and then
db.execute("SELECT ?,? FROM tchdrop", t).fetchall()
the output is [(u'cell', u'trx'), (u'cell', u'trx')] (which is wrong)
I'm doing this to figure out how to extract columns dynamically which is a part of a bigger problem.
The place holder (?) of python DB-API (like sqlite3) don't support columns names to be passed, so you have to use python string formatting like this:
a = ("cell", "trx")
query = "SELECT {0},{1} FROM tchdrop".format(*a)
db.execute(query)
EDIT:
if you don't know the length of the columns that you want to pass , you can do something like this:
a = ("cell", "trx", "foo", "bar")
a = ", ".join(a)
query = "SELECT {0} FROM tchdrop".format(a)
# OUTPUT : 'SELECT cell, trx, foo, bar FROM tchdrop'
db.execute(query)
The library replaces the specified values ("cell", "trx") with their quoted SQL equivalent, so what you get is SELECT "cell", "trx" FROM tchdrop. The result is correct.
What you are trying to achieve is not possible with the ? syntax. Instead, do string replacement yourself. You can check column names with regular expressions (like ^[a-zA-Z_]$) for more security.
For example:
columns = ",".join(("cell", "trx"))
db.execute("SELECT %s FROM tchdrop" % columns).fetchall()

Categories

Resources