-1

Twitterに投稿されたリンクを表示するTwitterアプリを構築していますが、テーブルを時間でソートするときに問題があります。

tweet
+----------------------------------------+
| tweet_id | [...] | created_at          |
+----------------------------------------+  
| 123456   | [...] | 2012-06-11 11:31:28 |
| 234567   | [...] | 2012-06-11 11:32:55 |
| 345678   | [...] | 2012-06-11 11:33:22 |
+----------------------------------------+

tweets_url
+---------------------+
| tweet_id | url      |
+---------------------+
| 123456   | cnn.com  |
| 123456   | fox.com  |
| 234567   | abc.com  |
| 345678   | abc.com  |
+---------------------+

Heres 私の SQL (一意の URL のみを返すために GROUP by を使用しています):

SELECT tweet_urls.url,
    FROM  `tweets` 
    LEFT JOIN tweet_urls ON tweet_urls.tweet_id = tweets.tweet_id 
    WHERE tweet_urls.url LIKE '%cnn.com%'
    GROUP BY tweet_urls.url 
    ORDER BY tweets.created_at DESC LIMIT 0 , 20

さまざまな結合と内部 SELECT を使用して、外部 select from hereでこのクエリを実行するさまざまなバリエーションを試しました。

編集:さらにテストを行いました。Mysql は GROUP BY tweet_urls.url に基づいて一時テーブルを作成し、一時テーブルで実行されるため、指定されたインデックスを使用せずに結果を並べ替えているようです。

EXPLAIN の出力は次のとおりです。

+----+-------------+------------+--------+---------------+---------+---------+-----+----------------------+----------------------------------------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref |                rows  | Extra                                        |
+---------------------------------------------------------------------------------------------------------+----------------------------------------------+
| 1  | SIMPLE      | tweet_urls | index  | tweet_id      | url     | 422     | NULL                 86783 | Using where; Using temporary; Using filesort 
| 1  | SIMPLE      | tweets     | eq_ref | PRIMARY       | PRIMARY | 8       | tweet_urls.tweet_id        |
+----+-------------+------------+--------+---------------+---------+---------+-----+----------------------+----------------------------------------------+
4

3 に答える 3

3

tweets.created_at列にインデックスを付けます

于 2012-06-18T18:34:35.437 に答える
0

ステートメントを調整する前に、ステートメントが正しい結果セット (予期している結果セット) を返すことが保証されていることを確認してください。(下記参照)

パフォーマンスに関しては、LIKE '%foo'述語 (ワイルドカードが先頭にある) はサージ可能ではありません。(つまり、クエリ エンジンはインデックスを使用して、チェックする行数を制限することはできません。クエリ エンジンは、テーブル内のすべての行をチェックする必要があります。

これは、JOIN 操作とともに、パフォーマンスの低下の主な原因であると思われます。(クエリに OUTER 結合が必要とは思われません。tweet_urls.url の述語を考えると、INNER 結合と同等のように見えます。

理想的には、先頭に % ワイルドカード文字は必要ありません。代わりに、先頭のワイルドカードなしで をチェックできます。これにより、クエリ エンジンが (列url LIKE 'cnn.com%'で) インデックスを使用する可能性があります。url

明らかに、テーブルとインデックスの定義を変更するとパフォーマンスが向上する可能性がありますが、それらの変更を行う自由がある場合に限られます。(多くの場合、あなたのような質問をしている投稿者は、変更できる内容が限られています。)

したがって、私はあなたが持っているクエリのみに対処しており、スキーマの変更を提案していません。(あなたのような要件に直面した場合、テーブルとインデックス、場合によっては FULLTEXT インデックスに変更を加えることを検討します。)

しかし、クエリの変更について尋ねられたので、クエリについてのみ説明します。


1 つの (必ずしも最新ではない) ツイートのcreated_atだけでなく、最新のツイートの順に並べられた個別の URL を返すことを意味しているように見えます。created_at

ORDER BYその場合、句に含まれていない句で非集計を参照していることを考えると、クエリが期待どおりの順序で行を返しているとは思いませんGROUP BY

注: 他のリレーショナル データベースは、このようなステートメントで例外をスローします。たとえば、Oracle は例外をスローORA-00979: not a GROUP BY expressionし、SQL Server は例外をスローしますMsg 8127 Column "tweets.created_at" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.

クエリが期待どおりの順序で行を返す場合、それは偶然によるものであり、動作が保証されているわけではありません。

LATEST ツイートの created_at で並べ替えられた行を取得するには、最大の created_at に基づいて並べ替えるように指定する別の形式のクエリが必要です。例として:

SELECT tweet_urls.url
  FROM tweet_urls
 WHERE tweet_urls.url LIKE '%cnn.com%'
 GROUP BY tweet_urls.url
 ORDER BY MAX((SELECT MAX(tweets.created_at) FROM tweets WHERE tweets.tweet_id = tweet_urls.tweet_id)) DESC LIMIT 0, 20

- また

SELECT t.url
  FROM ( SELECT tweet_urls.url, MAX(tweets.created_at) AS max_created_at
           FROM tweets 
           JOIN tweet_urls ON tweet_urls.tweet_id = tweets.tweet_id 
          WHERE tweet_urls.url LIKE '%cnn.com%'
          GROUP BY tweet_urls.url
       ) t
 ORDER BY t.max_created_at DESC LIMIT 0, 20

示したテスト ケースをセットアップします。

CREATE TABLE tweets (tweet_id INT UNSIGNED NOT NULL, created_at DATETIME) ENGINE=MyISAM; 
CREATE TABLE tweet_urls (tweet_id INT UNSIGNED NOT NULL, url VARCHAR(20) NOT NULL) ENGINE=MyISAM;
INSERT INTO tweets VALUES (123456, '2012-06-11 11:31:28'),(234567,'2012-06-11 11:32:55'),(345678,'2012-06-11 11:33:22');
INSERT INTO tweet_urls VALUES (123456,'cnn.com'),(123456,'fox.com'),(234567,'abc.com'),(345678,'abc.com');

さらに数行追加します。

INSERT INTO tweets VALUES (1, '2012-06-10'),(2,'2012-06-12'); 
INSERT INTO tweet_urls VALUES (1,'Xcnn.com'),(2,'Xcnn.com');

クエリを実行すると、SOME created_at の順に行が返されますが、必ずしも LATEST ツイートの created_at であるとは限りません。

于 2012-06-18T22:42:14.543 に答える