From SQL to Django without raw method - python

I'm trying to convert SQL Query to Django.
I'm new to Django and I find it hard to do simple things,
here is the SQL query I'm trying to run with Django :
SELECT *
FROM balance_hold bh1
WHERE
bh1.account_balance = X and
bh1.hold_id in (
SELECT hold_id
FROM balance_hold bh2
WHERE
bh2.transaction_id = bh1.transaction_id and
bh2.account_balance = X and
(
bh2.release_time = null and hold_type >=
(select * from balance_hold bh3 where bh3.transaction_id = bh2.transaction_id and bh3.account_balance = X and bh3.release_time = null)
) or
(
bh2.release_time != null and bh2.release_time >= (select release_time from balance_hold bh3 where bh3.transaction_id = bh2.transaction_id and bh3.account_balance = X and bh3.release_time != null)
))
how do I run this with Django (without using RAW method!)?

I don't think attempting to translate an sql query to django ORM is the right way to tackle your issue, regardless you'll need several tools to achieve this.
OR and AND can be written with Q objects (https://docs.djangoproject.com/fr/4.1/ref/models/querysets/#operators-that-return-new-querysets).
The kind of subquery can be achieved with Subquery and OuterRef(https://docs.djangoproject.com/fr/4.1/ref/models/expressions/#subquery-expressions).
#column = null can be achieved with __isnull lookup (https://docs.djangoproject.com/fr/4.1/ref/models/querysets/#isnull)
If the syntax of subqueries is not clear, for instance:
BalanceHold.objects.filter(account_balance=X, id__in=Subquery(BalanceHold.objects.filter(transaction_id=OuterRef("transaction_id")).values("id")))
Will produce:
SELECT * FROM "balance_hold" WHERE ("balance_hold"."id" IN (SELECT U0."id" FROM "balance_hold" U0 WHERE U0."transaction_id" = "balance_hold"."transaction_id") AND "balance_hold"."account_balance" = X) ORDER BY "balance_hold"."id" DESC

Related

Use same parameter multiple times in sql query

I am using pyodbc and Microsoft SQL Server
I am trying to replicate a stored procedure in python where this query is executed for every #currentSurveyId
SELECT *
FROM
(
SELECT
SurveyId,
QuestionId,
1 as InSurvey
FROM
SurveyStructure
WHERE
SurveyId = #currentSurveyId
UNION
SELECT
#currentSurveyId as SurveyId,
Q.QuestionId,
0 as InSurvey
FROM
Question as Q
WHERE NOT EXISTS
(
SELECT *
FROM SurveyStructure as S
WHERE S.SurveyId = #currentSurveyId AND S.QuestionId = Q.QuestionId
)
) as t
ORDER BY QuestionId
In Python, I so far have:
cursor.execute("""SELECT UserId FROM dbo.[User]""")
allSurveyID = cursor.fetchall()
for i in allSurveyID:
p = i
test = cursor.execute("""SELECT *
FROM
(
SELECT
SurveyId,
QuestionId,
1 as InSurvey
FROM
SurveyStructure
WHERE
SurveyId = (?)
UNION
SELECT
(?) as SurveyId,
Q.QuestionId,
0 as InSurvey
FROM
Question as Q
WHERE NOT EXISTS
(
SELECT *
FROM SurveyStructure as S
WHERE S.SurveyId = (?)AND S.QuestionId = Q.QuestionId
)
) as t
ORDER BY QuestionId""",p)
for i in test:
print(i)
The parameter works when used once (if I delete everything from UNION onwards). When trying to use the same parameter in the rest of the query, I get the following error:('The SQL contains 3 parameter markers, but 1 parameters were supplied', 'HY000')
Is it possible to use the same parameter multiple times in the same query?
Thank you
pyodbc itself only supports "qmark" (positional) parameters (ref: here), but with T-SQL (Microsoft SQL Server) we can use an anonymous code block to avoid having to pass the same parameter value multiple times:
cnxn = pyodbc.connect(connection_string)
crsr = cnxn.cursor()
sql = """\
SET NOCOUNT ON;
DECLARE #my_param int = ?;
SELECT #my_param AS original, #my_param * 2 AS doubled;
"""
results = crsr.execute(sql, 2).fetchone()
print(results) # (2, 4)
If reusing the same parameter value, simply multiply a one-item list of the parameter:
cursor.execute(sql, [p]*3)
Consider also refactoring your SQL for LEFT JOIN (or FULL JOIN) requiring two qmarks:
SELECT DISTINCT
ISNULL(S.SurveyId, ?) AS SurveyId,
Q.QuestionId,
IIF(S.SurveyId IS NOT NULL, 1, 0) AS InSurvey
FROM Question Q
LEFT JOIN SurveyStructure S
ON S.QuestionId = Q.QuestionId
AND S.SurveyId = ?
ORDER BY Q.QuestionId
Possibly even for one parameter:
SELECT MAX(S.SurveyId) AS SurveyId,
Q.QuestionId,
IIF(S.SurveyId IS NOT NULL, 1, 0) AS InSurvey
FROM Question Q
LEFT JOIN SurveyStructure S
ON S.QuestionId = Q.QuestionId
AND S.SurveyId = ?
GROUP BY Q.QuestionId,
IIF(S.SurveyId IS NOT NULL, 1, 0)
ORDER BY Q.QuestionId

Oracle SQL show records WHERE there are more than one record for each pair of (a,b)

I am using Python to access an Oracle Exadata database, which is HUGE. The documentation for the table is rather poor and I need to understand strange cases. Coming from an R/python world I ran the following query:
query = ("""
SELECT COUNT(counter) as freq, counter
FROM (
SELECT COUNT(*) as counter
FROM schema.table
WHERE x = 1 AND y = 1
GROUP BY a,b )
GROUP BY counter""")
with cx_Oralce.connct(dsn=tsn, encoding = "UTF-8") as con:
df = pd.read_sql(con=con, query=sql)
This essentially counts the frequency of observations for a given (a,b) pair. My prior was that they are all 1 (they are not). So I would like to see the observations that drive this:
query = ("""
SELECT *
FROM schema.table
WHERE x = 1 and y = 1
AND (for each (a,b) there is more than one record)""")
I am struggling to translate this into proper Oracle SQL.
In R (dplyr) this would be a combination of group_by and mutate (instead of summarise) and in Python pandas this could be done with transform.
I am new to SQL and may use incorrect terminology. I appreciate being corrected.
You can use window functions:
SELECT ab.*
FROM (SELECT t.*, COUNT(*) OVER (PARTITION BY a, b) as cnt
FROM schema.table t
WHERE x = 1 AND y = 1
) ab
WHERE cnt > 1;

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 use NOT IN clause in sqlalchemy ORM query

how do i convert the following mysql query to sqlalchemy?
SELECT * FROM `table_a` ta, `table_b` tb where 1
AND ta.id = tb.id
AND ta.id not in (select id from `table_c`)
so far i have this for sqlalchemy:
query = session.query(table_a, table_b)
query = query.filter(table_a.id == table_b.id)
The ORM internals describe the not_in() operator (previously notin_()), so you can say:
query = query.filter(table_a.id.not_in(subquery))
# ^^^^^^
From the docs:
inherited from the ColumnOperators.not_in() method of ColumnOperators
implement the NOT IN operator.
This is equivalent to using negation with ColumnOperators.in_(), i.e. ~x.in_(y).
Note that version 1.4 states:
The not_in() operator is renamed from notin_() in previous releases. The previous name remains available for backwards compatibility.
So you may find notin_() in some cases.
Try this:
subquery = session.query(table_c.id)
query = query.filter(~table_a.id.in_(subquery))
Note: table_a, table_b and table_c should be mapped classes, not Table instances.
here is the full code:
#join table_a and table_b
query = session.query(table_a, table_b)
query = query.filter(table_a.id == table_b.id)
# create subquery
subquery = session.query(table_c.id)
# select all from table_a not in subquery
query = query.filter(~table_a.id.in_(subquery))

Categories