2

読者に読んだ旨を伝える掲示板のようなものを作っているのですが、もっと効率的な方法はないかと考えていました。

MySQL 側に 3 つのテーブルがあります。

    +-----------------+        +-----------------+        +-----------------+
    |  Announcements  |        | Acknowledgement |        |      User       |
    +-----------------+        +-----------------+        +-----------------+
    | announce_id     |        | ack_id          |        | user_id         |
    | announce_msg    |        | announce_id     |        | user_name       |
    | ...             |        | user_id         |        | ...             |
    +-----------------+        +-----------------+        +-----------------+

ユーザーが (ボタンをクリックして) アナウンスを「読む」Acknowledgmentと、アナウンス ID とユーザー ID を含むテーブルが挿入されます。2 番目のユーザーが同じアナウンスを「読む」と、Acknowledgementテーブルは同じアナウンス ID と 2 番目のユーザー ID などで再度挿入されます...

    +--------------------------------+
    |        Acknowledgement         |
    +--------+-------------+---------+
    | ack_id | announce_id | user_id |
    +--------+-------------+---------+
    |   1    |      1      |    1    |
    |   2    |      1      |    4    |
    |   3    |      1      |    3    |
    |   4    |      3      |    1    |
    |   5    |      3      |    6    |
    |   6    |      3      |    2    |
    +--------+-------------+---------+

今問題に。フロント エンドで、ページにすべてのお知らせを一覧表示する場合、最初にすべてのお知らせをクエリする必要があります。次に、アナウンスごとに、このアナウンスを読んだすべてのユーザーに対して別のクエリを実行する必要があります。

 $sql = "select * from Announcements";
 $result = $pdo->query($sql);
 while ($row = $result->fetch())
 {
   $announce_id = $row['announce_id'];
   $announce_msg = $row['annouce_msg'];
   $readers = "";

   $sql2 = "select u.user_name from Acknowledgement as a INNER JOIN User as u where announcement_id =".$annouce_id;
   $result2 = $pdo->query($sql);
   while ($row2 = $result2->fetch())
   {
     $readers .= $row2['user_name'].", ";
   }

   echo "id:".$annouce_id.", message:".$announce_msg.", Readers:".$readers;
 }

そのため、ページに 10 個のお知らせがある場合、各お知らせに対して 10 個のサブクエリが存在します。私が今持っているものは今のところ仕事をしています...しかし、1000のアナウンスがあるとしたらどうでしょうか? 次に、1000のサブクエリがありますか?データベースが本当に打撃を受けるように聞こえます。ですから、これを行うためのより良い方法があることを願っています。

また、ユーザー テーブルの 1000 人が 1000 件のアナウンスすべてを読む場合、承認テーブルには 1000x1000 エントリが含まれます。承認テーブルが非常に長くなるようです。時間が経つと問題になるのでしょうか?

これは私がやろうとしていることの非常に大まかな例ですが、これをすべて書くのに長い時間がかかりました. 詳細が必要な場合はお知らせください。

4

2 に答える 2

3

もっと良い方法があります。group_concat で単一のクエリを使用できます。

select a.*, group_concat(u.user_name separator ', ') as AllUsers
from Announcements a join
     Acknowledgement ak
     on a.Announce_Id = ak.Announce_Id join
     User u
     on u.user_ID = ak.User_ID
group by a.announce_id

これは、非表示列の MySQL 機能を使用して、1 つの列 (announce_id) のみでグループ化しますが、集計なしで他の列の束を取り込みます (他のすべては "*" によって取り込まれます)。

于 2012-08-22T02:04:40.550 に答える
2

ここでの目的が、現在のユーザーが読んだアナウンスを除外することである場合は、これをまったく異なる方法で行うことができます。すべてのアナウンスをクエリし、それらのアナウンスを読んだすべてのユーザーを見つけて、それらの結果を調べて、使用したアナウンスを見つけて表示されたリストからトリミングする代わりに、特定のすべてを一度にクエリすることができますユーザー(またはユーザーのリスト)はまだ読んでいません。

クエリを次のように変更します。

SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID = <INSERT USER ID HERE>)

これにより、この特定のユーザーがまだ確認していないすべてのアナウンス行が返されます。その最後のWHERE句をに変更するとWHERE User_ID IN ()、ユーザーIDのリストを指定できます。

編集:上記で投稿したコメントを前提として、このクエリを使用して、誰も読んでいないすべてのアナウンスを取得できます。

SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID IN (SELECT User_ID FROM User))

誰か(全員ではないにしても)が読んでいないアナウンスを見つけるためのクエリをまとめるためのロジックは、今すぐ私を逃れています。

2番目の編集:すべてのアナウンス、およびそれを読んだ人と読んでいない人はすべて、上記で使用した別の種類の結合を使用する必要がありますFULL OUTER JOIN。残念ながら、MySQLにはその機能IIRCはありませんが、ユニオンクエリでシミュレートできます。

SELECT A.*, ACK.*, U.* FROM Announcements AS A 
INNER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID 
LEFT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID 
WHERE U.User_ID IS NOT NULL
UNION ALL
SELECT A.*, ACK.*, U.* FROM Announcements AS A
LEFT OUTER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID
RIGHT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID

私はそれがそれをするべきだと思います。もちろん、現時点でテストする施設はありません。

于 2012-08-22T02:01:39.390 に答える