web-dev-qa-db-fra.com

Comment construire une requête SQLite pour GROUP par ORDER?

J'ai une situation assez intéressante. J'ai une base de données SQLite pleine d'adresses et de messages (les adresses ne sont pas uniques; les messages le sont). Chaque message est également associé à une date. Ce que je veux faire, c'est sélectionner le premier l'adresse, le message, la date et combien de messages sont associés à l'adresse.

Alors, j'ai pensé: "Je peux simplement GROUPER par l'adresse pour obtenir un seul message par adresse, puis les COMMANDER par la date, et aussi récupérer le COUNT de la colonne d'adresse."

J'ai fait ça, et ça marche ... un peu. Il récupère le nombre correct, récupère un seul message par adresse et les classe par date - mais il ne sélectionne pas le message le plus récent pour l'adresse. Cela semble arbitraire.

Par exemple, j'ai trois messages (du plus ancien au plus récent) A, B, C de l'adresse Y et trois messages D, E, F de l'adresse Z. La requête peut récupérer les messages B et E, alors les trier par date. Il devrait récupérer les messages C et F, et les trier par date.

Voici ce que j'ai jusqu'à présent:

// Expanded version:
Cursor cursor = db.query(
    /* FROM */ "messages_database",
    /* SELECT */ new String[]{ "*", "COUNT(address) AS count" },
    /* WHERE */ null,
    /* WHERE args */ null,
    /* GROUP BY */ "address",
    /* HAVING */ null,
    /* ORDER BY */ "date DESC"
);

// Or, same code on one line:
Cursor cursor = db.query("messages_database", new String[]{ "*", "COUNT(address) AS count" }, null, null, "address", null, "date DESC");

J'ai l'impression que cela peut avoir à voir avec la clause HAVING, mais je ne sais vraiment pas. J'ai beaucoup utilisé MySQL avec PHP, mais je n'ai jamais eu à toucher HAVING auparavant. J'ai essayé de définir ma clause HAVING sur "MAX(date)", mais cela n'a eu aucun effet. Si je définis ma clause GROUP BY Sur "address, date", Elles sont triées par date, mais bien sûr, elles sont toutes individuelles au lieu de groupées (car les dates diffèrent).

Les recherches sur Google se sont révélées infructueuses; des requêtes comme " Android sqlite order before group" et " Android sqlite group by order" ne donnent aucun résultat associé.

Comment puis-je sélectionner le message un dernier pour chaque adresse sans supprimer ma clause GROUP (car COUNT() en dépend)? Ai-je besoin de deux requêtes?

Edit : Sur la base de la réponse @Adrian m'a lié dans les commentaires, j'ai trouvé deux requêtes qui ont toutes deux produit le même résultat; une ligne, dans laquelle le nombre était de 7 (qui est le nombre total d'adresses, pas de messages par adresse), et l'adresse indiquée n'était pas celle du dernier message.

Les deux requêtes étaient:

Cursor cursor = db.rawQuery(
      "SELECT t.*, COUNT(t.message_content) AS count "
    + "FROM messages_database t "
    + "INNER JOIN ("
    + "    SELECT address, MAX(date) AS maxdate "
    + "    FROM messages_database "
    + "    GROUP BY address "
    + ") ss ON t.address = ss.address AND t.date = ss.maxdate",
    null
);
Cursor cursor = db.rawQuery(
      "SELECT t1.*, COUNT(t1.message_content) AS count "
    + "FROM messages_database t1 "
    + "LEFT OUTER JOIN messages_database t2 "
    + "ON (t1.address = t2.address AND t1.date < t2.date) "
    + "WHERE t2.address IS NULL",
    null
);
17
Eric

SQLite a une extension qui rend les problèmes le plus grand-n-par-groupe beaucoup plus faciles:
Si vous utilisez les fonctions d'agrégation MAX() ou MIN(), et si vous sélectionnez d'autres colonnes en même temps sans les utiliser dans une fonction d'agrégation ou en les regroupant , alors les valeurs résultantes pour ces colonnes sont garanties de sortir du même enregistrement qui a la valeur maximale/minimale. (Ceci n'est pas autorisé dans d'autres dialectes SQL et a été introduit dans SQLite 3.7.11 .)

Donc, pour votre problème, vous pouvez utiliser une requête comme celle-ci:

SELECT *, COUNT(address) AS count, MAX(date)
FROM messages_database
GROUP BY address

Si vous n'avez pas SQLite 3.7.11 (qui est probablement sur la plupart Android) ou en utilisant un autre moteur SQL, la requête suivante fonctionnera:

SELECT *,
       (SELECT COUNT(address) AS count
        FROM messages_database m2
        WHERE m1.address = m2.address)
FROM messages_database m1
WHERE date = (SELECT MAX(date)
              FROM messages_database m3
              WHERE m1.address = m3.address)
GROUP BY address
15
CL.

Résolu! J'ai fini par utiliser une combinaison de la méthode de @ CL. et les méthodes que j'ai décrites dans mon article édité (clarifiées dans cette réponse , publiées par @ Adrian ).

Parce que je ne voulais pas utiliser 3 SELECT instructions (comme décrit la réponse de @ CL.), J'ai utilisé les mêmes INNER JOIN concept comme dans les autres énoncés, tout en conservant sa méthodologie.

Le résultat est le suivant:

Cursor cursor = db.rawQuery(
      "SELECT t.*, ss.count AS count "
    + "FROM messages_database t "
    + "INNER JOIN ("
    + "    SELECT address, MAX(date) AS maxdate, COUNT(address) AS count "
    + "    FROM messages_database "
    + "    GROUP BY address "
    + ") ss ON t.address = ss.address AND t.date = ss.maxdate "
    + "GROUP BY t.address "
    + "ORDER BY t.date DESC ",
    null
);

Et ça marche parfaitement!

5
Eric