Simple SQLAlchemy query filter in python - python

I'm trying to select all rows in a sql table where the first four characters of a text column match a certain string. (The backend database is a sqlite instance with limited column types, so bear with me)
The code I've written for the select is this:
rows = SECtable.query.filter(str(SECtable.date)[:4] == str(matchingString)).all()
What am I doing wrong here? The query never matches any rows

If you use SECtable.date == 'some_string', this produces an expression (sqlalchemy.sql.expression.BinaryExpression), which will be evaluated when you execute the query.
str(SECtable.date)[:4] == str(matchingString) is evaluated immediately, it produces the string representation of SECtable.date (i'd guess 'SECTable.date'), and compares all but the fist for characters to str(matchingString). so what you're writing here is basically:
'able.date' == str(matchingString)
which will probably evaluate to false, so you end up with filter(False).
sqlalchemy provides a endswith functionality you could use in this case:
rows = SECtable.query.filter(SECtable.date.endswith(matchingString)).all()

You probably want to use SQLAlchemy's implementation of SQL's LIKE.
See the following documentation:
http://docs.sqlalchemy.org/en/rel_0_8/orm/tutorial.html#common-filter-operators
http://docs.sqlalchemy.org/en/rel_0_8/core/sqlelement.html#sqlalchemy.sql.operators.ColumnOperators.like
http://docs.sqlalchemy.org/ru/latest/orm/query.html

Related

Getting error when running a sql select statement in python

I am new to this and trying to learn python. I wrote a select statement in python where I used a parameter
Select """cln.customer_uid = """[(num_cuid_number)])
TypeError: string indices must be integers
Agree with the others, this doesn't look really like Python by itself.
I will see even without seeing the rest of that code I'll guess the [(num_cuid_number)] value(s) being returned is a string, so you'll want to convert it to integer for the select statement to process.
num_cuid_number is most likely a string in your code; the string indices are the ones in the square brackets. So please first check your data variable to see what you received there. Also, I think that num_cuid_number is a string, while it should be in an integer value.
Let me give you an example for the python code to execute: (Just for the reference: I have used SQLAlchemy with flask)
#app.route('/get_data/')
def get_data():
base_sql="""
SELECT cln.customer_uid='%s' from cln
""" % (num_cuid_number)
data = db.session.execute(base_sql).fetchall()
Pretty sure you are trying to create a select statement with a "where" clause here. There are many ways to do this, for example using raw sql, the query should look similar to this:
query = "SELECT * FROM cln WHERE customer_uid = %s"
parameters = (num_cuid_number,)
separating the parameters from the query is secure. You can then take these 2 variables and execute them with your db engine like
results = db.execute(query, parameters)
This will work, however, especially in Python, it is more common to use a package like SQLAlchemy to make queries more "flexible" (in other words, without manually constructing an actual string as a query string). You can do the same thing using SQLAlchemy core functionality
query = cln.select()
query = query.where(cln.customer_uid == num_cuid_number)
results = db.execute(query)
Note: I simplified "db" in both examples, you'd actually use a cursor, session, engine or similar to execute your queries, but that wasn't your question.

Python SQLAlchemy create in_ on text Column

I need a condition in sqlalchemy that allow me to use a list of ids stored in a TEXT column as a parameter for a in_. The ids are simply stored like 1,2,3,4. I tried to use an in_ but it's seems that a "list" stored in a TEXT column is not the right way to do.
This is what I've tried:
filters.append(and_(Organisation.id.in_(Session.list_id), Session.token == token))
That gives me this error:
sqlalchemy.exc.InvalidRequestError: in_() accepts either a list of expressions, a selectable, or an "expanding" bound parameter: Column('list_id', Text(), table=<MYTABLE>)
So I try to find a split to do something like:
filters.append(and_(Organisation.id.in_(Session.list_id.split(',')), Session.token == token))
But split, explode or every kind of dissociate function doesn't exist!
I try to use a more SQL way, but i always bump in the use of a function that doesn't exist!
filters.append(Organisation.id.in_(ds.query(Session.list_id).filter(Session.token == token)))
What is the best way to achieve this? My last idea is to create a table to store each id in single row, but it seems a little bit overkill for what i need. Or do it in a separate query.
Any hint will be appreciated.
Thanks

How do you SELECT all rows of an sqlite3 database that have a column that contains a certain variable substring?

I know how to use the LIKE clause to extract rows that have a column that contains a hard-coded substring:
SELECT * FROM userTable WHERE UPPER(column) LIKE UPPER('%this_is_a_hard_coded_string%')
But I can't figure out how to use a variable for the substring instead of a hardcoded substring. Usually, when I make an sqlite3 query with python3, I can set a value equal to a ? and then add an extra argument to the execute method on the cursor; however, that does not seem to work here.
Try this
t = ("%"+"this_is_a_hard_coded_string"+"%",)
c.execute('SELECT * FROM userTable WHERE UPPER(column) LIKE UPPER(?)', t)
You can also use a different more readable use of "?".
Refer this answer
https://stackoverflow.com/a/1010804/1471352
or this
https://stackoverflow.com/a/3105370/1471352

Why use SqlAlchemy's func.lower(mystring) vs. mystring.lower()?

Look at this answer: Case Insensitive Flask-SQLAlchemy Query
Why is it better to use SQLAlchemy's func.lower(mystring) instead of python's native mystring.lower()?
The context is important.
user = models.User.query.filter(func.lower(User.username) == func.lower("GaNyE")).first()
Here, there is no string with a lower method to call; you are composing a SQL command to execute in the database. func.lower doesn't return a lowercase string; it returns an object that represents SQL code that can be used to produce a lowercase string.
As mgilson points out, there's no need to use func.lower on actual Python values; they are constant for each row the filter will be applied against, but I'm not sure SQLAlchemy is able to detect and optimize such situations. Either of the following
user = models.User.query.filter(func.lower(User.username) == "ganye").first()
user = models.User.query.filter(func.lower(User.username) == "GaNyE".lower()).first()
would produce SQL code like lower(username) = :lower_1 instead of lower(username) = lower(:lower_1), which can make the SQL query more efficient by eliminating a call to lower for each line.

Sqlalchemy: use PSQL's `~` operator when string is on left and column on right

I know that
WHERE column ~ 'regexp'
is this is sqlalchemy:
where(column.op('~')('regexp'))
but how could I create this?
WHERE 'string' ~ column
(the regex is stored in the database)
You need to create either a literal() or a bindparam():
from sqlalchemy.sql import expression
expression.literal('string').op('~')(column)
# or
expression.bindparam('text', 'string').op('~')(column)
A literal() is basically a bindparam() with an implicit name; in the above example 'text' is the name (key) of the parameter as used in the SQL statement, and 'string' the text you want to appear on the left-hand side of the operator.
A bindparam() gives you a bit more control, including the ability to pass in a calleble instead of a concrete value:
def value_function():
return some_calculation_result()
expression.bindparam('calculated_value', callable_=value_function).op('~')(column)
and SQLAlchemy will call value_function() when sending the statement to the database server. You'd also use bindparam() if you have a sequence of values, and SQLAlchemy will take care of adjusting the expression to apply to each value in the sequence:
strings = ['string1', 'string2', ...]
expression.bindparam('one_or_more', strings).op('~')(column)
# executes `('string1' ~ column OR 'string2' ~ column OR ...)`
An non-Sqlalchemy-specific solution would be to create your own operator in the PostgreSQL backend that has the operands reversed. That would also allow you to do things like
string ### ANY(ARRAY[regexp1, regexp2, ...])
where ### is your operator. (Finding a good name might be a challenge.)

Categories