Im struggling at a very specific problem here:
I need to make a LIKE search in SQLAlchemy, but the amount of keywords are varying.
Heres the code for one keyword:
search_query = request.form["searchinput"]
if selectet_wg and not lagernd:
query = db_session.query(
Artikel.Artnr,
Artikel.Benennung,
Artikel.Bestand,
Artikel.Vkpreisbr1
).filter(
and_(
Artikel.Benennung.like("%"+search_query+"%"),
Artikel.Wg == selectet_wg
)
).order_by(Artikel.Vkpreisbr1.asc())
"searchinput" looks like this : "property1,property2,property3", but also can be just 1,2,5 or more propertys.
I want to split the searchinput at "," (yes i know how to do that :) ) and insert another LIKE search for every property.
So for the above example the search should be looking like this:
search_query = request.form["searchinput"]
if selectet_wg and not lagernd:
query = db_session.query(
Artikel.Artnr,
Artikel.Benennung,
Artikel.Bestand,
Artikel.Vkpreisbr1
).filter(
and_(
Artikel.Benennung.like("%"+search_query+"%"), #property1
Artikel.Benennung.like("%"+search_query+"%"), #property2
Artikel.Benennung.like("%"+search_query+"%"), #property3
Artikel.Wg == selectet_wg
)
).order_by(Artikel.Vkpreisbr1.asc())
I dont think its a smart idea just to make an if statement for the amount of propertys and write down the query serveral times...
Im using the newest version of sqlalchemy and python 3.4
It should be possible to create a list of you like filters and pass them all to and_.
First create a list of the like queries:
search_queries = search_query.split(',')
filter = [Artikel.Benennung.like("%"+sq"%") for sq in search_queries]
Then pass them to and_, unpacking the list:
and_(
Artikel.Wg == selectet_wg,
*filter
)
*filter has to be the last argument to and_ otherwise python will give you an error.
You can call filter multiple times:
search_query = request.form["searchinput"]
if selectet_wg and not lagernd:
query = db_session.query(
Artikel.Artnr,
Artikel.Benennung,
Artikel.Bestand,
Artikel.Vkpreisbr1
).filter(Artikel.Wg == selectet_wg)
for prop in search_query.split(','):
query = query.filter(Artikel.Benennung.like("%"+prop+"%"))
query = query.order_by(Artikel.Vkpreisbr1.asc())
Related
I have two queries and the only difference between then is that one is counting the success status and the other failure status. Is there a way to get this result in just one query? I'm using SQLALchemy to do the queries.
success_status_query = (
db_session.query(Playbook.operator, func.count(Playbook.operator).label("success"))
.filter(Playbook.opname != "failed")
.join(AccountInfo, AccountInfo.hardware_id == Playbook.hardware_id)
.group_by(Playbook.operator)
)
failure_status_query = (
db_session.query(Playbook.operator, func.count(Playbook.operator).label("failure"))
.filter(Playbook.opname == "failed")
.join(AccountInfo, AccountInfo.hardware_id == Playbook.hardware_id)
.group_by(Playbook.operator)
)
You can use conditions on Count, your query will look like:
query = (
db_session.query(
Playbook.operator,
func.count(
case(
[((Playbook.opname != "failed"), Playbook.operator)],
else_=literal_column("NULL"),
)
).label("success"),
func.count(
case(
[((Playbook.opname == "failed"), Playbook.operator)],
else_=literal_column("NULL"),
)
).label("failure"),
)
.join(AccountInfo, AccountInfo.hardware_id == Playbook.hardware_id)
.group_by(Playbook.operator)
)
you can use the or_() operator like the sample bellow:
from sqlalchemy import or_
stmt = select(users_table).where(
or_(
users_table.c.name == 'wendy',
users_table.c.name == 'jack'
)
)
It won't be a straight swap, but you will be able to work it out.
You can find more information in the SQLAlchemy documentation:
https://docs.sqlalchemy.org/en/14/core/sqlelement.html#sqlalchemy.sql.expression.or_
I have a python code that create sql query in order to allow the user to filter the required data from a database.
Until now I am able to create a query using function.
The problem is that the logical operators or/and are the same for all fields
Query: name.str.contains('') or nickname.str.contains('') or mother_name.str.contains('') or first_nationality == 'None'
what i want is to be able to create different logical operator for each field like this:
Query: name.str.contains('') or nickname.str.contains('') and mother_name.str.contains('') or first_nationality == 'None'
the image below show the user input and the constructed query
code:
import streamlit as st
import pandas as pd
#SQL pckgs
import pyodbc
def build_query(input_values, logical_op, compare_op):
query_frags = []
for k, v in input_values.items():
if v['dtype'] == list:
query_frag_expanded = [f"{v['db_col']} {compare_op} '{val}'" for val in v['value']]
query_frag = f' {logical_op} '.join(query_frag_expanded)
elif v['dtype'] == int or v['dtype'] == float:
query_frag = f"{v['db_col']} {compare_op} {v['dtype'](v['value'])}"
elif v['dtype'] == str:
query_frag = f"{v['db_col']}.str.contains('{v['dtype'](v['value'])}')"
else:
query_frag = f"{v['db_col']} {compare_op} '{v['dtype'](v['value'])}')"
query_frags.append(query_frag)
query = f' {logical_op} '.join(query_frags)
return query
def configure_query():
c1, c2, _ = st.columns([1,1,2])
with c1:
logical_op = st.selectbox('Logical operator', options=['and', 'or'], index=1)
with c2:
compare_op = st.selectbox('Comparator operator', options=['==', '>', '<', '<=', '>='], index=0)
return logical_op, compare_op
logical_op, compare_op = configure_query()
query = build_query(input_values, logical_op, compare_op)
Take a look at SQLAlchemy
I've never used pyodbc to be honest and I'm not sure if you have any constraint that makes you use it. I normally use Sqlalchemy to create a connection to a database (my case is PostgreSQL) and then I use pd.read_sql(QUERY,con) where con is the connection I've created with Sqlalchemy and QUERY is a string that represents the sql query.
If you do that you would be able to easily create a function that receives the conditions you want to add and pass it to the query text.
I want to use outerjoin operation on a subquery and also include values from the subquery also.
My code
q_responses = session.query(Candidate, CandidateProfile)
.join(CandidateProfile, CandidateProfile.candidate_id == Candidate.id)
subq = (session.query(AppAction.candidate_id, Activity.archived)\
.join(Activity, and_(AppAction.candidate_id == Activity.candidate_id,
Activity.archived == 1)))\
.subquery("subq")
responses = q_responses.outerjoin(subq, Candidate.id == subq.c.candidate_id).all()
So I get the result in this format
(Candidate, CandidateProfile)
But I also want to include the archived value from subquery in the result.
By reading many relevant posts from the internet, I have tried
add_entity(subq.c.archived)
with_entities
add_column
select_from
But all those have resulted in some error.
Please help me out.
Please share your error for when you try add_column. The code below should work just fine (assuming that it does work without like which contains add_column):
responses = (
q_responses
.add_column(subq.c.archived) # #new
.outerjoin(subq, Candidate.id == subq.c.candidate_id)
).all()
Also you could have created a query straight away with this column included:
subq = (
session.query(AppAction.candidate_id, Activity.archived)
.join(Activity, and_(AppAction.candidate_id == Activity.candidate_id,
Activity.archived == 1))
).subquery("subq")
q_responses = (
session.query(Candidate, CandidateProfile, subq.c.archived)
.join(CandidateProfile, CandidateProfile.candidate_id == Candidate.id)
.outerjoin(subq, Candidate.id == subq.c.candidate_id)
).all()
Many times I find myself writing code similar to:
query = MyModel.objects.all()
if request.GET.get('filter_by_field1'):
query = query.filter(field1 = True)
if request.GET.get('filter_by_field2'):
query = query.filter(field2 = False)
field3_filter = request.GET.get('field3'):
if field3_filter is not None:
query = query.filter(field3 = field3_filter)
if field4_filter:
query = query.filter(field4 = field4_filter)
# etc...
return query
Is there a better, more generic way of building queries such as the one above?
If the only things that are ever going to be in request GET are potential query arguments, you could do this:
query = MyModel.objects.filter(**request.GET)
I have the following query call:
SearchList = (DBSession.query(
func.count(ExtendedCDR.uniqueid).label("CallCount"),
func.sum(ExtendedCDR.duration).label("TotalSeconds"),
ExtendedCDR,ExtensionMap)
.filter(or_(ExtensionMap.exten == ExtendedCDR.extension,ExtensionMap.prev_exten == ExtendedCDR.extension))
.filter(between(ExtendedCDR.start,datebegin,dateend))
.filter(ExtendedCDR.extension.in_(SelectedExtension))
.group_by(ExtendedCDR.extension)
.order_by(func.count(ExtendedCDR.uniqueid).desc()))
.all()
)
I would like to be able to define the order_by clause prior to calling the .query(), is this possible?
I tried doing as this stackoverflow answer suggests for a filter spec, but I had no idea how to create the filter_group syntax.
From that post:
filter_group = list(Column.in_('a','b'),Column.like('%a'))
query = query.filter(and_(*filter_group))
You build a SQL query with the DBSession.query() call, and this query is not executed until you call .all() on it.
You can store the intermediary results and add more filters or other clauses as needed:
search =DBSession.query(
func.count(ExtendedCDR.uniqueid).label("CallCount"),
func.sum(ExtendedCDR.duration).label("TotalSeconds"),
ExtendedCDR,ExtensionMap)
search = search.filter(or_(
ExtensionMap.exten == ExtendedCDR.extension,
ExtensionMap.prev_exten == ExtendedCDR.extension))
search = search.filter(between(ExtendedCDR.start, datebegin, dateend))
search = search.filter(ExtendedCDR.extension.in_(SelectedExtension))
search = search.group_by(ExtendedCDR.extension)
search = search.order_by(func.count(ExtendedCDR.uniqueid).desc())
The value you pass to order_by can be created ahead of time:
search_order = func.count(ExtendedCDR.uniqueid).desc()
then used like:
search = search.order_by(search_order)
Once your query is complete, get the results by calling .all():
SearchList = search.all()