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.
I have an entity called tracking. It relates to a User and a Course.
I also have an entity called credential which officially links User and Course
User <- Tracking -> Course || User <- Credential -> Course
Tracking is a joining entity of sorts, but it's not the primary join between the two. I have a query where I already have the user and the course joined, and I want to left-join the tracking if it exists. I've tried to simplify my example.
$q->select(
'user.id',
'user.firstname',
'user.lastname',
'count(t) as courses',
'count(t.completed) as completed'
);
$q->from(Credential::class, 'c');
$q->from(Course::class, 'course');
$q->from(User::class, 'user');
$q->leftJoin(Tracking::class, 't', 'WITH', 't.user = user and t.course = course');
$q->where('c.user = user and c.object = course');
$q->groupBy('user');
What I'm trying to achieve here, is a list of users who are enrolled in courses, the number of courses, and where possible the number of completed courses.
Unfortunately, doctrine can only seem to join to either the user table or the course table, but not to both. This might even be a mysql limitation. I've debugged this over and over - and I've run into the problem several times with different examples - and I just can't seem to find a solution other than using ->from(Tracking) which would eliminate students who haven't started any courses, and stats from courses they haven't started. I've googled over and over again, but It's so hard to search for this problem and not get 'How to join two tables with Doctrine'.
I get the error Column not found: 1054 Unknown column 'c1_.id' in 'on clause' which I assume means it can join on t.user = user but not t.course = course
Here is the actual code and error
$q = $this->em->createQueryBuilder();
$q->select(
'user.id',
'user.firstname',
'user.lastname',
'count(sc.id) as courses',
'count(ct.commenced) as commenced',
'count(ct.completed) as completed',
'avg(ct.scorePercent) as avgscore',
'avg(ct.totalTime) as avgtime'
);
$q->from(Security\Credential::class, 'c');
$q->from(Security\SecurableCourse::class, 'sc');
$q->from(Security\AccreditableInheritance::class, 'ai');
$q->from(Security\AccreditableUser::class, 'au');
$q->from(User::class, 'user');
$q->join(Tracking\CourseTracking::class, 'ct', 'WITH', 'ct.objectIdentity = sc and ct.user = user');
$q->where('sc = c.securable and ai.parent = c.accreditable and au = ai.child and user = au.user');
$q->andWhere('c.action = :action and sc.course in (:courses)');
$q->setParameter('action', 'study')->setParameter('courses', $courses);
$q->groupBy('user.id');
$users = $q->getQuery()->getScalarResult();
Doctrine\DBAL\Exception\InvalidFieldNameException(code: 0): An exception occurred while executing 'SELECT u0_.id AS id_0, u0_.firstname AS firstname_1, u0_.lastname AS lastname_2, count(s1_.id) AS sclr_3, count(t2_.commenced) AS sclr_4, count(t2_.completed) AS sclr_5, avg(t2_.scorePercent) AS sclr_6, avg(t2_.totalTime) AS sclr_7 FROM Credential c3_ INNER JOIN Tracking t2_ ON (t2_.objectIdentity_id = s1_.id AND t2_.user_id = u0_.id) AND t2_.dtype IN ('coursetracking') AND ((t2_.deleted IS NULL OR t2_.deleted > '2016-04-26 08:33:31')), SecurableIdentity s1_, AccreditableInheritance a4_, AccreditableIdentity a5_, User u0_ WHERE (((s1_.id = c3_.securable_id AND a4_.parent_id = c3_.accreditable_id AND a5_.id = a4_.child_id AND u0_.id = a5_.user_id) AND (c3_.action = ? AND s1_.course_id IN (?, ?, ?))) AND ((u0_.deleted IS NULL OR u0_.deleted > '2016-04-26 08:33:31'))) AND (s1_.dtype IN ('securablecourse') AND a5_.dtype IN ('accreditableuser')) GROUP BY u0_.id' with params [\"study\", \"46\", \"45\", \"160\"]:\n\nSQLSTATE[42S22]: Column not found: 1054 Unknown column 's1_.id' in 'on clause'
This is just a hint how to achieve it. I cannot give you the correct answer as you don't give enough details. But this will help you to achieve what you need.
$q->select(u, t, co, ce);
$q->from('User', 'u');
$q->leftJoin('u.tracking', 't');
$q->leftJoin('t.course', 'co');
$q->leftJoin('u.credential', 'ce');
I had a query which was working just fine:
#schedule = Schedule.find(params[:id])
#schedule_tasks = ScheduleTask.select("s.*, t.*, t.*, d.*, st.*").from("schedule_tasks st").
joins("left join schedules s ON s.id = st.schedule_id").
joins("left join tasks t ON t.id = st.task_id").
joins("right join days d ON d.id = st.day_id").
order("d.number, t.name").
group_by{|d| d.number}
I had to refine my search to only schedule_tasks with a specific schedule_id, so I edited the second line to:
joins("left join schedules s ON s.id = st.schedule_id AND s.id = ?", #schedule.id).
This has cause the following error:
unknown class: Fixnum
The error goes away if I take out the group_by - but I need that, and I have tried hard coding in the number instead of #schedule.id and that does not work either, a google search does not reveal a lot of details on this error.
For anyone coming here from Google, I used plain string interpolation to fix this issue. This method is vulnerable to SQL Injection, so make sure you type check your variables before using them.
In this case I would do
#schedule_id = #schedule.id
.
.
.
joins("left join schedules s ON s.id = st.schedule_id AND s.id = #{#schedule_id}")
Rather than following learning_to_swim's answer, which as noted is at risk of SQL injection, couldn't you cast your #schedule_id to a string?
#tasks = ScheduleTask.joins("left join [...] s.id = ?", #schedule.id.to_s)
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")
)
Mysql Version Works
UPDATE results SET rCARRIER = (
SELECT cellCarrierName
FROM tblImportedTempTable, user, cellCarrier
WHERE
userEmployeeNumber = tblImportedTempTable.EMPLOYEENUMBER
AND userId = results.rUserId
AND results.rPHONENUMBER = tblImportedTempTable.PHONENUMBER
AND CARRIER = cellCarrierId )
I have written this sql that works fine in MySql(above) and fails in access 2003(below) any suggestions? Is one or both of the 2 nonstandard sql? Does Access hav an admin problem?
Sorry the field and table names are diferent this is the ACCESS version.
Access version
UPDATE tblWorkerPhoneNumber SET tblWorkerPhoneNumber.PhoneCarrier = (
SELECT PhoneCarrierType.CarrierName
FROM tblImportedPhoneCarrier, tblWorkerMaster, PhoneCarrierType
WHERE
tblWorkerMaster.EmployeeNumber = tblImportedPhoneCarrier.Emp
AND tblWorkerMaster.WorkerID = tblWorkerPhoneNumber.WorkerID
AND tblWorkerPhoneNumber.PhoneNumber = tblImportedPhoneCarrier.Cell
AND tblImportedPhoneCarrier.CarrierCode = PhoneCarrierType.CarrierID )
Error Message
Operation must use and updateable query
Thanks
In MS Access, something like this:
UPDATE tblWorkerPhoneNumber
INNER JOIN tblWorkerMaster ON tblWorkerMaster.WorkerID = tblWorkerPhoneNumber.WorkerID
INNER JOIN tblImportedPhoneCarrier ON tblWorkerPhoneNumber.PhoneNumber = tblImportedPhoneCarrier.Cell
INNER JOIN PhoneCarrierType ON tblImportedPhoneCarrier.CarrierCode = PhoneCarrierType.CarrierID
SET tblWorkerPhoneNumber.PhoneCarrier = PhoneCarrierType.CarrierName
WHERE tblWorkerMaster.EmployeeNumber = tblImportedPhoneCarrier.Emp
(Might need to change the join conditions; I'm not familiar with your schema)