3

私は記事テーブルとカテゴリテーブルを持っています。カテゴリごとに 7 つの記事を取得したいと考えています。現在、私はこれを持っていますが、大きなテーブルでは非常に遅いため、実際には解決策ではありません:

SELECT id, 
       title, 
       categories_id, 
       body, 
       DATE_FORMAT(pubdate, "%d/%m/%y %H:%i") as pubdate   
FROM articles AS t 
WHERE ( 
    SELECT COUNT(*) 
    FROM articles 
    WHERE t.categories_id = categories_id 
      AND id< t.id AND publish = 1 
      AND expires > '2008-12-14 18:38:02' 
      AND pubdate <= '2008-12-14 18:38:02' 
    ) < 7 
ORDER BY categories_id DESC

Explain を使用すると、結合タイプ ALL & REF を実行していることがわかります。選択タイプは PRIMARY および DEPENDENT SUBQUERY です。

より良い解決策はありますか?

4

6 に答える 6

5

この問題を解決する方法は次のとおりです。

SELECT a1.id, 
       a1.title, 
       a1.categories_id, 
       a1.body, 
       DATE_FORMAT(a1.pubdate, "%d/%m/%y %H:%i") as pubdate  
FROM articles AS a1
  LEFT OUTER JOIN articles AS a2
  ON (a1.categories_id = a2.categories_id AND 
     (a1.pubdate < a2.pubdate OR (a1.pubdate = a2.pubdate AND a1.id < a2.id)))
GROUP BY a1.id
HAVING COUNT(*) < 7;

通常、相関サブクエリはパフォーマンスが低いため、この手法では代わりに結合を使用します。

特定の記事について、検討中の現在の記事 (a1) のカテゴリに一致し、日付がより新しい (id同点の場合はそれ以降の) 記事 (a2) を検索します。その基準を満たす記事が 7 つ未満の場合、現在の記事はそのカテゴリの最新のものである必要があります。

idと同じ並べ替え順序を持つ一意の列に依存できる場合は、一意の列pubdateに関係がないため、結合を簡素化できます。

  ON (a1.categories_id = a2.categories_id AND a1.id < a2.id)
于 2008-12-14T22:52:05.207 に答える
2
  1. テーブルの大きさはどれくらいですか? 遅いとはどのくらい遅いですか?

  2. テーブルにはどのようなインデックスがありますか?

  3. EXPLAIN からの全体的な情報は何ですか?

また、2 つの datetime 値は明示的であるため、これは、他の情報からそれを構成する何かによって生成されたコードから生成されているように見えます。リストのループ内でこれを実行している、何らかの種類の別の SQL クエリはありますか?

どの 7 つの記事が選択されているかは明らかではありませんが、最新のものですか? どの日付までに?

于 2008-12-14T20:30:51.437 に答える
1

そのため、記事が 7 つ未満のカテゴリを求めているようです。ここからクエリを開始します --

SELECT categories_id,  COUNT(1)  
FROM articles  
WHERE publish = 1 
  AND expires > '2008-12-14 18:38:02' 
  AND pubdate <= '2008-12-14 18:38:02'
GROUP BY categories_id
HAVING COUNT(1) < 7

次に、次のサブクエリを作成します。

SELECT 
    c.id, c.title, c.id, a.body,  
    DATEFORMAT(a.pubdate, "%d/%m/%y %H:%i") as pubdate  
FROM categories c  
JOIN articles a ON c.id = a.categories_id  
JOIN 
(  
    SELECT DISTINCT categories_id  
    FROM articles  
    WHERE publish = 1  
        AND expires > '2008-12-14 18:38:02'  
        AND pubdate <= '2008-12-14 18:38:02'  
    GROUP BY categories_id  
    HAVING COUNT(1) <= 7  
) AS j ON c.id = j.categories_id  
ORDER BY whatever  

次のステップは、返される記事の数を 7 に制限することです。これが適切であると思われる場合は、次に対処できます。(そのまま試して、EXPLAIN がどのように見えるかを確認してください。)

編集: "< 7" を <= 7" に変更

于 2008-12-14T21:32:18.083 に答える
0

テストでは、Limit 7 が MySQL のサブクエリ内で機能しないことがわかりました。Bill の提案を参照してください。私はそれがうまく機能することを確認しました。

SELECT id, 
       title, 
       categories_id, 
       body, 
       DATE_FORMAT(pubdate, "%d/%m/%y %H:%i") as pubdate   
FROM articles A INNER JOIN articles B ON B.categories_ID = A.Categories_ID
WHERE A.ID IN ( 
    SELECT ID
    FROM Articles  
    WHERE categories_id = A.categories_id 
      AND publish = 1 
      AND expires > '2008-12-14 18:38:02' 
      AND pubdate <= '2008-12-14 18:38:02' 
    LIMIT 7
    ORDER BY Categories_ID DESC) 
ORDER BY B.Categories_ID DESC
于 2008-12-14T20:36:40.613 に答える
0

Bill のクエリは、平均するともう少しうまく機能するかもしれませんが、1 回の実行で 230 秒かかりました。完全なテスト (数回の実行) は行いませんでしたが、それでも遅すぎるため、カテゴリごとに 1 つのクエリを実行して最新の 7 項目をフェッチする方がよいと思います。これは、他のすべてのオプションよりも高速になるようです。

于 2008-12-15T02:33:24.543 に答える
0

いくつかのオプションがあります。パフォーマンスの問題が発生するものもありますが、多くの要因によって異なります。

いくつかのクエリに分割できます。すべてのカテゴリを読み取る 1 つのクエリ:

SELECT categories_id FROM Categories

次に、カテゴリごとに上位 7 つの記事を読み上げます。

SELECT 
  id, 
  title, 
  ...etc.
FROM articles
where categories_id = 1 

... など、カテゴリごとに。

これには、理解が少し簡単になるという利点がありますが、欠点は、1 つのクエリが 1 + (1* 猫の数) になることです。繰り返しますが、カテゴリの数に制限を設けて、ある程度の制御要素を持たせることができます. 1 つの複雑なクエリよりも 5 つの単純なクエリの方がはるかに優れたパフォーマンスを発揮する場合があります。

この種のことは、あなたが制御できる何らかのコードから SQL を呼び出していることを前提としています - これは事実ですか?

于 2008-12-14T20:21:49.457 に答える