Making a subrequest in SQLAlchemy - sqlalchemy

I try to do the follwoing request in SQLAlchemy (ORM) :
SELECT id, ref_prog FROM stepvand_1c_1t.equipment_day_hour
WHERE id IN (SELECT id FROM stepvand_1c_1t.equipment WHERE equipment_type='L')
I did :
subq = session.query(Equipment)
subq = subq.filter(Equipment.equipment_type == "L").subquery()
query = session.query(EquipmentDayHour)
query = query.filter(EquipmentDayHour.id.in_(subq))
But that doesn't work...
Python tells me that the subrequest has too many columns.

I think you should only change one line of your sample code:
# error: includes all columns of Equipment
`subq = session.query(Equipment)`
# correct: include only ID column
`subq = session.query(Equipment.id)`
However, I believe that you can do this without subquery:
query = (session.query(EquipmentDayHour).
# version-1: if you have a relationship between EquipmentDayHour and Equipment
join(Equipment).
# version-2: if you do not have such relationship
#join(Equipment, EquipmentDayHour.id==Equipment.id).
filter(Equipment.equipment_type == "L")
)

Related

SQLAlchemy: Join table with object's children

I have three object types with corresponding tables:
class Order:
suborders = relationship('Suborder', lazy='dynamic')
class Suborder:
...
class PurchaseOrder:
suborder = relationship('Suborder', foreign_keys=[suborder_id], lazy='joined')
Now I need to get all PurchaseOrder instances matching Order's suborders. In pure SQL I'd write something like this:
SELECT po.*
FROM purchase_orders AS po JOIN suborders AS so ON po.suborder_id = so.id
WHERE so.order_id = 'order-007'
How do I do it using SQLAlchemy? I tried this:
o = Order.query.get('order-007')
PurchaseOrder.query.join(o.suborders)
But this gave me an error:
AttributeError: 'AppenderQuery' object has no attribute 'is_selectable'
What is the right way to do it?
Getting required data with just one query:
query = (
PurchaseOrder.query
.join(Suborder)
.filter(Suborder.order_id == 'order-007')
)
If you already have the Order instance o, you can do the following:
o = Order.query.get('order-007') # already have the order instance
query = (
session.query(PurchaseOrder)
.join(Suborder)
.with_parent(o)
)
, but you will still need to join on Suborder.
But again, the first one would correspond to the SQL query you provided as the SQL implementation.

How to create union of two different django-models?

I have two django-models
class ModelA(models.Model):
title = models.CharField(..., db_column='title')
text_a = models.CharField(..., db_column='text_a')
other_column = models.CharField(/*...*/ db_column='other_column_a')
class ModelB(models.Model):
title = models.CharField(..., db_column='title')
text_a = models.CharField(..., db_column='text_b')
other_column = None
Then I want to merge the two querysets of this models using union
ModelA.objects.all().union(ModelB.objects.all())
But in query I see
(SELECT
`model_a`.`title`,
`model_a`.`text_a`,
`model_a`.`other_column`
FROM `model_a`)
UNION
(SELECT
`model_b`.`title`,
`model_b`.`text_b`
FROM `model_b`)
Of course I got the exception The used SELECT statements have a different number of columns.
How to create the aliases and fake columns to use union-query?
You can annotate your last column to make up for column number mismatch.
a = ModelA.objects.values_list('text_a', 'title', 'other_column')
b = ModelB.objects.values_list('text_a', 'title')
.annotate(other_column=Value("Placeholder", CharField()))
# for a list of tuples
a.union(b)
# or if you want list of dict
# (this has to be the values of the base query, in this case a)
a.union(b).values('text_a', 'title', 'other_column')
In SQL query, we can use NULL to define the remaining columns/aliases
(SELECT
`model_a`.`title`,
`model_a`.`text_a`,
`model_a`.`other_column`
FROM `model_a`)
UNION
(SELECT
`model_b`.`title`,
`model_b`.`text_b`,
NULL
FROM `model_b`)
In Django, union operations needs to have same columns, so with values_list you can use those specific columns only like this:
qsa = ModelA.objects.all().values('text_a', 'title')
qsb = ModelB.objects.all().values('text_a', 'title')
qsa.union(qsb)
But there is no way(that I know of) to mimic NULL in union in Django. So there are two ways you can proceed here.
First One, add an extra field in your Model with name other_column. You can put the values empty like this:
other_column = models.CharField(max_length=255, null=True, default=None)
and use the Django queryset union operations as described in here.
Last One, the approach is bit pythonic. Try like this:
a = ModelA.objects.values_list('text_a', 'title', 'other_column')
b = ModelB.objects.values_list('text_a', 'title')
union_list = list()
for i in range(0, len(a)):
if b[i] not in a[i]:
union_list.append(b[i])
union_list.append(a[i])
Hope it helps!!

Sql Alchemy filter / where clause joined by OR not AND

I want to select a bunch distinct records based off a composite key. In SQL I'd write something like this:
SELECT * FROM security WHERE (
exchange_code = 'exchange_code_1' AND code = 'code_1')
OR (exchange_code = 'exchange_code_2' AND code = 'code_2')
...
OR (exchange_code = 'exchange_code_N' AND code = 'code_N')
)
With SQLAlchemy I'd like to use the filter clause like:
query = sess.query(Security)
[query.filter(
and_(Security.exchange_code == security.exchange_code,
Security.code == security.code)
) for security in securities]
result = query.all()
The problem is filter and where join clauses with an AND not an OR... is there some way to use filter with OR?
Or is my only choice to generate a bunch of individual select's and UNION them? Something like:
first = exchanges.pop()
query = reduce(lambda query, exchange: query.union(exchange.pk_query),
first.pk_query())
query.all()
Use or_:
query = sess.query(Security).filter(
or_(*(and_(Security.exchange_code == security.exchange_code,
Security.code == security.code)
for security in securities)))
If your database supports it, you should use tuple_ instead.

IF condition in mysql

I have a contact table I wish to query when a certain condition exists. I tried the query below but am getting a syntax error.
SELECT *
FROM contact_details
WHERE contactDeleted` =0
AND IF ( contactVisibility = "private"
, SELECT * FROM contact_details
WHERE contactUserId = 1
, IF( contactVisibility = "group"
, SELECT * FROM contact_details
WHERE contactGroup = 3
)
)
If I'm understanding your question correctly (which is difficult with the lack of info you've provided. Sample datasets and expected outcomes are typically helpful), then I don't believe you need IFs at all for what you want. The following will return contacts that are not deleted and who either have (visibility = "private" and userId = 1) OR (visibility = "group" and group = 3)
SELECT *
FROM contact_details
WHERE contactDeleted = 0
AND (
(contactVisibility = "public")
OR
(contactVisibility = "private" AND contactUserId = 1)
OR
(contactVisibility = "group" AND contactGroup = 3)
)
I am assuming you want to use the IF() function and not the statement which is for stored functions..
Refer to this link for more information on that.
Notice that you have put 2 select statements in there, where the custom return values are supposed to be. So you are returning a SELECT *... now notice that in your upper level sql statement you have an AND.. so you basically writing AND SELECT *.. which will give you the syntax error.
Try using .. AND x IN (SELECT *) .. to find if x is in the returned values.
Let me also list this link to make use of an existing and well written answer which may also applicable to your question.

Django ORM Creates Phantom Alias in SQL Join

I am executing the following code (names changed to protect the innocent, so the model structure might seem weird):
memberships =
models.Membership.objects.filter(
degree__gt=0.0,
group=request.user.get_profile().group
)
exclude_count =
memberships.filter(
member__officerships__profile=request.user.get_profile()
).count()
if exclude_officers_with_profile:
memberships = memberships.exclude(
member__officerships__profile=request.user.get_profile()
)
total_count = memberships.count()
which at this point results in:
OperationalError at /
(1054, "Unknown column 'U1.id' in 'on clause'")
The SQL generated is:
SELECT
COUNT(*)
FROM
`membership`
WHERE (
`membership`.`group_id` = 2 AND
`membership`.`level` > 0.0 AND
NOT (
`membership`.`member_id`
IN (
SELECT
U2.`member_id`
FROM
`membership` U0 INNER JOIN `officers` U2
ON (U1.`id` = U2.`member_id`)
WHERE U2.`profile_id` = 2
)
)
)
It appears that the SQL Join's ON statement is referencing an alias that hasn't been defined. Any ideas why!? I dropped my MySQL database and re-synced the tables from my models to try and ensure that there weren't any inconsistencies there.
The structure of the models I'm using are:
class Membership(models.Model):
member = models.ForeignKey(Member, related_name='memberships')
group = models.ForeignKey(Group, related_name='memberships')
level = models.FloatField(default=0.0)
class Member(models.Model):
name = models.CharField(max_length=32)
class Officer(models.Model):
member = models.ForeignKey(Member, related_name='officerships')
profile = models.ForeignKey(UserProfile)
class UserProfile(models.Model)
group = models.ForeignKey(Group)
class Group(models.Model)
pass
I think this may be a symptom of:
http://code.djangoproject.com/ticket/9188
which was fixed as of django revision 9589, I think. Now how to figure out which revision I'm working from...
Confirmed. When I implemented the change referenced in the ticket above:
http://code.djangoproject.com/changeset/9589
my error went away.