Multiple SQL Queries with python & flask - python

I have a SQLite3 table that collects data from a gym booking form; all the data goes into one table - Name, Email, Day, Exercise Class, Class Time.
If it is possible, I would like to find a way to get all from Name if Day, Exercise Class & Class Time are equal to a selected value.
I am using Flask so once I can do this the data would then be used to generate a new HTML page (which would be the registration checklist page for the gym tutor).
I am pretty sure this is incorrect, but this is the general idea of what I would like to achieve..
db.execute("SELECT * FROM GymTable WHERE (Day == "Monday", Ex_Class == "BoxFit", Ex_Time == "0745-0845"))

the correct query in this case would be:
db.execute("SELECT * FROM GymTable WHERE Day = 'Monday' and Ex_Class = 'BoxFit' and Ex_Time = '0745-0845'")

You may find these two tutorials on the SQL WHERE clause and SQL AND, OR, NOT Operators helpful. Notice first that the equal operator is = instead of ==. This query needs AND between column filters. The WHERE clause should not be enclosed in (). You may find the python sqlite3 doc useful as well.

To better illustrate:
db.execute( "
SELECT name
FROM gymtable
WHERE day = 'Monday'
AND ex_class = 'BoxFit'
AND ex_time = '0745-0845'
");

Related

Pass multiple user inputs from python into SQL query

I am working on a project that asks me to prompt the user to enter some ids in order to retrieve relevant data from database using SQL developer.
I knew how to just take one piece of user input and put it into one SQL query but struggle to tackle multiple user inputs.
For example, I can prompt the user to enter share_id for me to search for corresponding trade like this:
connection = cx_Oracle.connect("hr", "oracle", "localhost/orcl")
cursor = connection.cursor()
share_id_val = input("Enter share id to search: ")
cursor.execute("SELECT * FROM trades WHERE share_id = :share_id", share_id = share_id_val)
But if the user may enter one or more of [share_id, broker_id, date_range], then I can only think of building 6 SQL queries, one for each case (that's very inefficient for sure).
Specifically, I don't know how to construct the SQL query inside the cursor.execute to account for different scenarios (the user may just want to retrieve trade information by share_id, or by both broker_id and date_range).
PS: date range should include from_date and to_date of transaction_time.
Any help will be appreciated.
You need to use the inputs provided by user with a case statement with an escape clause (else part of case here) corresponding to the no input situation. This way you can handle more than one inputs independently. In this case, no input is mapped to input variable being null (not a default non-null value). You should be able to extend this to include date range inputs also.
Try
share_id_val = input("Enter share id to search: ")
broker_id_val = input("Enter broker id to search: ")
cursor.execute("
SELECT * FROM trades WHERE 1=1
and share_id = case when :share_id_bind_var is not null then :share_id_bind_var else share_id end
and broker_id = case when :broker_id_bind_var is not null then :broker_id_bind_var else broker_id end
",
share_id_bind_var = share_id_val,
broker_id_bind_var = broker_id_val
)

SQL LIKE using variable from user as a search

I'm trying to make a search functionality to output all available books based on author typed in a form (I'm using sqlite)
I'd like to use LIKE function.
The piece of code I'm using is as follows:
# User reached route via POST (submitting the form)
else:
query = request.form.get("query")
# Ensure symbol is not blank
if not query:
return apology("you haven't typed anything!", 400)
# A list querying available books by a similar author
books = db.execute("SELECT author, title, genre, id FROM books WHERE booked = 0 AND author LIKE '%' + :query + '%'",
query = query)
However, it doesn't seem to work.
No errors are found, but when I try to print out books, it returns always empty, even if I type in the exact author name.
How can I fix this?
Thanks!

Searching a date with parameter 'LIKE'

I am building a small program in Python and SQlite where a client can type a date in the format YYYY-MM-DD and get back all the plays that take place in that specific date. All the plays are store in Play.db which saves the date as: YYYY-MM-DD HH:MM
I do not want the user to type the exact date with hour and minutes. He just has to type the year-month-day and all the plays in that specific date should appear.
I realise that I need the keyword 'LIKE' and pass my variable between '%'variable'%' in order to get a similar (not exact) match.
However I am not sure how to do so by using the syntax of my SQLite query:
user_input = input("Please, type the date of the play in format: 'YYYY-MM-DD':")
search_date = """SELECT * FROM play WHERE play_date LIKE ? """
cursor.execute(search_date, [(user_input)])
results = cursor.fetchall()
The issue is that 'results' is not populated because the SQL query is looking for exact match.
THEREFORE I tried with %user_input% or %?% but, obsiously, the variable '%user_input%' or %?% is not defined.
I understand how logically is should be done:
how to search similar strings in SQLite
But I am not sure how the "LIKE '%'variable'%'" can be obtained using my code syntax.
import datetime
user_input = datetime.datetime(input("Please, type the date of the play in format: 'YYYY-MM-DD':"), '%Y-%m-%d')
# 2011-11-11
search_date = f'SELECT * FROM play WHERE play_date LIKE {user_input}'
print(search_date)
# SELECT * FROM play WHERE play_date LIKE 2011-11-11
Is this what you're looking for?
Edited to prevent sql injections
Use the date function in your query to just compare the dates of your stored datetime values and user input.
SELECT * FROM play WHERE date(play_date) = date(?)
You need a date function. Also you can use slicing for the string. Like:
theDate = input() #example: 2018-12-24
Year = theDate[:3]
Month = theDate[5:6]
Day = theDate[8:9]
Good day,
Well it may not be exactly what you want but I would share you some pieces of code I wrote when I wanted to build something like this but this time using Flask and SQLAlchemy.
First is to convert the data to a date/datetime to ensure the data is valid
converted_date = datetime.datetime.strptime(user_input, "%Y-%m-%d").date()
Converted to regular SQL query should be
SELECT * FROM play WHERE play_date LIKE {"%" + converted_date}
Please note that the "%" is very important, and putting it at the beginning means you want to search for any date that starts with the inputted date.

Using variable combining LIKE and %s% in SQLite and Python

Using:
Python3
SQLite
TKinter
I am currently trying to create a function to search for a keyword in a database, but as soon as I try to combine it with TKinter, it seems to fail.
Here are the relevant lines:
(I tried it in a lot of different ways, those 3 lines below seem to work with variables, but not with the input from TKinter, so I thought they might actually work, if I edit them a little.
The problem I got is, that I'm not experienced in TKinter and SQLite yet and worked with those 2 for about 3 days yet.
def searcher(column):
#Getting input from user (TKinter)
keyword = tk.Entry(self)
keyword.pack()
#Assigning the input to a variable
kword = keyword.get()
c.execute("SELECT * FROM my_lib WHERE {kappa} LIKE {%goal%}".format(kappa=column, goal=kword))
#c.execute("SELECT * FROM my_lib WHERE "+column+"=?", (kword,))
#c.execute("SELECT * FROM my_lib WHERE {} LIKE '%kword%'".format(column))
I want to check if any of the data CONTAINS the keyword, so basically:
k_word in column_data
and not
column_data == k_word
My question is:
Is there a way to take the user input (by TKinter) and search in the database (SQLite) and check, if any data in the database contains the keyword.
The SQLite docs explain that you can use ? as a placeholder in the query string, which allows you so substitute in a tuple of values. They also advise against ever assembling a full query using variables with Python's string operations (explained below):
c.execute("SELECT * FROM my_lib WHERE ? LIKE ?", (column, '%'+kword+'%'))
You can see above that I concatenated the % with kword, which will get substituted into the second ?. This also is secure, meaning it will protect against SQL Injection attacks if needed.
Docs: https://docs.python.org/2/library/sqlite3.html
Related posts: Escaping chars in Python and sqlite
Try:
kword = "%"+kword+"%"
c.execute("SELECT * FROM my_lib WHERE kappa LIKE '%s'" % kword)
After trying over and over again, I got the actual solution. It seems like I can't just add the '%' to the variable like a string, but rather:
c.execute("SELECT * FROM my_lib WHERE {} LIKE '%{}%'".format(column, kword))

SQLAlchemy filter query by related object

Using SQLAlchemy, I have a one to many relation with two tables - users and scores. I am trying to query the top 10 users sorted by their aggregate score over the past X amount of days.
users:
id
user_name
score
scores:
user
score_amount
created
My current query is:
top_users = DBSession.query(User).options(eagerload('scores')).filter_by(User.scores.created > somedate).order_by(func.sum(User.scores).desc()).all()
I know this is clearly not correct, it's just my best guess. However, after looking at the documentation and googling I cannot find an answer.
EDIT:
Perhaps it would help if I sketched what the MySQL query would look like:
SELECT user.*, SUM(scores.amount) as score_increase
FROM user LEFT JOIN scores ON scores.user_id = user.user_id
WITH scores.created_at > someday
ORDER BY score_increase DESC
The single-joined-row way, with a group_by added in for all user columns although MySQL will let you group on just the "id" column if you choose:
sess.query(User, func.sum(Score.amount).label('score_increase')).\
join(User.scores).\
filter(Score.created_at > someday).\
group_by(User).\
order_by("score increase desc")
Or if you just want the users in the result:
sess.query(User).\
join(User.scores).\
filter(Score.created_at > someday).\
group_by(User).\
order_by(func.sum(Score.amount))
The above two have an inefficiency in that you're grouping on all columns of "user" (or you're using MySQL's "group on only a few columns" thing, which is MySQL only). To minimize that, the subquery approach:
subq = sess.query(Score.user_id, func.sum(Score.amount).label('score_increase')).\
filter(Score.created_at > someday).\
group_by(Score.user_id).subquery()
sess.query(User).join((subq, subq.c.user_id==User.user_id)).order_by(subq.c.score_increase)
An example of the identical scenario is in the ORM tutorial at: http://docs.sqlalchemy.org/en/latest/orm/tutorial.html#selecting-entities-from-subqueries
You will need to use a subquery in order to compute the aggregate score for each user. Subqueries are described here: http://www.sqlalchemy.org/docs/05/ormtutorial.html?highlight=subquery#using-subqueries
I am assuming the column (not the relation) you're using for the join is called Score.user_id, so change it if this is not the case.
You will need to do something like this:
DBSession.query(Score.user_id, func.sum(Score.score_amount).label('total_score')).group_by(Score.user_id).filter(Score.created > somedate).order_by('total_score DESC')[:10]
However this will result in tuples of (user_id, total_score). I'm not sure if the computed score is actually important to you, but if it is, you will probably want to do something like this:
users_scores = []
q = DBSession.query(Score.user_id, func.sum(Score.score_amount).label('total_score')).group_by(Score.user_id).filter(Score.created > somedate).order_by('total_score DESC')[:10]
for user_id, total_score in q:
user = DBSession.query(User)
users_scores.append((user, total_score))
This will result in 11 queries being executed, however. It is possible to do it all in a single query, but due to various limitations in SQLAlchemy, it will likely create a very ugly multi-join query or subquery (dependent on engine) and it won't be very performant.
If you plan on doing something like this often and you have a large amount of scores, consider denormalizing the current score onto the user table. It's more work to upkeep, but will result in a single non-join query like:
DBSession.query(User).order_by(User.computed_score.desc())
Hope that helps.

Categories

Resources