web-dev-qa-db-ja.com

ORDER BY GROUPにSQLiteクエリを構築する方法は?

なかなか面白い状況です。 SQLiteデータベースにアドレスとメッセージがいっぱいあります(アドレスは一意ではなく、メッセージは一意です)。各メッセージには、日付も関連付けられています。 最初のメッセージのアドレス、メッセージ、日付、およびアドレスに関連付けられているメッセージの数を選択します。

したがって、「アドレスごとにグループ化して、アドレスごとに1つのメッセージのみを取得し、日付順に並べ替え、アドレス列のCOUNTをフェッチすることもできる」と考えました。

私はそれをしました、そしてそれはうまくいきます...ちょっと。正しい数を取得し、アドレスごとに1つのメッセージのみを取得し、日付順に並べ替えますが、アドレスの最新メッセージは選択しません。それは恣意的であるように見えます。

例として、アドレスYからの3つのメッセージ(最も古いものから最新のものまで)A、B、C、およびアドレスZからの3つのメッセージD、E、Fがあります。クエリはメッセージBおよびEをフェッチします次に日付順に並べ替えます。メッセージCとFをフェッチする必要があります。日付順にソートします。

ここに私がこれまでに持っているものがあります:

_// 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");
_

これはHAVING句と関係があるように思えますが、本当にわかりません。私はMySQLをPHPで頻繁に使用しましたが、これまでHAVINGに触れる必要はありませんでした。 HAVING句を"MAX(date)"に設定しようとしましたが、効果がありませんでした。 _GROUP BY_句を_"address, date"_に設定すると、日付順に並べ替えられますが、もちろん日付は異なるため、グループ化されているのではなく、すべて個別です。

グーグル検索は実を結ばなかった。 「Android sqlite order before group」や「Android sqlite group by order」のようなクエリでは、関連する結果は得られません。

GROUP句を削除せずに、各アドレスに対してone latestメッセージを選択するにはどうすればよいですか(COUNT()がこれに依存しているため)? 2つのクエリが必要ですか?

Editthe answer に基づいて、@ Adrianがコメントにリンクしてくれたので、2つのクエリを作成しました。同じ結果;カウントが7(アドレス(メッセージの数ではなくアドレスの総数)/アドレス)である1行で、表示されたアドレスは最新のメッセージのアドレスではありませんでした。

2つのクエリは次のとおりです。

_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には greatest-n-per-group 問題をはるかに容易にする拡張機能があります。
MAX()またはMIN()集約関数を使用していて、集約関数で使用したりグループ化したりせずに、同時に他の列を選択する場合は、これらの列の結果の値は、最大値/最小値を持つ同じレコードから取得されることが保証されています。 (これは他のSQL方言では許可されておらず、 SQLite 3.7.11 で導入されました。)

したがって、問題については、次のようなクエリを使用できます。

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

SQLite 3.7.11(ほとんどのAndroidバージョン)にある可能性が高い)または別のSQLエンジンを使用していない場合、次のクエリが機能します。

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.

解決しました!私は最終的に @ CL。 のメソッドと編集した投稿で概説したメソッド( this answer で明確化された @ Adrian )。

3つのSELECTステートメントを(@CL。の回答で説明したように)使用したくなかったため、彼の方法論を維持しながら、他のステートメントと同じINNER JOINの概念を使用しました。

結果はこれです:

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
);

そしてそれは完全に働いています!

5
Eric