-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  |
+---------------------+

这是我的 SQL(我使用 GROUP by 仅返回唯一的 URL):

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

我尝试了使用不同的连接和内部 SELECTS从此处使用外部选择运行此查询的不同变体。

编辑:我做了一些进一步的测试。似乎 Mysql 基于 GROUP BY tweet_urls.url 创建了一个临时表,然后使用指定的索引对结果进行排序,因为它是在临时表上运行的。

这是解释输出:

+----+-------------+------------+--------+---------------+---------+---------+-----+----------------------+----------------------------------------------+
| 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

7

我认为真正的问题在这里:

WHERE tweet_urls.url LIKE '%cnn.com%'

这种类型的查询(LIKE没有常量前缀)不能有效地使用索引。

您可以通过在表中添加一个额外的列来解决这个问题,调用domain并索引它。然后,您可以将查询更改为:

WHERE tweet_urls.domain = 'cnn.com'
于 2012-06-18T18:38:16.847 回答
3

tweets.created_at在列上放置索引

于 2012-06-18T18:34:35.437 回答
0

在调整语句之前,请确保语句保证返回正确的结果集,即您期望的结果集。(见下文)

至于性能,LIKE '%foo'谓词(带有前导通配符)是不可预测的。(也就是说,查询引擎不能使用索引来限制要检查的行数。查询引擎将需要检查表中的每一行。

我怀疑这与 JOIN 操作一起可能是导致性能下降的主要原因。(我没有看到您的查询中需要 OUTER 连接,它似乎等同于 INNER 连接,给定 tweet_urls.url 上的谓词。

理想情况下,您不需要在开头使用 % 通配符,而是可以检查url LIKE 'cnn.com%',而不使用前导通配符,这有可能让查询引擎使用索引(在url列上)。

显然,对表和索引定义的一些更改可能有助于提高性能,但前提是您可以自由地进行这些更改。(通常,像你这样提问的发帖人在他们能够做出的改变方面受到限制。)

所以,我只处理您的查询,而不是建议任何架构更改。(如果我遇到像您这样的要求,我会考虑更改表和索引,甚至可能是 FULLTEXT 索引。)

但是您询问了有关更改查询的问题,因此我只解决该查询。


看起来好像您的意思是返回不同的 url,按created_atLATEST 推文的排序,而不仅仅是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.MySQL 更自由,这并不总是一件好事。

如果您的查询以您期望的顺序返回行,那是偶然的,而不是由于某些有保证的行为。

要获取按 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 回答