Python SQL NOT LIKE , trying to exclude multiple values - python

I have a code, below, where a user can put a list of allergies. I want my code to come up with a list of recipes, that exclude the ingredients from the user allergy list. I made a test code but cannot figure out how to exclude all three ingredients at the same time.
userallergy = conn.execute ('SELECT Allergies from User where userid = 4')
userallergy = userallergy.fetchall()
userallergy = userallergy[0][0].replace(" ","")
listallergy=list(userallergy.split(","))
listallergy = ["'%" + i.capitalize() + "%'" for i in listallergy]
print([listallergy])
query='SELECT RecipeName FROM Recipess where Ingredients Not LIKE {}'.format(listallergy[1])
print(query)
aller = conn.execute(query)
saferecipe = aller.fetchall()
print(saferecipe)

Use REGEXP for a clean approach(Though you can't use it directly, check out the answer of Problem with regexp python and sqlite ):
def regexp(expr, item):
reg = re.compile(expr)
return reg.search(item) is not None
conn.create_function("REGEXP", 2, regexp)
allergies = "|".join([i.capitalize() for i in listallergy])
print(allergies)
query='SELECT RecipeName FROM Recipess where Ingredients Not REGXP \'{}\''.format(allergies)
print(query)
A simple approach is to join all the statements by AND operator(but as #alexis mentioned in comments it comes with it's own issues):
allergies = " AND ".join(["Ingredients Not LIKE '%" + i.capitalize() + "%'" for i in listallergy])
print(allergies)
query='SELECT RecipeName FROM Recipess where {}'.format(allergies)
print(query)

You may use MySQL REGEX 'OR' to exclude all three. Your query should then look like this:
query = f"SELECT RecipeName FROM `Recipess` WHERE Ingredients NOT REGEXP '{('|').join(listallergy)}'"

Related

How to efficiently submit multiple SQL query strings in Python?

I have a python function that takes in a list of people (first name, last name, and date of birth stored in a dict), loops through this list and queries the database person by person, and returns a list containing the results found.
So for example if the user would want to query 3 people using the list [{'name': 'Alice'}, {'name': 'Bob'}, {'name': 'Charlie'}], the function would query the database 3 times, each time getting a list of potential matches that gets put into another list. The could return a list of 3 lists each containing matching results for Alice, Bob and Charlie. The function looks like the following.
query_string = "SELECT * FROM some_db"
def improved_batch_client_check(user_requests, db_connection):
responses = []
for one_request in user_requests:
first_name = one_request.get('FirstName')
last_name = one_request.get('LastName')
dob = one_request.get('BirthDate')
query_string_end = " WHERE db.NAME LIKE '%{}%' AND db.NAME LIKE '%{}%'".format(first_name, last_name)
if dob is None:
pass
else:
query_string_end += " AND db.DOB = '{}'".format(str(dob))
df_candidates = pandas.read_sql(query_string + query_string_end, db_connection)
responses.append(df_candidates.to_dict('records'))
return jsonify(responses)
What I want to do is to improve efficiency by having to call the function pandas.read_sql() only once by somehow submitting a list of different queries that returns dataframes in a similar manner to what is used below. What is the easiest way to do it in Python?
In order to make it one query I would create a query of or's, that is -
select * from table where (request1) or (request2) ...
Each request is of the form if dob is not given or -
db.NAME LIKE '%{}%' AND db.NAME LIKE '%{}%'".format(first_name, last_name)
and if dob is given -
db.NAME LIKE '%{}%' AND db.NAME LIKE '%{}%' AND db.DOB = '{}'".format(first_name, last_name, str(dob))
Putting it all together -
def improved_batch_client_check(user_requests, db_connection):
requests = []
for one_request in user_requests:
first_name = one_request.get('FirstName')
last_name = one_request.get('LastName')
dob = one_request.get('BirthDate')
curr_request = "db.NAME LIKE '%{}%' AND db.NAME LIKE '%{}%'".format(first_name, last_name)
if dob is not None:
curr_request += " AND db.DOB = '{}'".format(str(dob))
requests.append(curr_request)
query_string = "SELECT * FROM some_db WHERE " + 'or '.join([f'({x})' for x in requests])
df = pandas.read_sql(query_string, db_connection)
return jsonify (df.to_dict('records'))

Sqlite select column text with multple words

So i have a table with all of the products and their language availability. I want to write a function to check the language availability when input the product name and language.
My code is as follow:
"CREATE TABLE t (Language,French, Italian, German, Spanish, Japanese, Korean, 'Traditional Chinese');")
//insert data to table t
def checkLanguageAvailability(self, product, language):
query = "SELECT " + language + " FROM t WHERE Language = '" + product + "'"
cur = self.df.cursor()
cur.execute(query)
# print cur.fetchall()
res = cur.fetchall()
if res[0][0] == '':
return False
elif int(float(res[0][0])) != 0:
return True
so when i test it , it all works fine with one-word text
checkLanguageAvailability("productname",'French')) --> True
But with multiple-word text
checkLanguageAvailability("productname",'Traditional Chinese'))
it raise this error :
cur.execute(query)
sqlite3.OperationalError: no such column: Traditional
It seems that instead of taking the whole string 'Traditional Chinese' as a parameter, it just take 'traditional' and there is no column have this name in table
I disagree with your table structure and also with your code. Adding a new column for each language is costly and maximally inflexible. This approach requires a major schema change each time you decide to support a new language. In addition, your current concatenated query string is prone to SQL injection. Beyond this, you should generally not make the column names in a query as parameters. When you find yourself doing this, it might indicate bad design or a hack. Instead, I propose the following table:
CREATE TABLE t (language TEXT, product TEXT)
This design represents the presence of a given product and language as a single row. Hence, if we find a record entry for a given product and language then we know it is present.
Try using code something like the following:
def checkLanguageAvailability(self, product, language):
cur = self.df.cursor()
cmd = cur.execute("SELECT 1 FROM t WHERE product = ? AND language = ?", (product, language))
res = cur.fetchall()
cnt = len(res)
if cnt == 0
return False
else
return True
Use LIKE:
def checkLanguageAvailability(self, product, language):
query = "SELECT " + language + " FROM t WHERE Language LIKE '%" + product + "%'"
cur = self.df.cursor()
cur.execute(query)
# print cur.fetchall()
res = cur.fetchall()
if res[0][0] == '':
return False
elif int(float(res[0][0])) != 0:
return True
And is this query accepting any external input? Because if so, you should use prepared statements.

how to see full query with values in ponyorm

Today i am using the sql_debug(True) that helps me to see the queries but without the values.
How could i see how ponyorm translate the query with values ?
Thank you very much.
This is an example of query i'm using.
with db_session:
access = select(p for p in Access if raw_sql('( lower(first_name) = lower($first_name) and lower(last_name) = lower($last_name) ) '
'or ( lower(first_name) = lower($last_name) and lower(last_name) = lower($first_name) ) '
'or (lower(facebook_url) = lower($facebook_url)) '
'or (lower(twitter_url) = lower($twitter_url)) '
'or (lower(linkedin_url) = lower($linkedin_url)) '))
.order_by(desc(Access.twitter_url),desc(Access.facebook_url),desc(Access.linkedin_url),
desc(Access.facebook_url))
print(access.get_sql())
I use
logging.getLogger(__name__).debug('SQL:\n\n\t\t\t%s\n', '\n'.join(unicode(x) for x in request._construct_sql_and_arguments()[:2]).replace('\n', '\n\t\t\t'))
for that.
For example,
19:30:01.902 data.py:231 [DEBUG] SQL:
SELECT "x"."_id", "x"."filename", "x"."_created", "x"."_updated"
FROM "reports" "x"
WHERE "x"."_id" <= ?
AND "x"."_created" >= ?
(50, '2019-04-17 19:30:01.900028')
will be printed out.
You can use set_sql_debug(debug=True, show_values=True).
Reference here.
There is a method called get_sql()
query_obj = select(c for c in Category if c.name.startswith('v'))
sql = query_obj.get_sql()
print(sql)
output:
SELECT "c"."id", "c"."name"
FROM "category" "c"
WHERE "c"."name" LIKE 'v%%'
code continue:
for obj in query_obj:
print('id:', obj.id, 'name:', obj.name)
output:
id: 1 name: viki
here is a link to the docs https://docs.ponyorm.com/api_reference.html#Query.get_sql
You can log the sql or simply print it.
Update:
OP updated the question:
If the sql query has a variable like $name it is passed as a sql parameter.
first_name = 'viki'
query = select(c for c in Category if raw_sql('( lower(name) = lower($first_name))'))
query.get_sql()
so get_sql() will return the value with a placeholder, and the output will look like this:
'SELECT "c"."id", "c"."name", "c"."age"\nFROM "Category" "c"\nWHERE ( lower(name) = lower(?))'
If we want no placeholders should be there in the query then we can avoid passing direct sql to query and instead build it separately in python.
Like this:
query = select(c for C in Category if c.name == 'viki')
query.get_sql()
output:
'SELECT "c"."id", "c"."name", "c"."age"\nFROM "Category" "c"\nWHERE "c"."name" = \'viki\''

mysql.connector select query with binary values

Using Python's mysql.connector, how can I select all records that match a tuple?
For example:
ids = (b'6TRsHMzWSrq0zeembo0Vbg',
b'7Qoq53lKTk-RZOi830t3MA',
b'7ZGO9S3DTcGHjwrfpV0E0A')
And then the query would do something like:
query = SELECT first_name
FROM users
WHERE id = (ids)
Returning records for all users whose ID appears in the tuple
Try doing this
query = "SELECT first_name FROM users WHERE id IN " + str(list(ids))
cursor.execute(query)
On second thoughts, the following should also work
query = "SELECT first_name FROM users WHERE id IN " + str(ids)
EDIT
OP mentions in comments that the ids are binary data returned from a previous query. In which case, taking hint from this answer and using BINARY operator, the following query should work
query = "SELECT first_name FROM users WHERE BINARY id IN " + str(ids) ;

Python String Formats with SQL Wildcards and LIKE

I'm having a hard time getting some sql in python to correctly go through MySQLdb. It's pythons string formatting that is killing me.
My sql statement is using the LIKE keyword with wildcards. I've tried a number of different things in Python. The problem is once I get one of them working, there's a line of code in MySQLdb that burps on string format.
Attempt 1:
"SELECT tag.userId, count(user.id) as totalRows FROM user INNER JOIN
tag ON user.id = tag.userId WHERE user.username LIKE '%%s%'" % (query)
This is a no go. I get value error:
ValueError: unsupported format character ''' (0x27) at index 128
Attempt 2:
"SELECT tag.userId, count(user.id) as totalRows FROM user INNER JOIN
tag ON user.id = tag.userId WHERE user.username LIKE '\%%s\%'" %
(query)
I get the same result from attempt 1.
Attempt 3:
like = "LIKE '%" + str(query) + "%'" totalq = "SELECT tag.userId,
count(user.id) as totalRows FROM user INNER JOIN tag ON user.id =
tag.userId WHERE user.username " + like
This correctly creates the totalq variable, but now when I go to run the query I get errors from MySQLdb:
File "build/bdist.macosx-10.6-universal/egg/MySQLdb/cursors.py", line
158, in execute query = query % db.literal(args) TypeError: not enough
arguments for format string
Attempt 4:
like = "LIKE '\%" + str(query) + "\%'" totalq = "SELECT tag.userId,
count(user.id) as totalRows FROM user INNER JOIN tag ON user.id =
tag.userId WHERE user.username " + like
This is the same output as attempt 3.
This all seems really strange. How can I use wildcards in sql statements with python?
Those queries all appear to be vulnerable to SQL injection attacks.
Try something like this instead:
curs.execute("""SELECT tag.userId, count(user.id) as totalRows
FROM user
INNER JOIN tag ON user.id = tag.userId
WHERE user.username LIKE %s""", ('%' + query + '%',))
Where there are two arguments being passed to execute().
It's not about string formatting but the problem is how queries should be executed according to db operations requirements in Python (PEP 249)
try something like this:
sql = "SELECT column FROM table WHERE col1=%s AND col2=%s"
params = (col1_value, col2_value)
cursor.execute(sql, params)
here are some examples for psycog2 where you have some explanations that should also be valid for mysql (mysqldb also follows PEP249 dba api guidance 2.0: here are examples for mysqldb)
To escape ampersands in Python string formatting expressions, double the ampersand:
'%%%s%%' % search_string
Edit: But I definitely agree with another answer. Direct string substitution in SQL queries is almost always a bad idea.
import mysql.connector
mydatabase = mysql.connector.connect(host="localhost", user="root", passwd="1234", database="databaseName")
mycursor = mydatabase.cursor()
user_input =[]
item = str("s%")
user_input.append(item)
mycursor.execute("SELECT * FROM employees WHERE FIRST_NAME LIKE %s ESCAPE ''",user_input )
result = mycursor.fetchall()
for row in enumerate(result):
print(row)
I used the following and it worked:
my_str = 'abc'
query = f"""select * from my_table where column_a like '%%{my_str}%%' """
df=pandas.read_sql_query(query, engine)
We could try escaping the percentage character by doubling them like this:
query_to_get_user_name = """
SELECT tag.userId, count(user.id) as totalRows FROM user INNER JOIN tag
ON user.id = tag.userId
WHERE user.username LIKE '%%%s%%' """ % (user_name,)
cursor.execute(query_to_get_user_name)
So I tried this and I think I have got the answer which is simpler for me to understand , a school student .
In my code the table name is "books" and the column I'm Searching for is "Name".
If you need more xplaination , then feel free to drop a mail at dhruv2003.joshi#gmail.com and I will try my best to answer ASAP
def S():
n=str(input('Enter the name of the book: '))
name='%'+n+'%'
NAME=name
query="select * from books where Name like '"+NAME+"' "
c.execute(query)
ans=c.fetchall()
if len(ans)>0:
print('')
for i in ans:
print(i)
print('')
else:
print('')
print('An error occured')
print('Name you gave does not exist :( ')
print('')
I have a solution to your problem :
You can not use :
"SELECT tag.userId, count(user.id) as totalRows FROM user INNER JOIN tag ON user.id = tag.userId WHERE user.username LIKE '%%s%'" % (query)
you can change it with string template, such as :
import MySQLdb
import string # string module
.......
value = {'user':'your value'}
sql_template = string.Template("""
SELECT tag.userId, count(user.id) as totalRows FROM user INNER JOIN
tag ON user.id = tag.userId WHERE user.username LIKE '%$user%'
""")
sql = sql_template.substitute(value)
try:
cursor.execute(sql)
...........
except:
...........
finally :
db.close()

Categories