0

現在、ネストされた選択を使用している MySQL クエリがあり、ネストされた選択を使用しないようにクエリを書き直すことができるかどうか疑問に思っています。

クエリは次のとおりです

SELECT
  b.id,
  b.name,
  b.description,
  b.order,
  b.icon,
  b.locked,
  u.username     AS lastPoster,
  p.time         AS lastPostTime,
  p1.subject     AS lastPostTopicSubject,
  p2.postscount  AS totalPosts,
  t1.topicscount AS totalTopics,
  p.subject      AS lastPostSubject,
  t.id           AS lastPostTopicId
FROM      kf_boards                                AS b
LEFT JOIN kf_topics                                AS t  ON (t.boardid = b.id)
LEFT JOIN (SELECT posterid, topicid, time, subject
           FROM kf_posts
           ORDER BY time DESC)                     AS p  ON (p.topicid = t.id)
LEFT JOIN (SELECT subject
           FROM kf_posts
           ORDER BY time ASC)                      AS p1 ON (p.topicid = t.id)
LEFT JOIN (SELECT COUNT(id) AS postscount
           FROM kf_posts)                          AS p2 ON (p.topicid = t.id)
LEFT JOIN (SELECT COUNT(id) AS topicscount
           FROM kf_topics)                         AS t1 ON (t.boardid = b.id)
LEFT JOIN kf_users                                 AS u  ON (p.posterid = u.id)
WHERE b.categoryid = :catid
GROUP BY b.name
ORDER BY b.order

そして、データベース構造は次のとおりです

ここに画像の説明を入力

どんな助けでも大歓迎です!

ありがとう!

編集:クエリの下で試したところ、結果が返されました

ここに画像の説明を入力

結果は次のようになります

ここに画像の説明を入力

4

3 に答える 3

0

すべてのサブクエリを削除することは可能のようですが、最後の投稿と、対応するトピックの最初の投稿がサブクエリを使用して見つかった場合、クエリはより明確になります。

SELECT b.id, b.name, b.description, b.sortorder, b.icon, b.locked,
       u.username AS lastPoster,
       p1.time AS lastpostTime,
       p0.subject AS lastPostTopicSubject,
       COUNT(DISTINCT p.id) AS totalPosts,
       COUNT(DISTINCT t.id) AS totalTopics,
       p1.subject AS lastPostSubject,
       p1.topicid AS lastPostTopicId
FROM  kf_boards b
LEFT JOIN kf_topics t ON t.boardid = b.id
LEFT JOIN kf_posts p ON p.topicid = t.id
LEFT JOIN kf_posts p1
ON p1.time = (SELECT MAX(time) FROM kf_posts p
              INNER JOIN kf_topics t
              ON p.topicid = t.id
              WHERE t.boardid = b.id)
LEFT JOIN kf_users u ON u.id = p1.posterid
LEFT JOIN kf_posts p0
ON p0.time = (SELECT MIN(time) FROM kf_posts p0
              WHERE p0.topicid = p1.topicid)
WHERE b.categoryid = :catid
GROUP BY b.id
ORDER BY b.sortorder;

ただし、次のクエリでは、自己結合を使用して、関連する前の投稿やネストされた投稿がない投稿を検索すると、同じ答えが得られます。

SELECT b.id, b.name, b.description, b.sortorder, b.icon, b.locked,
       u.username AS lastPoster,
       lastpost.time AS lastpostTime,
       firstpost.subject AS lastPostTopicSubject,
       COUNT(DISTINCT p.id) AS totalPosts,
       COUNT(DISTINCT t.id) AS totalTopics,
       lastpost.subject AS lastPostSubject,
       lastpost.topicid AS lastPostTopicId
FROM  kf_boards b
LEFT JOIN kf_topics t ON t.boardid = b.id
LEFT JOIN kf_posts p ON p.topicid = t.id

LEFT JOIN kf_topics lasttopic ON lasttopic.boardid = b.id
LEFT JOIN kf_posts lastpost ON lastpost.topicid = lasttopic.id
LEFT JOIN kf_topics nexttopic ON nexttopic.boardid = b.id
LEFT JOIN kf_posts nextpost              -- order posts
ON nextpost.topicid = nexttopic.id       -- in same board
AND nextpost.time > lastpost.time        -- by time

LEFT JOIN kf_users u ON u.id = lastpost.posterid

LEFT JOIN kf_posts AS firstpost ON firstpost.topicid = lastpost.topicid
LEFT JOIN kf_posts prevpost              -- order posts
ON prevpost.topicid = firstpost.topicid  -- on same topic
AND prevpost.time < firstpost.time       -- by time

WHERE nextpost.id IS NULL                -- last post has no next
AND prevpost.id IS NULL                  -- first post on topic has no previous

AND b.categoryid = :catid
GROUP BY b.id
ORDER BY b.sortorder;

http://sqlfiddle.com/#!2/1c042/1/0で結果を確認してください

于 2012-11-17T23:19:40.010 に答える
0

これが役立つ解決策ですが、トリガーを介して後ですべてのクエリを簡素化するための追加の提案があります。それについては後で説明します。

カテゴリ (パラメーター) のボード ID と、ボードに (LEFT-JOIN 経由ではなく) 投稿があるという最も内側のクエリから始めます。これから、トピックに関係なく、ボードごとに最大の投稿 ID を取得しています (参加ごとに有効なボードでなければなりません)。

それができたら、次のクエリはトピックを決定するために最後の投稿に基づいて posts テーブルに再結合します...次に、同じトピック ID で再度投稿に THAT を再結合します。これで、ボードのこのトピックの最初の投稿 ID と合計エントリ数を取得できます...すべて「ボード ID」ごとにグループ化されます。

これらは明らかに、少なくとも 1 つのボードを持っているボードだけですが、それはあなたが望むものではありません。投稿に関係なく、すべてのボードが必要です。そのため、最初に戻って、カテゴリ ID = パラメータで同じ WHERE を使用して kf_boards を再度クエリします...これにより、カテゴリのすべてのボードが取得されます...

今、最小/最大投稿数とエントリ数の事前集計クエリに左結合できます...次に、それを使用して投稿テーブルに左結合しますが、2回...最初の投稿に対して1回(できるようになります最初の件名のキャプション、時間、その他気になるものをすべて取得します)、最後の投稿の AGAIN で IT の時間、件名などを取得します...事前集計クエリから、このトピックの合計投稿エントリが既にあります。最後に、users テーブルへの最後の投稿を左結合して、最後に投稿したユーザーを確認します。

私はその構文をテストしましたが、実際のデータに基づいて確認することはできません。

SELECT 
      B.ID,
      B.Name,
      B.Description,
      B.Order,
      B.Icon,
      FP.Subject as FirstPostSubject,
      FP.Time as FirstPostTime,
      LP.Subject as LastPostSubject,
      LP.Time as LastPostTime,
      U.UserName as LastPostUserName,
      QryPerBoard.PostEntries
   from 
      kf_boards B
         LEFT JOIN
              ( select 
                      PQ1.ID,
                      PQ1.LastPostID,
                      MIN( P2.ID ) as FirstPostID,
                      COUNT(*) as PostEntries
                   from 
                      ( SELECT
                              B1.ID,
                              MAX( P1.ID ) as LastPostID
                           from
                              kf_boards B1
                                 join kf_topics T1
                                    ON B1.ID = T1.BoardID
                                    join kf_posts P1
                                       ON T1.ID = P1.TopicID
                           where
                              B1.CategoryID = 1    <-- Insert your Category Parameter ID here
                           group by
                              B1.ID ) as PQ1
                      LEFT JOIN kf_posts P1
                         ON PQ1.LastPostID = P1.ID
                         LEFT JOIN kf_posts P2
                            ON P1.TopicID = P2.TopicID
                   group by
                      PQ1.ID ) QryPerBoard
            ON B.ID = QryPerBoard.ID
            LEFT JOIN kf_posts FP
               ON QryPerBoard.FirstPostID = FP.ID
            LEFT JOIN kf_posts LP
               ON QryPerBoard.LastPostID = LP.ID
               LEFT JOIN kf_users U
                  ON LP.PosterID = U.ID
   where
      B.CategoryID = 1    <-- Insert your Category Parameter ID here (second copy for parameter)

さて、特にウェブサイトの場合、再帰レベルのクエリを防ぐためにどのように調整するか. トリガーを使用します。POST が作成されて保存されたら、いくつかのことを行うトリガーを用意します...

トリガーの影響に関する修正された考え。

それぞれのボード ID に対して作成された最新のトピック ID で kf_Boards を更新します。これにより、後でそれを探す必要がなくなります。最後のものを取得して実行するだけです。さらに、TOPIC レコードを更新します。トピックの FIRST POST、LAST POST、および TOTAL POSTS の列を用意します。トピックへの最初の投稿の場合は、最初と最後の投稿の両方を新しい ID で更新し、合計投稿数を増やします。

これらのトリガーを組み込んで「余分な」列を更新する時間は、将来のクエリの複雑さを軽減します。基本的に次のようなことができます

select
      B.*,
      LP.Fields,  <obviously apply specific fields you want per table>
      FP.Fields,
      U.Fields
   from
      kf_boards B
         LEFT JOIN kf_topics T
            on B.LastTopicID = T.ID
            LEFT JOIN kf_posts FP
               on T.FirstPostID = FP.ID
            LEFT JOIN kf_posts LP
               on T.LastPostID = LP.ID
               LEFT JOIN kf_users U
                  on LP.PosterID = U.ID
   where
      B.CategoryID = 1  <-- your parameterID  
于 2012-11-18T00:11:50.687 に答える
0

試す:

SELECT 
   b.id, b.name, b.description, b.order, b.icon, b.locked, 
   u.username AS lastPoster, p.time AS lastPostTime, 
   p.subject AS lastPostSubject, t.id AS lastPostTopicId
FROM kf_boards AS b
LEFT JOIN kf_topics AS t ON t.boardid = b.id
LEFT JOIN kf_posts AS p ON p.topicid = t.id
LEFT JOIN kf_users AS u ON p.posterid = u.id
WHERE b.categoryid = :catid
GROUP BY b.name 
ORDER BY b.order ASC, p.time DESC

更新:以下は新しいクエリ用です。

SELECT b.id, b.name, b.description, b.order, b.icon, b.locked, 
    u.username AS lastPoster, MAX(p.time) AS lastPostTime, 
    p.subject AS lastPostTopicSubject, count(p.id) AS totalPosts, 
    count(t.id) AS totalTopics, p.subject AS lastPostSubject, 
    max(t.id) AS lastPostTopicId
FROM kf_boards AS b 
LEFT JOIN kf_topics AS t ON t.boardid = b.id
LEFT JOIN kf_posts AS p ON p.topicid = t.id
LEFT JOIN kf_users AS u ON p.posterid = u.id
WHERE b.categoryid = :catid
GROUP BY b.name, b.id, b.name, b.description, b.order, b.icon, 
    b.locked, u.username, p.subject
ORDER BY b.order
于 2012-11-17T04:31:48.533 に答える