SqlAlchemy makes strange implicit queries - sqlalchemy

I have relation in my user model
photos = relationship('UserPhoto', backref='user', lazy='joined')
After I get all entries with
users = query.all()
SA makes query with LEFT OUTER JOIN to userPhoto table
I start to iterate over them and print all properties
And suddenly SA makes strange implicit queries for each entry
INFO:sqlalchemy.engine.base.Engine:BEGIN (implicit)
INFO:sqlalchemy.engine.base.Engine:SELECT users.id AS users_id, ... FROM users
WHERE users.id = %(param_1)s
INFO:sqlalchemy.engine.base.Engine:{'param_1': <integer idetifier>}
INFO:sqlalchemy.engine.base.Engine:SELECT user_photo.id... FROM user_photo
WHERE %(param_1)s = user_photo.user_id
INFO:sqlalchemy.engine.base.Engine:{'param_1': <integer idetifier>}
I've spent a few hours to find out the reason, but still searching
If I construct joins myself like below:
query.options(joinedload_all('some field'))
SA makes only one select with join

Related

Am I doing nested joins incorrectly?

Folks! I am scratching my head on this one. Right when I add UserRole part in last join I start seeing this error. What am I doing wrong? Is there a limit on number of nested joins in MySQL?
Error:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ON Opportunity.AccountId = AccountD.Id' at line 31
Table Structure
Opportunity
-----------
Id, Name, AccountId, OwnerId
Account
-------
Id,Name,OwnerId
User
----
Id,Name,UserRoleId
UserRole
--------
Id,Name
RecordType
----------
Id,Name
SQL
SELECT
Opportunity.Id AS 'Opportunity_ID',
Opportunity.Opportunity_Number__c AS 'Opportunity_Number__c',
UserA.Name AS 'Opportunity_Owner',
Opportunity.Name AS 'Opportunity_Name',
Opportunity.Probability AS 'Opportunity_Probability',
Opportunity.StageName AS 'Opportunity_Stage',
Opportunity.Amount AS 'Opportunity_Amount',
Opportunity.CloseDate AS 'Opportunity_CloseDate',
Opportunity.Roll_Out_End_Formula__c AS 'Opportunity_Rollout_End_Date',
RecordTypeA.Name AS 'Record_Type',
Opportunity.Division__c AS 'Division',
Pricebook2.Name AS 'Price_Book',
Opportunity.Won_Date__c AS 'Opportunity_Won_Date',
Opportunity.CreatedDate AS 'Opportunity_Created_Date',
AccountA.Id AS 'Account_ID',
AccountA.Name AS 'Account_Name',
AccountA.Type AS 'Account_Type',
RecordTypeB.Name AS 'Account_Record_Type',
AccountA.Key_Account__c AS 'Key_Account',
UserB.Name AS 'Account_Owner',
UserB.Sales_Team__c AS 'Account_Owner_Sales_Team',
UserRoleA.Name AS 'Account_Owner_User_Role'
FROM Opportunity
LEFT JOIN User UserA ON Opportunity.OwnerId = UserA.Id
LEFT JOIN RecordType RecordTypeA ON Opportunity.RecordTypeId = RecordTypeA.Id
LEFT JOIN Pricebook2 ON Opportunity.Pricebook2Id = Pricebook2.Id
LEFT JOIN Account AccountA ON Opportunity.AccountId = AccountA.Id
LEFT JOIN Account AccountB JOIN RecordType RecordTypeB ON AccountB.RecordTypeId = RecordTypeB.Id ON Opportunity.AccountId = AccountB.Id
LEFT JOIN Account AccountC JOIN User UserB ON AccountC.OwnerId = UserB.Id ON Opportunity.AccountId = AccountC.Id
LEFT JOIN Account AccountD JOIN User UserC JOIN UserRole UserRoleA ON UserC.UserRoleId = UserRoleA.Id ON AccountD.OwnerId = UserC.Id ON Opportunity.AccountId = AccountD.Id
LIMIT 5\G
If you want to do nested joins to your "Account" instances, you have to use parentheses.
LEFT JOIN Account AccountA ON Opportunity.AccountId = AccountA.Id
LEFT JOIN (Account AccountB JOIN RecordType RecordTypeB ON AccountB.RecordTypeId = RecordTypeB.Id)
ON Opportunity.AccountId = AccountB.Id
LEFT JOIN (Account AccountC JOIN User UserB ON AccountC.OwnerId = UserB.Id)
ON Opportunity.AccountId = AccountC.Id
LEFT JOIN (Account AccountD JOIN User UserC ON AccountD.OwnerId = UserC.Id JOIN UserRole UserRoleA ON UserC.UserRoleId = UserRoleA.Id)
ON Opportunity.AccountId = AccountD.Id
Edit: I fixed the last line (the one with AccountD).
This looks like the sort of code that gets auto-generated when you use the Query Designer (tends to be a dead giveaway actually)
That said it does work, it's just difficult to read.
Replace your from clause with the following and see if you're still having problems...
FROM
Opportunity
LEFT JOIN User UserA
ON Opportunity.OwnerId = UserA.Id
LEFT JOIN RecordType RecordTypeA
ON Opportunity.RecordTypeId = RecordTypeA.Id
LEFT JOIN Pricebook2
ON Opportunity.Pricebook2Id = Pricebook2.Id
LEFT JOIN Account AccountA
ON Opportunity.AccountId = AccountA.Id
JOIN RecordType RecordTypeB
ON AccountB.RecordTypeId = RecordTypeB.Id
LEFT JOIN Account AccountB
ON Opportunity.AccountId = AccountB.Id
JOIN User UserB
ON AccountC.OwnerId = UserB.Id
LEFT JOIN Account AccountC
ON Opportunity.AccountId = AccountC.Id
LEFT JOIN Account AccountD
ON Opportunity.AccountId = AccountD.Id
JOIN User UserC
ON AccountD.OwnerId = UserC.Id
JOIN UserRole UserRoleA
ON UserC.UserRoleId = UserRoleA.Id
A nested join is nothing that you write in your query, but a technique the DBMS uses to perform a join.
When you join two tables, say, from a join b on a.aa = b.bb, the DBMS has several methods to put this into practise. For example it could read a and sort it by aa. Then it would read b and sort it by bb. Then it would read the two result list sequentially and write all the matches in a final result list.
Another method would be to read one record from a, then read b to find all matches on aa = bb. Then it would read the next record from a and read b again to find matches for this second record, and so on. This method is called a nested join.
You however, simply tell the DBMS what to join and on what criteria to join, but not what method to use. The DBMS will (hopefully) find the best method for each join.
As to your query: You select all records from Opportunity. Then you outer join the table User. So for some reason you expect some opportunities not to have an owner, but want a result row for the opportunity nonetheless. Same for RecordType, Pricebook2 and Account; you expect some opportunities not to have a match in these tables, but want the opportunity still in the results. From the names we can gather that there are foreign keys on the columns, e.g. you cannot store an opportunity with RecordTypeId = 'X' when 'X' doesn't exist in RecordType. So you outer join obviously, because these columns are optional (nullable).
Then it gets queer. You try to outer join Account again. But this time you don't specify an ON clause. This is invalid SQL, but MySQL lets it slip. The join becomes a CROSS JOIN internally, i.e. you join every row you got so far with every account. Let's say you have 1000 records in Opportunity and 500 in Account, this makes 500,000 rows. Then you suddenly inner join RecordType again. But this time you want the record type for the accounts, not for the opportunities. You join on AccountB.RecordTypeId = RecordTypeB.Id So every row with an account with a RecordTypeId null gets dismissed, the others are kept. Then you have another ON clause which is misplaced. I don't know how MySQL interprets this. Either this results in a syntax error or its treated as a WHERE clause or its treated as part of the former ON clause. This is invalid and should raise an error, but so should the missing ON clause before which doesn't.
To make a long story short: you are doing things very wrong here. We'd have to know the expected results to get this straight.
Apart from this: Single quotes are for string literals:
'lalala' -- this is a string conform with the SQL standard
lalala -- this is a name conform with the SQL standard
"lalala" -- this is a name conform with the SQL standard
-- it could also be a string in MySQL violating the SQL standard
`lalala` -- this is a name in MySQL violating the SQL standard
Queries with many tables get easier to read with short alias names
FROM opprtunity o
JOIN User u ON u.Id = o.OwnerId

SUM function summing records twice

First of all for you to understand my question I need to show you my DB model, so here it is.
Now that you have seen it, here goes my question:
I have this query
select SUM(e.totalEtapa) totalProyecto,
e.idProyecto
from Etapa e
inner join EtapaPartida ep on ep.idEtapa = e.idEtapa
inner join Partida p on p.numero = ep.idPartida
and p.version = ep.versionPartida
left join Partida b on b.numero = p.numero
and p.version < b.version
inner join LineaRecursoPartida lrp on p.numero = lrp.numPartida
and p.version = lrp.versionPartida
inner join LineaRecurso lr on lr.id = lrp.IdLinea
inner join Recurso r on r.codigo = lr.codigo
and r.version = lr.version
where r.codigo = 2
and r.version = 2
and b.version is null
group by e.idProyecto
So as you can see, and in relation you the model I show you above, I want the sum of all the records on etapa that are related to a recurso's record taking into account all the join necessary to get to it and that the partida's records should be their "maximun versions", in this case the recurso with codigo = 2 and version = 2. So the thing is this query is returning twice the value that should. Making some research I realized that this is happening because my recurso's record is related to two records on partida's table and likewise this ones are related to the same etapa's record through the EtapaPartida table.
I already tried using the distinct clause, adding the column idEtapa from table Etapa on the above query with no success. Anyway, I don't like to use this kind of clauses to get rid of the problem beacause it may cause another. So I would like to know if any of you have any idea on how just returning one record from etapa not regarding whether are they are related to several partida's record related to the same recurso's record?

Join on multiple ids

I've been helping a friend out with some of his MySQL queries, and came up with this query. While writing this I kind of wondered if there wasn't a better way to do this:
select offers.*, users.*, items.* from offers
join users on users.id = offers.sender_id
join items on
(items.id = offers.receiver_gets_1)
or
(items.id = offers.receiver_gets_2)
or
(items.id = offers.receiver_gets_3)
or
(items.id = offers.receiver_gets_4)
or
(items.id = offers.sender_gets_1)
or
(items.id = offers.sender_gets_2)
or
(items.id = offers.sender_gets_3)
or
(items.id = offers.sender_gets_4)
where receiver_id = 1;
It kind of made me feel bad, because I really have the feeling I gave him some messed up query (not that he minds). It has left me wondering if there isn't a nicer way to write this down?
I tried looking on google for stuff like these questions:
- Mysql Left Join on multiple conditions
- MySQL how to join tables on two fields
- https://dba.stackexchange.com/questions/33996/which-is-better-many-join-conditions-or-many-where-conditions
But these didn't really offer any other way. I did run in to another question where the answer was set-up the same as my own query.
I know the database could perhaps modeled better, but it's not my database to toy with. I don't really agree with how his tables are built up or anything, but the question remains interesting to me non the less.
You can use IN:
select offers.*, users.*, items.* from offers
join users on users.id = offers.sender_id
join items on
items.id IN (offers.receiver_gets_1, offers.receiver_gets_2, offers.receiver_gets_3, offers.receiver_gets_4,
offers.sender_gets_1, offers.sender_gets_2, offers.sender_gets_3, offers.sender_gets_4)
However, it's generally better to design your schema so you don't have repeated columns like receiver_gets_1, receiver_gets_2, .... Instead, you should have a relation table where each if these is in a different row, then you just need to join with it once to get all the matches.

SQL inner join doens't return proper answer

Hello i'm making an external application that needs to access and read data from a webserver running a drupal website.
I want to join two tables and read 1 column from the first table, when something from the other table is true.
the tables are "profile" and "user_roles"
http://i.stack.imgur.com/jRhFG.png
as you can see when i run "SELECT * FROM users_roles WHERE rid = 10" it returns results
http://i.stack.imgur.com/jU5GL.png
and when i "SELECT * FROM profile where uid = 220" it also returns a result, there a alot of users in these tables i just included a small part to demonstrate the point.
But when i run "SELECT pid FROM profile INNER JOIN users_roles on profile.pid = users_roles.uid WHERE users_roles.rid = 10"
It returns nothing, if i change the same statement to users_roles.rid = 5 it returns them correctly.
but with 10 it doesn't.
I need to get all the pid's where the role id is a given number
can anyone tell me what im doing wrong?
I think you have the join statement wrong. You need to join on uid columns the tables like the following
SELECT pid FROM profile
INNER JOIN users_roles on profile.uid = users_roles.uid WHERE users_roles.rid = 10

MySQL subquery question

I am trying to accomplish something and havent been able to find out if it can be done. I have a table "Events" that has event info. Within that table are ID's for Associations that sponsor that event. Sometimes it is only one and sometimes it can be as many as five. These Associations are in the "Associations" table. Within the Associations table, there are several details about that Association. What I am trying to do is do a query that will search the events table, and get all of the events that are between now and the event date, as well as retrieve the information about each Association that relates to each particular event. Here is the query that I have so far:
SELECT
`Events`.EventID,
`Events`.AssociationID,
`Events`.Association2ID,
`Events`.Association3ID,
`Events`.Association4ID,
`Events`.Association5ID,
`Events`.DateFrom,
`Events`.DateTo,
`Events`.EventName,
`Events`.VenueID,
`Events`.TestnTune,
`Events`.ShownShine,
`Events`.SpecialInfo,
`Events`.OtherInfo,
`Events`.Rating,
`Events`.EventOverlay,
`Events`.HavePictures,
`Events`.IncludeInSchedule,
`Events`.EventURL,
Associations.Active,
Associations.Acronym,
Associations.OrgName,
Associations.WebURL,
Associations.LogoURL,
Associations.AssociationID,
Venues.LocationName,
Venues.Location,
Venues.longetude,
Venues.latitude,
Venues.Directions,
Venues.SitePros,
Venues.SiteCons,
Venues.BasicInfo,
Venues.SiteRating,
Venues.HostedEvents,
Venues.CurrentWeather
FROM
`Events`
LEFT JOIN Associations ON `Events`.AssociationID = Associations.AssociationID AND `Events`.Association2ID = Associations.AssociationID AND `Events`.Association3ID = Associations.AssociationID AND`Events`.Association4ID = Associations.AssociationID AND `Events`.Association5ID = Associations.AssociationID
LEFT JOIN Venues ON `Events`.VenueID = Venues.VenueID
WHERE
`Events`.DateFrom >= NOW()
It looks like you're trying to model an many-to-many relationship by using several 1-n relationships and naming them association1, association2, etc... That's probably not a good idea.
Create a new entity called EventToAssociation (or similar), holding foreign keys to both events, and association. Then, your joins will be a lot easier to create.
In an example query, this would read:
SELECT *
FROM Event e
JOIN EventToAssociation e2a ON e.EventID = e2a.EventID
JOIN Association a ON e2a.AssociationID = a.AssociationID
If changing the schema is not an option, then you'll have to join Association several times to Event, e.g.
SELECT *
FROM Event e
LEFT OUTER JOIN Associations a1 ON e.AssociationID = a1.AssociationID
LEFT OUTER JOIN Associations a2 ON e.Association2ID = a2.AssociationID
LEFT OUTER JOIN Associations a3 ON e.Association3ID = a3.AssociationID
LEFT OUTER JOIN Associations a4 ON e.Association4ID = a4.AssociationID
LEFT OUTER JOIN Associations a5 ON e.Association5ID = a5.AssociationID
Every join you make is between your Events-Table and the same Associations-Table. This is just like an AND-Condition.
Try using Aliases:
LEFT JOIN Associations Asso1 ON `Events`.AssociationID = Asso1.AssociationID
LEFT JOIN Associations Asso2 ON `Events`.Association2ID = Asso2.AssociationID