2011-01-07 3 views
15

En utilisant SQLalchemy, je veux effectuer une jointure externe gauche et filtrer les lignes qui ont une correspondance dans la table jointe.Filtrage sur une jointure gauche dans SQLalchemy

J'envoie des notifications push, donc j'ai une table Notification. Cela signifie que j'ai également une table ExpiredDeviceId pour stocker device_ids qui ne sont plus valides. (Je ne veux pas simplement supprimer les notifications affectées que l'utilisateur pourrait plus tard réinstaller l'application, à quel point les notifications doivent reprendre selon les documents d'Apple.)

CREATE TABLE Notification (device_id TEXT, time DATETIME); 
CREATE TABLE ExpiredDeviceId (device_id TEXT PRIMARY KEY, expiration_time DATETIME); 

Note: il peut y avoir plusieurs Notifications par device_id. Il n'y a pas de table "Device" pour chaque périphérique.

Donc, en faisant SELECT FROM Notification, je devrais filtrer en conséquence. Je peux le faire en SQL:

SELECT * FROM Notification 
    LEFT OUTER JOIN ExpiredDeviceId 
    ON Notification.device_id = ExpiredDeviceId.device_id 
WHERE expiration_time IS NULL 

Mais comment puis-je le faire dans SQLalchemy?

sess.query(
    Notification, 
    ExpiredDeviceId 
).outerjoin(
    (ExpiredDeviceId, Notification.device_id == ExpiredDeviceId.device_id) 
).filter(
    ??? 
) 

Sinon je pourrais le faire avec une clause device_id NOT IN (SELECT device_id FROM ExpiredDeviceId), mais qui semble bien moins efficace.

Répondre

13

Avez-vous besoin d'extraire l'ExpiredDeviceId dans un tuple? Si vous ne le faites pas (c'est-à-dire que vous ne vous souciez que de device_ids en direct), alors ne pouvez-vous pas simplement:

sess.query(
    Notification 
).outerjoin(
    (ExpiredDeviceId, Notification.device_id == ExpiredDeviceId.device_id) 
).filter(
    ExpiredDeviceId.expiration_time == None 
) 
+0

Vous avez raison. J'ai incorrectement supposé que ExpiredDeviceId était en fait une référence d'objet, et que s'il n'y avait pas de ligne correspondante, ExpiredDeviceId lui-même serait None. Cependant, il s'agit en fait d'un nom de classe (table) et l'expression est compilée en SQL valide. (Notez que pour le libellé de ma question, je dois vérifier si * IS * None, ce qui fonctionne aussi.) –

+0

Ah bon point, j'ai édité mon extrait de code pour éviter toute confusion –

+1

'ExpiredDeviceId.expiration_time is None' ne sera pas work - il sera toujours évalué à False avant d'être passé à .filter(), provoquant un comportement inattendu (inattendu si vous vous attendez à lire la requête comme vous liriez SQL). Utilisez '=='. – Baczek