sqlalchemy equvalent of sql syntax - python

I have a sql code that will print out all events that user with id=3 did not join particular yet:
SELECT * from Event where id not in (select event_id from Participant where user_id =3);
I want to write it in SQLAlchemy and so far I've got this
Event.query.filter(not_(Participant.user_id==3))
but produced query is not what I initially wrote:
SELECT "Event".id AS "Event_id",
"Event".name AS "Event_name",
"Event".published AS "Event_published",
"Event".published_when AS "Event_published_when",
"Event".published_by AS "Event_published_by"
FROM "Event", "Participant"
WHERE "Participant".user_id != ?
The above is not giving any results. I guess I wrote this SQLAlchemy query incorrectly. What is wrong with it?

Correct syntax:
Event.query.filter(Event.id.notin_(db.session.query(Participant.event_id).filter(Participant.user_id==session['uid'])))
Subquery had to be limited to event_id only. Thanks to this it will now return correct data from DB.

Try something like this:
sub = Participant.query(Participant.event_id).filter(Participant.user_id==3)
res = Event.query.filter(Event.id.notin_(sub))
or maybe this way:
res = Session.query(Event, Participant).filter(Event.id == Participant.user_id).filter(Participant.user_id == 3)

Related

Python TinyDB Get last record in db

I am trying to get the last record from my tinyDB, so I want to make a query that looks like that:
"SELECT * FROM table ORDER BY id DESC LIMIT 1"
which should give me the last row. though I can't figure out how to do it with TinyDB.
If you want to order db by time descending for example:
od = sorted(db.all(), key=lambda k: k['time'])
print(od[-1])
According to the documentation, the following would return the doc id of the final element in the db in TinyDB 4.7.0:
el = db.all()[-1]
record = db.get(doc_id=el.doc_id)
Using Document IDs
How about:
table = db.table('table_name')
table.get(doc_id=len(table))
See the way of doc_id here
Using a Query and doing an update :
with TinyDB('db.json') as db:
my_table = db.table('a_table_name')
my_query= Query()
first_of_table_for_this_query = my_table.search(my_query.some_field == some_value)[1]
last_of_table_for_this_query = my_table.search(my_query.some_field == some_value)[-1]
# exemple of use for updating first inserted and last inserted only
my_table.update({'some_field': some_value+42}, doc_ids=[
first_of_table_for_this_query,
last_of_table_for_this_query
])

Execute flask-SQLAlchemy subquery

I want to execute the following subquery in flask-SQLAlchemy but don't know how:
SELECT *
FROM (
SELECT *
FROM `articles`
WHERE publisher_id = "bild"
ORDER BY date_time DESC
LIMIT 10
) AS t
ORDER BY RAND( )
LIMIT 2
I know I can build the query as:
subq = Article.query.filter(Article.publisher_id =='bild').order_by(Article.date_time.desc()).limit(10).subquery()
qry = subq.select().order_by(func.rand()).limit(2)
However I don't know how to execute it in the same fashion as I would execute e.g.
articles = Article.query.filter(Article.publisher_id =='bild').all()
i.e. to get all the Article objects. What I can do is call
db.session.execute(qry).fetchall()
but this only gives me a list with actual row values instead of the objects on which I could for example call another function (like article.to_json()).
Any ideas? qry is a sqlalchemy.sql.selectable.Select object and db.session.execute(qry) a sqlalchemy.engine.result.ResultProxy while Article.query, on which I could call all(), is a flask_sqlalchemy.BaseQuery. Thanks!!
You can use select_entity_from
qry = db.session.query(Article).select_entity_from(subq).order_by(func.rand()).limit(2)
or from_self
Article.query.filter(Article.publisher_id =='bild')\
.order_by(Article.date_time.desc())\
.limit(10)\
.from_self()\
.order_by(func.rand())\
.limit(2)

Can we make correlated queries with SQLAlchemy

I'm trying to translate this SQL query into a Flask-SQLAlchemy call:
SELECT *
FROM "ENVOI"
WHERE "ID_ENVOI" IN (SELECT d."ID_ENVOI"
FROM "DECLANCHEMENT" d
WHERE d."STATUS" = 0
AND d."DATE" = (SELECT max("DECLANCHEMENT"."DATE")
FROM "DECLANCHEMENT"
WHERE "DECLANCHEMENT"."ID_ENVOI" = d."ID_ENVOI"))
As you can see, it uses subqueries and, most important part, one of the subqueries is a correlated query (it use d table defined in an outer query).
I know how to use subqueries with subquery() function, but I can't find documentation about correlated queries with SQLAlchemy. Do you know a way to do it ?
Yes, we can.
Have a look at the following example (especially the correlate method call):
from sqlalchemy import select, func, table, Column, Integer
table1 = table('table1', Column('col', Integer))
table2 = table('table2', Column('col', Integer))
subquery = select(
[func.if_(table1.c.col == 1, table2.c.col, None)]
).correlate(table1)
query = (
select([table1.c.col,
subquery.label('subquery')])
.select_from(table1)
)
if __name__ == '__main__':
print(query)
will result in the following query
SELECT table1.col, (SELECT if(table1.col = :col_1, table2.col, NULL) AS if_1
FROM table2) AS subquery
FROM table1
As you can see, if you call correlate on a select, the given Table will not be added to it's FROM-clause.
You have to do this even when you specify select_from directly, as SQLAlchemy will happily add any table it finds in the columns.
Based on the link from univerio's comment, I've done this code for my request:
Declch = db.aliased(Declanchement)
maxdate_sub = db.select([db.func.max(Declanchement.date)])\
.where(Declanchement.id_envoi == Declch.id_envoi)
decs_sub = db.session.query(Declch.id_envoi)\
.filter(Declch.status == SMS_EN_ATTENTE)\
.filter(Declch.date < since)\
.filter(Declch.date == maxdate_sub).subquery()
envs = Envoi.query.filter(Envoi.id_envoi.in_(decs_sub)).all()

How to count rows with SELECT COUNT(*) with SQLAlchemy?

I'd like to know if it's possible to generate a SELECT COUNT(*) FROM TABLE statement in SQLAlchemy without explicitly asking for it with execute().
If I use:
session.query(table).count()
then it generates something like:
SELECT count(*) AS count_1 FROM
(SELECT table.col1 as col1, table.col2 as col2, ... from table)
which is significantly slower in MySQL with InnoDB. I am looking for a solution that doesn't require the table to have a known primary key, as suggested in Get the number of rows in table using SQLAlchemy.
Query for just a single known column:
session.query(MyTable.col1).count()
I managed to render the following SELECT with SQLAlchemy on both layers.
SELECT count(*) AS count_1
FROM "table"
Usage from the SQL Expression layer
from sqlalchemy import select, func, Integer, Table, Column, MetaData
metadata = MetaData()
table = Table("table", metadata,
Column('primary_key', Integer),
Column('other_column', Integer) # just to illustrate
)
print select([func.count()]).select_from(table)
Usage from the ORM layer
You just subclass Query (you have probably anyway) and provide a specialized count() method, like this one.
from sqlalchemy.sql.expression import func
class BaseQuery(Query):
def count_star(self):
count_query = (self.statement.with_only_columns([func.count()])
.order_by(None))
return self.session.execute(count_query).scalar()
Please note that order_by(None) resets the ordering of the query, which is irrelevant to the counting.
Using this method you can have a count(*) on any ORM Query, that will honor all the filter andjoin conditions already specified.
I needed to do a count of a very complex query with many joins. I was using the joins as filters, so I only wanted to know the count of the actual objects. count() was insufficient, but I found the answer in the docs here:
http://docs.sqlalchemy.org/en/latest/orm/tutorial.html
The code would look something like this (to count user objects):
from sqlalchemy import func
session.query(func.count(User.id)).scalar()
Addition to the Usage from the ORM layer in the accepted answer: count(*) can be done for ORM using the query.with_entities(func.count()), like this:
session.query(MyModel).with_entities(func.count()).scalar()
It can also be used in more complex cases, when we have joins and filters - the important thing here is to place with_entities after joins, otherwise SQLAlchemy could raise the Don't know how to join error.
For example:
we have User model (id, name) and Song model (id, title, genre)
we have user-song data - the UserSong model (user_id, song_id, is_liked) where user_id + song_id is a primary key)
We want to get a number of user's liked rock songs:
SELECT count(*)
FROM user_song
JOIN song ON user_song.song_id = song.id
WHERE user_song.user_id = %(user_id)
AND user_song.is_liked IS 1
AND song.genre = 'rock'
This query can be generated in a following way:
user_id = 1
query = session.query(UserSong)
query = query.join(Song, Song.id == UserSong.song_id)
query = query.filter(
and_(
UserSong.user_id == user_id,
UserSong.is_liked.is_(True),
Song.genre == 'rock'
)
)
# Note: important to place `with_entities` after the join
query = query.with_entities(func.count())
liked_count = query.scalar()
Complete example is here.
If you are using the SQL Expression Style approach there is another way to construct the count statement if you already have your table object.
Preparations to get the table object. There are also different ways.
import sqlalchemy
database_engine = sqlalchemy.create_engine("connection string")
# Populate existing database via reflection into sqlalchemy objects
database_metadata = sqlalchemy.MetaData()
database_metadata.reflect(bind=database_engine)
table_object = database_metadata.tables.get("table_name") # This is just for illustration how to get the table_object
Issuing the count query on the table_object
query = table_object.count()
# This will produce something like, where id is a primary key column in "table_name" automatically selected by sqlalchemy
# 'SELECT count(table_name.id) AS tbl_row_count FROM table_name'
count_result = database_engine.scalar(query)
I'm not clear on what you mean by "without explicitly asking for it with execute()" So this might be exactly what you are not asking for.
OTOH, this might help others.
You can just run the textual SQL:
your_query="""
SELECT count(*) from table
"""
the_count = session.execute(text(your_query)).scalar()
def test_query(val: str):
query = f"select count(*) from table where col1='{val}'"
rtn = database_engine.query(query)
cnt = rtn.one().count
but you can find the way if you checked debug watch
query = session.query(table.column).filter().with_entities(func.count(table.column.distinct()))
count = query.scalar()
this worked for me.
Gives the query:
SELECT count(DISTINCT table.column) AS count_1
FROM table where ...
Below is the way to find the count of any query.
aliased_query = alias(query)
db.session.query(func.count('*')).select_from(aliased_query).scalar()
Here is the link to the reference document if you want to explore more options or read details.

Select as in sqlalchemy

I want to do something like this:
select username, userid, 'user' as new_column from users_table.
The columns of the table can be selected using sqlalchemy as follows:
query = select([users_table.c.username, users_table.c.userid])
How do I do the select x as col_x to the query in sqlalchemy?
Use the label(...) function: users_table.c.userid.label('NewColumn')
i.e.,
query = select([users_table.c.username, users_table.c.userid.label('NewColumn')])
evaluates to:
SELECT username, userid as NewColumn From MyTable;
Maybe literal_column?
query = select([users_table.c.username, users_table.c.userid, literal_column("user", type_=Unicode).label('new_column')])
See https://docs.sqlalchemy.org/en/13/core/sqlelement.html#sqlalchemy.sql.expression.literal_column
Edit:
actually I should have said "literal":
query = select([users_table.c.username, users_table.c.userid, literal("user", type_=Unicode).label('new_column')])
See the alias() in the docs here: https://docs.sqlalchemy.org/en/14/core/selectable.html?highlight=alias#sqlalchemy.sql.expression.Alias

Categories