Optimiser deux sous-requête EXISTS OR NOT EXISTS

L'auteur de ce sujet a trouvé une solution à son problème.
Staff
Auteur du sujet

Coucou,

J'ai deux tables :

  • users ( user_id, user_firstname, user_lastname… )
  • cotisations_users ( cotiu_id_user, cotiu_start, cotiu_end )

Chaque entrée de cotisation_users est donc une durée car les cotisations sont valables un an, à partir de la date du paiement. Mais certains utilisateurs ne paient pas de cotisation, donc il n'y a pas toujours d'entrée cotisation_users correspondant à un user_id.

Il me faut donc une requête pour lister tous les utilisateurs qui n'ont pas encore d'entrée dans cotisations_users, mais également ceux dont la date de fin de la cotisation est dépassée.

J'ai fait cette requête qui fonctionne :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
SELECT  user_id, user_firstname, user_lastname
FROM    users
WHERE   NOT EXISTS
        (
                SELECT  cotiu_id_user
                FROM    cotisations_users
                WHERE   cotiu_id_user = user_id
        )
        OR EXISTS
        (
                SELECT  cotiu_id_user, cotiu_end
                FROM    cotisations_users
                WHERE   cotiu_id_user = user_id AND cotiu_end < CURRENT_DATE()
        )

Mais je trouve ça relativement affreux puisque ça me fait potentiellement deux sous-requêtes… J'imagine qu'il y a moyen de faire mieux, mais comme je ne suis pas du tout expert en SQL, je poste ici ^^

Merci :)

Édité par Thunderseb

Responsable de la validation - TodoFox - Le JavaScript, c'est bon, mais pas jQuery ! Séries

+0 -0
Staff

J'aurai plutot opté pour une jointure externe. ça donnerait quelque chose de la sorte.

1
2
3
4
5
SELECT user_id, user_firstname, user_lastname
FROM users
FULL OUTER JOIN cotisations_users
ON cotisations_users.cotiu_id_user=users.user_id
WHERE (cotisations_users.cotiu_id_user IS NULL) OR (cotisations_users.cotiu_end < CURRENT_DATE());

Pour ma par je garderai la table cotisations_users pour l'historique, mais j'aurai mis l'informations utile directment dans users avec un champ cotiu_end, de cette manière on a pas besoin du tout de jointure, juste de vérifier que la date n'est pas dépassée ou null.

Lorsque l'utilisateur cotise on ajoute une entrée dans cotisations_users et on met à jour la table users.

+0 -0
Staff
Auteur du sujet

J'aurai plutot opté pour une jointure externe. ça donnerait quelque chose de la sorte.

1
2
3
4
5
SELECT user_id, user_firstname, user_lastname
FROM users
FULL OUTER JOIN cotisations_users
ON cotisations_users.cotiu_id_user=users.user_id
WHERE (cotisations_users.cotiu_id_user IS NULL) OR (cotisations_users.cotiu_end < CURRENT_DATE());

firm1

Ca semble en effet beaucoup plus propre, mais j'ai omis de le mentionner, c'est pour MySQL, donc ce type de jointure n'est pas supporté :( . Y'a des alternatives avec UNION, mais là je patauge :-°

Pour ma par je garderai la table cotisations_users pour l'historique, mais j'aurai mis l'informations utile directment dans users avec un champ cotiu_end, de cette manière on a pas besoin du tout de jointure, juste de vérifier que la date n'est pas dépassée ou null.

Lorsque l'utilisateur cotise on ajoute une entrée dans cotisations_users et on met à jour la table users.

La source

Je n'aime pas trop l'idée de dupliquer des données. Mais c'est vrai que c'est plus simple…

Responsable de la validation - TodoFox - Le JavaScript, c'est bon, mais pas jQuery ! Séries

+0 -0
Staff

Cette réponse a aidé l'auteur du sujet

J'aurai plutot opté pour une jointure externe. ça donnerait quelque chose de la sorte.

1
2
3
4
5
SELECT user_id, user_firstname, user_lastname
FROM users
FULL OUTER JOIN cotisations_users
ON cotisations_users.cotiu_id_user=users.user_id
WHERE (cotisations_users.cotiu_id_user IS NULL) OR (cotisations_users.cotiu_end < CURRENT_DATE());

firm1

Ca semble en effet beaucoup plus propre, mais j'ai omis de le mentionner, c'est pour MySQL, donc ce type de jointure n'est pas supporté :( . Y'a des alternatives avec UNION, mais là je patauge :-°

Thunderseb

Arf, MySQL c'est vraiment pas un SGBD correct :-°

Mais on devrait s'en sortir avec une simple jointure à droite. ça donnerait quelque chose comme ça :

1
2
3
4
SELECT user_id, user_firstname, user_lastname
FROM cotisations_users AS c
RIGHT JOIN users AS u ON u.user_id = c.cotiu_id_user
WHERE (c.cotiu_id_user IS NULL) OR (c.cotiu_end < CURRENT_DATE());

Cette réponse a aidé l'auteur du sujet

Sur la base de ton premier essai, c'est pas ça que tu veux ?

1
2
3
4
5
6
7
8
SELECT  user_id, user_firstname, user_lastname
FROM    users
WHERE   NOT EXISTS
        (
                SELECT  cotiu_id_user
                FROM    cotisations_users
                WHERE   cotiu_id_user = user_id AND cotiu_end >= CURRENT_DATE()
        )

Édité par elegance

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte