SELECT + 2 COUNT (avec 2 différentes conditions) en une seule requête SQL

Le problème exposé dans ce sujet a été résolu.

Bonjour.

J’ai besoin d’un peu d’aide SVP car je n’arrive pas à finir une requête SQL.

En une seule requête SQL, je veux :

Récupérer tous les utilisateurs + Compter leurs nombre de commandes validés + Compter leurs nombre de commandes qui ne sont pas validés.

Voici ma requête SQL actuelle :

User::select([
        Database::USERS.'.id', Database::USERS.'.first_name', Database::USERS.'.last_name',
        // ces 2 COUNT me retournent la même valeur, c'est ça le problème...
        DB::raw('COUNT( CASE WHEN '.Database::ORDERS.'.validated_at IS NOT NULL THEN 1 ELSE 0 END ) as nbOrdersValidatedJoined'),
        DB::raw('COUNT( CASE WHEN '.Database::ORDERS.'.validated_at IS NULL THEN 1 ELSE 0 END ) as nbOrdersNotValidatedJoined')
    ])
    ->join(
        Database::ORDERS,
        Database::USERS.'.id', '=', Database::ORDERS.'.user_id'
    )
    ->groupBy(
        Database::USERS.'.id', Database::USERS.'.first_name', Database::USERS.'.last_name',
    )
    ->get();

Là c’est du Laravel, mais c’est compréhensible pour tous développeurs PHP.

PS :

_Les commandes validés ont le champ 'validated_at' qui n’est pas NULL (je met la date de validation lorsqu’une commande est validé).

_Database::USERS est la tables des utilisateurs et Database::ORDERS est la tables des commandes.

Je précise aussi que c’est la 1ère fois que j’essaye d’utiliser CASE WHEN… C’est la où je ne doit surement pas bien faire les choses… Et je ne sais même pas si c’est techniquement possible de faire ce que je veux en une seule requête SQL.

Et à l’affichage dans la vue de listage, je veux afficher le nombre de commandes validés par utilisateur et le nombre de commandes non validés par utilisateur comme ceci :

@foreach ($users as $user)
    {{ $user->first_name }} {{ $user->nbOrdersValidatedJoined }} {{ $user->nbOrdersNotValidatedJoined }}
@endforeach

Merci beaucoup.

+0 -0

Salut,

Je n’ai pas la solution à ton souci, mais plusieurs remarques / questions.

Pourquoi ne pas effectuer les 2 requêtes séparément ? Ce serait plus simple, voire plus performant il me semble (à confirmer par d’autres sur ce point).

Sinon, tu ne peux pas voir dans une console (ou une barre de debug) la requête SQL générée ? Ça te donnerait peut-être une piste sur l’origine du souci.
D’ailleurs, tu dis que les 2 count() retournent le même résultat, quel est-il ?
Celui des commandes validées ? Celui des commandes non validées ? Autre chose ?

Remplace Count par SUM dans ta requête, et ça devrait être bon.

Ou bien, si tu veux utiliser count(), remplacer les 1 else 0 par 1 else NULL —> Cette remarque devrait te faire comprendre pourquoi ta requête ne marchait pas.

Sinon, faire une seule requête est mieux que faire 2 requêtes. Tu sollicites le serveur une seule fois au lieu de 2, et en gros (c’est à peine carricatural), ça va 2 fois plus vite avec une seule requête au lieu de 2.

Edit : pour compter les lignes avec Date_validated not null, il y a en fait plus simple : count( Date_validated) permet d’avoir les lignes pour lesquelles date_validated est renseignée.

Et pour le complément, à vérifier : count(date_validated is not null) ou bien : count( not isnull(date_validated))

+1 -0

Sinon, tu ne peux pas voir dans une console (ou une barre de debug) la requête SQL générée ? Ça te donnerait peut-être une piste sur l’origine du souci.
D’ailleurs, tu dis que les 2 count() retournent le même résultat, quel est-il ?
Celui des commandes validées ? Celui des commandes non validées ? Autre chose ?

Mysterri1

Merci pour ta réponse. ça me retournais les nombres de toute les commandes validés ou non.

Remplace Count par SUM dans ta requête, et ça devrait être bon.

Ou bien, si tu veux utiliser count(), remplacer les 1 else 0 par 1 else NULL —> Cette remarque devrait te faire comprendre pourquoi ta requête ne marchait pas.

Sinon, faire une seule requête est mieux que faire 2 requêtes. Tu sollicites le serveur une seule fois au lieu de 2, et en gros (c’est à peine carricatural), ça va 2 fois plus vite avec une seule requête au lieu de 2.

Edit : pour compter les lignes avec Date_validated not null, il y a en fait plus simple : count( Date_validated) permet d’avoir les lignes pour lesquelles date_validated est renseignée.

Et pour le complément, à vérifier : count(date_validated is not null) ou bien : count( not isnull(date_validated))

elegance

Merci beaucoup, tu es trop fort en SQL ! Tout ce que tu dit fonctionne, sauf : count(date_validated is not null) ou bien : count( not isnull(date_validated))

Merci.

+0 -0

Yes OK !

J’avais résussi à avoir le résultat que je souhaitais avec une seule req SQL, mais en faisant 2 boucles en PHP… En m’amusant à créer des array…

Si je suis venu poser la question ici c’est justement pour progresser sur des req SQL "complexes". Req SQL propre est parfois = un peu moins de code PHP pour le même résultat.

Si j’ai bien compris :

_Ceci :

DB::raw('SUM(CASE WHEN '.Database::ORDERS.'.validated_at IS NOT NULL THEN 1 ELSE 0 END) as nbOrdersValidatedJoined'),
DB::raw('SUM(CASE WHEN '.Database::ORDERS.'.validated_at IS NULL THEN 1 ELSE 0 END) as nbOrdersNotValidatedJoined'),

Fonctionne car on fait un SUM de la valeur du THEN (ici c’est "1") ou du ELSE (ici c’est "0") selon si 'validated_at' est NULL ou NOT NULL. (Je me suis amusé à faire des tests en remplaçant le "1" ou le "0" par d’autres nombres pour comprendre le fonctionnement du CASE dans un SUM).

_Ceci :

DB::raw('COUNT(CASE WHEN '.Database::ORDERS.'.validated_at IS NOT NULL THEN 1 ELSE NULL END) as nbOrdersValidatedJoined'),
DB::raw('COUNT(CASE WHEN '.Database::ORDERS.'.validated_at IS NULL THEN 1 ELSE NULL END) as nbOrdersNotValidatedJoined'),

Fonctionne car si 'validated_at' est NOT NULL, 'validated_at' prendra la valeur "1" donc ça vaudra une ligne. Et si 'validated_at' est NULL, 'validated_at' prendra la valeur NULL donc ça ne vaudra pas de ligne donc le COUNT ne comptera pas cette ligne. D’ailleurs, à la place de "1" on peux mettre n’importe quel autre chiffre (y compris le "0") que ça donne le même résultat étant donné que c’est le nombre de lignes qui ne sont pas NULL que ça compte.

_Et ceci :

DB::raw('COUNT('.Database::ORDERS.'.validated_at) as nbOrdersValidatedJoined'),
DB::raw('COUNT(CASE WHEN '.Database::ORDERS.'.validated_at IS NULL THEN 1 ELSE NULL END) as nbOrdersNotValidatedJoined'),

Reviens au même que mon dernier exemple, car quand on fait juste un COUNT de 'validated_at' ça compte toute les lignes qui ne sont pas NULL (c’est d’ailleurs la base du COUNT).

voilà, je pense avoir appris ma leçon par cœur :)

Encore merci !

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

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