【问题标题】:Fetching a Single Row from Join Table从连接表中获取单行
【发布时间】:2011-08-24 09:35:03
【问题描述】:

这是我的桌子:

CREATE TABLE `articles` (
    `id` int(10) unsigned not null auto_increment,
    `author_id` int(10) unsigned not null,
    `date_created` datetime not null,
    PRIMARY KEY(id)
) ENGINE=InnoDB;

CREATE TABLE `article_contents` (
    `article_id` int(10) unsigned not null,
    `title` varchar(100) not null,
    `content` text not null,
    PRIMARY KEY(article_id)
) ENGINE=InnoDB;

CREATE TABLE `article_images` (
    `article_id` int(10) unsigned not null,
    `filename` varchar(100) not null,
    `date_added` datetime not null,
    UNIQUE INDEX(article_id, filename)
) ENGINE=InnoDB;

每篇文章都可以有一张或多张与之关联的图片。我想在一个页面上显示最近 40 篇书面文章,以及与该文章相关的最新图像。我想不通的是如何加入 article_images 表,并且只检索一行。

编辑:解决方案表现良好很重要。到目前为止,我看到的解决方案(使用派生表)需要一分钟或更长时间才能完成。

【问题讨论】:

  • 为什么有单独的articlesarticle_contents 表?这似乎是一对一的关系,也就是一个记录。为什么不将titlecontent 移动到articles 表?如果需要支持空文章,则为titlecontent创建默认值''
  • 您需要一个更好的索引方案以及一个在最深子查询中只有 40 行的查询。我的回答提供了两者。我必须使用存储过程实际生成自己的示例数据。
  • 我刚刚在查询中添加了 date_added 字段,以显示每篇文章的最新图像的 date_added。查询和输出在底部。
  • @Bacon Bits - 出于性能原因,我已经分解了表格,尽管我对 MySQL 的查询缓存以及操作系统的缓存如何工作的想法可能不正确。这个想法是将 WHERE 子句中使用的所有列以及 GROUP BY 和 ORDER BY 子句中使用的列都保留在一个只包含数字和日期时间列的小表中。希望这张表足够小以完全放入内存,而更大的 article_contents 表可以放在磁盘上。
  • 您的问题引起了某种轰动。这个问题提出了一个值得这样的挑战。即使您接受了我的回答,仍然提交了另一个答案。我想查询速度和表设计是开发人员应该看到的,那些有足够热情去改进的人肯定会尝试。就您的问题 +1 鼓励开发人员按照这些思路思考!!!

标签: mysql


【解决方案1】:

让我首先描述一下查询解决这个问题所花费的时间的原因。我的英语不太好,但我会尽量用最好的方式解释它。

我们以2表连接为例,当我们使用join table命令时,基本上做了什么。根据查询,引擎扫描第一个表,然后它转到第二个表并开始读取第一个表和第二个表,查询匹配的地方,它会占用内存。现在让我们以 3 个表连接为例,读取第 1 个表,然后 2 秒并在第 3 个 sql 上执行,如果数据很重,则创建 tmp 表并开始读取然后执行命令,如果正常则存储在内存中。

现在您的问题不在于表格加入命令,而在于第二个表格的结构。它有文本列,使用最少,在搜索过程中,引擎必须选择该查询并对其执行读写功能,每次写入或读取时,内存的使用都会减少,引擎变得越来越慢。

解决方案:这个问题有两种解决方案。第一个解决方案是对第二个表进行水平和垂直分区。他的引擎会在搜索过程中进行标记,并且会快速写入大量数据,并且计时结果会增加。但我不推荐该解决方案,因为它只会减少时间,但不会减少阅读和写作的时间。我会建议第二个解决方案。

解决方法二:去掉第二个表的text列,将author_aritcle_content的第四个表的title和content列为2列,最后加入第四个表。这个解决方案会发生什么,引擎首先用 10 + 10 字节扫描第一个表,当我们移动到第二个表时,扫描将在 10 + 10 + 10 + 100 中完成,第三个表将是 10 + 10 + 10 + 100 + 10 + 100,此时引擎只需搜索 240 个字节,您的结果就准备好了,引擎只需添加内容,这很容易完成。查询的时间减少非常大,因为它不必像最后一个加入引擎那样使用带有引擎的文本列,只有将要显示的结果。

今天,我在表中添加了 10,000 篇文章并执行了以下查询,耗时 0.2015 秒,每篇文章有 65,000 字节,图片名称为 10 字节,查询为:

select 
    a.id,
    a.author_id,
    a.date_created,
    b.title,
    c.filename,
    c.date_added,
    d.content
from
    articles as a,
    article_contents as b,
    article_images as c,
    article_contents as d
where
    a.id = b.article_id and
    b.article_id = c.article_id and
    c.article_id = d.article_id
order by
    a.date_created desc
limit
    0, 40;

我将再次发布结果,文章数量达到 50,000 并且执行查询所需的时间。

【讨论】:

  • 我已经阅读了您的回答,我认为您提到的优化从磁盘空间的角度来看非常棒,并且可以使基表尽可能小。但是,@mellowsoon 只希望连接三个表的 40 行,并尽可能快地完成。在我的解决方案中,我做了 3 件事来解决原始问题:1)我为两个表添加了一个额外的索引,2)我只是强制第一个临时表从文章表中恰好有 40 行,以及 3)延迟加入桌子直到最后。事实上,随着数据负载的增加,我的答案的运行时间为 O(log n)。
  • 为您的答案+1。在其他提交的答案中,人们只是提出疑问并希望它是正确的。开发人员应该更多地考虑查询执行计划、表设计和内存注意事项,就像您在回答中所做的那样!!!
  • 感谢您的赞赏,您通过命令提供的答案已经足够好,但我希望您重新制作命令,只需从您的命令中删除文本列,输出结果时间是多少数据库很容易获得,这使您可以轻松地做到这一点,如果您可以在最后一次连接中添加文本列,那么时间。只是,我想知道时间变化,这也有助于我思考这些之间的任何其他选择。通过重新搜索并快速关注帖子,您也做得非常好。
  • 我只是再次考虑您的答案,因为我雇主的网络托管公司的客户在 MySQL 主服务器上并行运行 5-10 个查询,但它通过复制序列化到从服务器。言归正传,奴隶轻松落后四万秒,差不多半天。减少表大小,或者至少减少临时表大小,将极大地帮助这个客户并减少他的内存消耗。我只需要说服那里的开发人员也实施这个想法。再次感谢您跳出框框思考。
  • @RolandoMySQLDBA 如果可以,请执行上述查询并提供上述查询时间的结果。查询没有任何 JOIN 条件。
【解决方案2】:

如果您确定每篇文章都有图片,请尝试此操作

SELECT a.author_id,a.date_created,i.filename FROM test.articles a
left join article_images i on i.article_id=a.id
where i.date_added =(select Max(date_added) from article_images i2);

否则,如果不需要与每篇文章相关的图像(这更合乎逻辑),这应该可以:

SELECT a.author_id,a.date_created,i.filename FROM test.articles a
left join article_images i on (i.article_id=a.id and i.date_added =(select Max(date_added) from article_images i2))

【讨论】:

    【解决方案3】:

    查看其他答案后,它帮助我意识到了一个潜在的问题。

    articles 表和 article_images 表都需要有一个额外的索引。

    这就是你现在拥有的:

    CREATE TABLE `articles` (
        `id` int(10) unsigned not null auto_increment,
        `author_id` int(10) unsigned not null,
        `date_created` datetime not null,
        PRIMARY KEY(id)
    ) ENGINE=InnoDB;
    
    CREATE TABLE `article_images` (
        `article_id` int(10) unsigned not null,
        `filename` varchar(100) not null,
        `date_added` datetime not null,
        UNIQUE INDEX(article_id, filename)
    ) ENGINE=InnoDB;
    

    这是你需要的:

    CREATE TABLE `articles` (
        `id` int(10) unsigned not null auto_increment,
        `author_id` int(10) unsigned not null,
        `date_created` datetime not null,
        PRIMARY KEY(id),
        INDEX (date_created DESC)
    ) ENGINE=InnoDB;
    
    CREATE TABLE `article_images` (
        `article_id` int(10) unsigned not null,
        `filename` varchar(100) not null,
        `date_added` datetime not null,
        UNIQUE INDEX (article_id, filename),
        INDEX (article_id, date_added),
    ) ENGINE=InnoDB;
    

    文章
    按插入日期降序排列文章的新索引

    文章图片
    仍然需要第一个索引,因为它可以防止将具有相同文件名的另一个图像附加到文章中。
    第二个索引将使查找最新图像变得简单,

    有了这些新索引,下面的查询将满足您的需求:

      SELECT
        AAA.author_id,
        AAA.date_created,
        IFNULL(BBB.title,'<NO_TITLE>') title,
        IFNULL(CCC.filename,'<NO-IMAGE>') filename
      FROM
      (
        SELECT
          AA.id,
          AA.date_added,
          BB.author_id,
          BB.date_created
        FROM
        (
          SELECT
            A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
            FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
            LEFT JOIN article_images B ON A.id = B.article_id
            GROUP BY A.id
        ) AA
        INNER JOIN articles BB USING (id)
      ) AAA
      LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
      LEFT JOIN article_images CCC
      ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
      ORDER BY AAA.date_created DESC;
    

    目标是首先创建一个只有 40 行的内部子查询。这就是AA应该带回来的。子查询 AAA 应该包含文章信息以及最后一张图片的插入日期。最后一组连接应该将 40 个 ID 与标题和图像连接起来。最后一步是以降序呈现结果集。

    由于我没有示例数据,我在一个名为 stuff 的示例数据库中编写了三个存储过程

    1 制作表格

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS `stuff`.`MakeTables` $$
    CREATE PROCEDURE `stuff`.`MakeTables` ()
    BEGIN
    
    DROP TABLE IF EXISTS articles;
    DROP TABLE IF EXISTS article_contents;
    DROP TABLE IF EXISTS article_images;
    
    CREATE TABLE `articles` (
        `id` int(10) unsigned not null auto_increment,
        `author_id` int(10) unsigned not null,
        `date_created` datetime not null,
        PRIMARY KEY(id),
        INDEX (date_created DESC)
    ) ENGINE=InnoDB;
    
    CREATE TABLE `article_contents` (
        `article_id` int(10) unsigned not null,
        `title` varchar(100) not null,
        `content` text not null,
    PRIMARY KEY(article_id)) ENGINE=InnoDB;
    
    CREATE TABLE `article_images` (
        `article_id` int(10) unsigned not null,
        `filename` varchar(100) not null,
        `date_added` datetime not null,
        UNIQUE INDEX (article_id, filename),
        INDEX (article_id, date_added)
    ) ENGINE=InnoDB;
    
    END $$
    
    DELIMITER ;
    

    1 制作数据

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS `stuff`.`LoadSampleData` $$
    CREATE PROCEDURE `stuff`.`LoadSampleData` ()
    BEGIN
    
        DECLARE x,y,z INT;
    
        SET x = 1;
        WHILE x <= 100 DO
            INSERT INTO articles (author_id,date_created) VALUES
            (RAND() * POWER(2,31),
            DATE('1970-01-01 00:00:00') + INTERVAL (RAND() * POWER(2,30)) SECOND);
            SET x = x + 1;
        END WHILE;
    
        SET x = 1;
        WHILE x <= 100 DO
            SET y = FLOOR(RAND() * 100);
            IF y >= 30 THEN
                INSERT INTO article_contents
                VALUES (x,
                CONCAT('TITLE_',FLOOR(RAND() * POWER(2,31))),
                CONCAT('CONTENT_',FLOOR(RAND() * POWER(2,31))));
            END IF;
            SET x = x + 1;
        END WHILE;
    
        SET x = 1;
        WHILE x <= 100 DO
            SELECT COUNT(1) INTO y FROM article_contents WHERE article_id = x;
            IF y = 1 THEN
                SET y = 0;
                WHILE y < 20 DO
                    SET y = y + 1;
                    SET z = FLOOR(RAND() * 10);
                    IF z >= 5 THEN
                        INSERT INTO article_images
                        VALUES (x,
                        CONCAT('IMAGE_',FLOOR(RAND() * POWER(2,31))),
                        DATE('1970-01-01 00:00:00') + INTERVAL (RAND() * POWER(2,30)) SECOND);
                    END IF;
                END WHILE;
            END IF;
            SET x = x + 1;
        END WHILE;
    
        SELECT COUNT(1) INTO x FROM articles;
        SELECT COUNT(1) INTO y FROM article_contents;
        SELECT COUNT(1) INTO z FROM article_images;
    
        SELECT CONCAT('Articles:',x,' Titles:',y,' Images: ',z) Results;
    
    END $$
    
    DELIMITER ;
    

    1 运行查询

    DELIMITER $$
    
    DROP PROCEDURE IF EXISTS `stuff`.`ShowLast40` $$
    CREATE PROCEDURE `stuff`.`ShowLast40` ()
    BEGIN
    
      SELECT
        AAA.author_id,
        AAA.date_created,
        IFNULL(BBB.title,'<NO_TITLE>') title,
        IFNULL(CCC.filename,'<NO-IMAGE>') filename
      FROM
      (
        SELECT
          AA.id,
          AA.date_added,
          BB.author_id,
          BB.date_created
        FROM
        (
          SELECT
            A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
            FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
            LEFT JOIN article_images B ON A.id = B.article_id
            GROUP BY A.id
        ) AA
        INNER JOIN articles BB USING (id)
      ) AAA
      LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
      LEFT JOIN article_images CCC
      ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
      ORDER BY AAA.date_created DESC;
    
    END $$
    
    DELIMITER ;
    

    这是我在 Windows 桌面 (MySQL 5.5.12) 上运行时的示例输出:

    mysql> call maketables; call loadsampledata;
    Query OK, 0 rows affected (0.31 sec)
    
    +------------------------------------+
    | Results                            |
    +------------------------------------+
    | Articles:100 Titles:67 Images: 666 |
    +------------------------------------+
    1 row in set (23.73 sec)
    
    mysql> call showlast40;
    +------------+---------------------+------------------+------------------+
    | author_id  | date_created        | title            | filename         |
    +------------+---------------------+------------------+------------------+
    | 1576679519 | 2004-01-02 14:05:17 | TITLE_1757853515 | IMAGE_1601858163 |
    |  992840519 | 2003-05-17 05:48:11 | TITLE_319026350  | IMAGE_1913708631 |
    | 1366977197 | 2003-03-20 19:37:23 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1705517382 | 2003-02-07 16:48:56 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1529899841 | 2002-11-02 20:59:26 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1756824942 | 2002-08-28 16:01:59 | <NO_TITLE>       | <NO-IMAGE>       |
    |  175825630 | 2002-05-08 13:48:56 | TITLE_240812804  | IMAGE_1214850809 |
    |  757530551 | 2002-02-08 00:20:17 | TITLE_4447486    | IMAGE_1511850161 |
    |  840251261 | 2002-01-25 20:06:56 | TITLE_1160842143 | IMAGE_206027488  |
    |  964653347 | 2001-12-26 19:15:47 | TITLE_1552408257 | IMAGE_717719932  |
    | 2106039126 | 2001-11-11 17:09:29 | <NO_TITLE>       | <NO-IMAGE>       |
    | 2085991608 | 2001-08-01 12:48:20 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1895462094 | 2000-12-02 05:31:41 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1589384729 | 2000-04-28 23:55:50 | TITLE_1040850308 | IMAGE_1200414639 |
    |  514341550 | 2000-04-20 07:25:05 | TITLE_188288840  | IMAGE_164856430  |
    |  887359583 | 2000-02-13 03:30:47 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1156687499 | 1999-06-16 00:29:17 | TITLE_686398996  | IMAGE_670200418  |
    | 1561242593 | 1998-12-08 05:50:17 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1117889993 | 1998-10-23 17:02:44 | TITLE_1491217221 | IMAGE_649630126  |
    |  740063135 | 1998-09-16 23:52:41 | TITLE_579374776  | IMAGE_757313192  |
    |  429699232 | 1998-04-19 01:41:17 | TITLE_73748980   | IMAGE_1881818111 |
    | 1827051060 | 1998-02-27 01:01:50 | TITLE_1588619991 | IMAGE_1657322715 |
    | 1442984429 | 1997-11-19 21:23:35 | TITLE_184173382  | IMAGE_597809368  |
    |  152267158 | 1997-05-02 20:25:50 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1323598169 | 1997-03-14 16:30:38 | TITLE_1355869397 | IMAGE_1058313818 |
    |   66122740 | 1997-01-05 15:12:20 | TITLE_1259073183 | IMAGE_198280936  |
    |    5161474 | 1996-06-28 10:47:26 | TITLE_1876022823 | IMAGE_1138098675 |
    | 1865082792 | 1996-03-01 19:09:11 | TITLE_1288151615 | IMAGE_245974646  |
    | 1923481146 | 1995-08-07 00:36:11 | TITLE_922744000  | IMAGE_2067090321 |
    | 1725218958 | 1995-03-18 05:15:29 | TITLE_583120586  | IMAGE_592773824  |
    |  117806248 | 1995-01-05 02:34:32 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1428777335 | 1993-06-06 01:52:32 | TITLE_661148588  | IMAGE_633345518  |
    | 1091245943 | 1993-06-05 05:51:47 | TITLE_1407444563 | IMAGE_538936256  |
    | 2088382260 | 1993-03-25 06:03:29 | TITLE_1144364681 | IMAGE_1790013089 |
    |  625878569 | 1992-12-21 07:41:26 | TITLE_1319355723 | IMAGE_921580624  |
    |  110555110 | 1992-01-01 20:49:59 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1110532475 | 1991-11-20 07:19:32 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1726795146 | 1990-10-09 00:23:44 | TITLE_782624350  | IMAGE_1760322575 |
    |  370183888 | 1990-03-30 15:59:17 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1497483317 | 1990-02-19 01:25:41 | TITLE_776483948  | IMAGE_665824222  |
    +------------+---------------------+------------------+------------------+
    40 rows in set (0.00 sec)
    

    试试看!!!

    更新

    我绝对确定您正在阅读的 40 个日期时间实际上是前 40 个。它们是。我运行了查询: select * from article ORDER BY date_created DESC;以确保。

    更新 2011-05-17 14:06

    mysql> call maketables; call loadsampledata;
    Query OK, 0 rows affected (0.45 sec)
    
    +-------------------------------------+
    | Results                             |
    +-------------------------------------+
    | Articles:100 Titles:67 Images: 6739 |
    +-------------------------------------+
    1 row in set (3 min 45.45 sec)
    
    Query OK, 0 rows affected (3 min 45.45 sec)
    
    mysql> call showlast40;
    +------------+---------------------+------------------+------------------+
    | author_id  | date_created        | title            | filename         |
    +------------+---------------------+------------------+------------------+
    |  196582776 | 2004-01-05 14:09:04 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1880371016 | 2003-07-31 05:50:37 | TITLE_1191518827 | IMAGE_1562208019 |
    |   22204986 | 2003-02-16 14:09:22 | <NO_TITLE>       | <NO-IMAGE>       |
    |  355490160 | 2002-11-21 02:35:19 | <NO_TITLE>       | <NO-IMAGE>       |
    |  869510149 | 2001-12-27 22:07:52 | TITLE_1528616779 | IMAGE_223327284  |
    | 2063556512 | 2001-04-16 18:47:46 | TITLE_1839975091 | IMAGE_1282187005 |
    |  529754190 | 2000-07-14 19:44:01 | TITLE_1557423205 | IMAGE_1931606737 |
    |  166226262 | 1999-11-08 03:27:22 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1981417562 | 1999-09-11 12:59:10 | TITLE_198262896  | IMAGE_1491273871 |
    |  831057001 | 1999-06-14 15:06:31 | TITLE_1170272131 | IMAGE_760396200  |
    | 1454252623 | 1998-06-02 08:35:46 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1435450777 | 1997-11-17 18:10:34 | TITLE_482497458  | IMAGE_1331932705 |
    | 1536315541 | 1997-11-02 05:24:49 | <NO_TITLE>       | <NO-IMAGE>       |
    | 2078028530 | 1997-03-14 22:36:58 | TITLE_321332010  | IMAGE_1897983295 |
    |  701651581 | 1997-01-13 22:36:58 | TITLE_1337390701 | IMAGE_1630983859 |
    |  101442444 | 1996-11-22 09:40:16 | <NO_TITLE>       | <NO-IMAGE>       |
    |   51114930 | 1996-11-20 03:24:49 | TITLE_1866751135 | IMAGE_1669595407 |
    |  722056183 | 1996-08-03 15:23:01 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1178720989 | 1996-06-29 22:47:19 | TITLE_579734376  | IMAGE_833229222  |
    |  511355958 | 1996-03-10 09:32:46 | TITLE_1540275289 | IMAGE_1168117261 |
    |  831921829 | 1996-01-31 06:36:04 | TITLE_661038882  | IMAGE_1199197195 |
    | 1288455163 | 1995-08-22 00:34:25 | TITLE_1599332515 | IMAGE_822445764  |
    | 1976208956 | 1995-06-02 09:23:01 | TITLE_58372998   | IMAGE_793318650  |
    | 2092066982 | 1995-03-28 20:35:37 | TITLE_1693142377 | IMAGE_1176935479 |
    | 1083841549 | 1994-07-11 18:46:52 | TITLE_1955674591 | IMAGE_1240485919 |
    |  359037132 | 1994-07-11 02:44:19 | TITLE_713454936  | IMAGE_1072569732 |
    | 1471985773 | 1994-05-10 17:08:01 | TITLE_1065017724 | IMAGE_393097704  |
    | 1888864730 | 1994-01-15 17:41:28 | TITLE_1060275498 | IMAGE_230810100  |
    | 1688028488 | 1993-12-28 06:36:58 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1739777948 | 1993-02-15 00:30:31 | TITLE_1226842225 | IMAGE_1615058467 |
    |  445721334 | 1991-12-15 20:54:49 | TITLE_1336145587 | IMAGE_2114729323 |
    | 1661002442 | 1991-06-30 05:49:34 | TITLE_151142910  | IMAGE_1623325381 |
    | 2092223006 | 1991-06-13 13:15:58 | TITLE_33175860   | IMAGE_1225117771 |
    | 1553434585 | 1991-01-12 03:34:25 | TITLE_728483442  | IMAGE_1954153339 |
    |  528544608 | 1990-11-10 08:21:04 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1043927395 | 1990-10-05 00:48:49 | TITLE_304307448  | IMAGE_1702062493 |
    | 1685702960 | 1990-04-28 05:44:19 | TITLE_1909853341 | IMAGE_263553036  |
    | 1392428383 | 1990-03-07 15:08:46 | <NO_TITLE>       | <NO-IMAGE>       |
    |  643714153 | 1990-02-14 08:32:10 | TITLE_837416724  | IMAGE_1673964259 |
    | 2132028206 | 1989-09-28 16:04:07 | TITLE_614908878  | IMAGE_1362210487 |
    +------------+---------------------+------------------+------------------+
    40 rows in set (0.01 sec)
    

    即使使用更大的图像集,所有内容都与我之前发布的一样。这是 ShowLast40 存储过程中的查询:

      SELECT
        AAA.author_id,
        AAA.date_created,
        IFNULL(BBB.title,'<NO_TITLE>') title,
        IFNULL(CCC.filename,'<NO-IMAGE>') filename
      FROM
      (
        SELECT
          AA.id,
          AA.date_added,
          BB.author_id,
          BB.date_created
        FROM
        (
          SELECT
            A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
            FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
            LEFT JOIN article_images B ON A.id = B.article_id
            GROUP BY A.id
        ) AA
        INNER JOIN articles BB USING (id)
      ) AAA
      LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
      LEFT JOIN article_images CCC
      ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
      ORDER BY AAA.date_created DESC;
    

    更新 2011-05-17 15:16

    只是为了一笑而过,我增加了样本并重新运行它。查询速度相同。

    mysql> call maketables; call loadsampledata; call showlast40;
    Query OK, 0 rows affected (0.38 sec)
    
    +----------------------------------------+
    | Results                                |
    +----------------------------------------+
    | Articles:1000 Titles:886 Images: 88596 |
    +----------------------------------------+
    1 row in set (51 min 22.29 sec)
    
    Query OK, 0 rows affected (51 min 22.29 sec)
    
    +------------+---------------------+------------------+------------------+
    | author_id  | date_created        | title            | filename         |
    +------------+---------------------+------------------+------------------+
    | 1464539515 | 2004-01-07 22:45:04 | TITLE_676010724  | IMAGE_1877060293 |
    |  272558724 | 2004-01-03 23:44:19 | TITLE_1213504045 | IMAGE_550812606  |
    | 2000476448 | 2003-12-28 18:05:10 | TITLE_1762951489 | IMAGE_1201290847 |
    |  955209697 | 2003-12-25 00:55:43 | TITLE_1064749344 | IMAGE_1335865507 |
    | 1657429856 | 2003-12-19 01:03:13 | TITLE_1931852743 | IMAGE_905288424  |
    |  759381001 | 2003-11-12 10:46:52 | TITLE_878255772  | IMAGE_2014780795 |
    | 1269478951 | 2003-11-06 02:06:22 | TITLE_2026098781 | IMAGE_982272966  |
    | 1049672131 | 2003-10-04 20:55:34 | TITLE_2043080215 | IMAGE_987859662  |
    | 1429108729 | 2003-09-16 19:07:52 | TITLE_424483080  | IMAGE_35379150   |
    | 1672198676 | 2003-09-13 11:49:52 | TITLE_1131552745 | IMAGE_875049630  |
    | 1645878842 | 2003-08-24 13:42:04 | TITLE_1077302833 | IMAGE_702269538  |
    |  172347180 | 2003-08-21 14:26:37 | TITLE_558691044  | IMAGE_1091183587 |
    | 1137674509 | 2003-08-15 08:44:37 | TITLE_1982979709 | IMAGE_1234487941 |
    |  282998112 | 2003-08-05 10:01:34 | TITLE_353831568  | IMAGE_738487608  |
    |  246145344 | 2003-08-02 00:42:31 | TITLE_376954044  | IMAGE_1279375459 |
    |  218409162 | 2003-07-14 02:55:16 | TITLE_1932540991 | IMAGE_1078689211 |
    |  593263087 | 2003-07-12 22:47:01 | TITLE_1604012533 | IMAGE_834822870  |
    | 2115914174 | 2003-07-06 03:06:31 | TITLE_1268165545 | IMAGE_1068632322 |
    |  552557275 | 2003-07-01 16:45:22 | TITLE_2022112717 | IMAGE_1410588295 |
    | 1500437041 | 2003-06-29 20:05:19 | TITLE_35559258   | IMAGE_159953586  |
    | 1098371257 | 2003-06-09 07:29:37 | TITLE_1694076415 | IMAGE_1409619391 |
    | 1570373503 | 2003-05-22 16:45:04 | TITLE_125157894  | IMAGE_723393492  |
    | 1330507411 | 2003-05-05 21:40:07 | TITLE_1571250589 | IMAGE_701840418  |
    | 1666035620 | 2003-04-26 02:51:40 | TITLE_504713706  | IMAGE_1410357553 |
    | 1458179791 | 2003-04-19 05:34:25 | <NO_TITLE>       | <NO-IMAGE>       |
    | 1365758305 | 2003-03-28 10:09:58 | TITLE_704554170  | IMAGE_2085080137 |
    | 2131082774 | 2003-03-26 16:43:25 | TITLE_1411034929 | IMAGE_303539208  |
    |  103396632 | 2003-02-14 09:11:28 | TITLE_915927396  | IMAGE_1381045723 |
    |  396479202 | 2003-02-01 15:51:40 | <NO_TITLE>       | <NO-IMAGE>       |
    | 2019916250 | 2003-01-30 00:44:46 | <NO_TITLE>       | <NO-IMAGE>       |
    |  431091906 | 2003-01-29 13:08:37 | <NO_TITLE>       | <NO-IMAGE>       |
    |  705166549 | 2003-01-23 21:37:07 | TITLE_1530318643 | IMAGE_257673696  |
    | 1278327049 | 2002-12-31 16:39:40 | TITLE_772845324  | IMAGE_1355754913 |
    | 1871174528 | 2002-12-30 19:03:40 | TITLE_65725764   | IMAGE_522904938  |
    |  611892727 | 2002-12-22 10:19:07 | TITLE_333758274  | IMAGE_734815032  |
    |  758497849 | 2002-12-04 15:05:10 | TITLE_129140574  | IMAGE_244407066  |
    |  518111034 | 2002-10-17 16:38:10 | TITLE_1976498683 | IMAGE_2008599775 |
    | 1737725786 | 2002-10-15 23:52:52 | TITLE_1441053871 | IMAGE_1595265847 |
    | 1206055789 | 2002-10-03 22:07:52 | TITLE_562697952  | IMAGE_198940092  |
    |  702790153 | 2002-08-31 17:37:16 | TITLE_1788304903 | IMAGE_1212944101 |
    +------------+---------------------+------------------+------------------+
    40 rows in set (0.01 sec)
    
    Query OK, 0 rows affected (0.01 sec)
    

    更新 2011-05-17 15:40

    这是新的输出,包括最新图像的日期:

    mysql> call showlast40;
    +------------+---------------------+------------------+------------------+---------------------+
    | author_id  | date_created        | title            | filename         | image_date          |
    +------------+---------------------+------------------+------------------+---------------------+
    | 1464539515 | 2004-01-07 22:45:04 | TITLE_676010724  | IMAGE_1877060293 | 2003-10-16 02:06:58 |
    |  272558724 | 2004-01-03 23:44:19 | TITLE_1213504045 | IMAGE_550812606  | 2003-12-28 07:25:43 |
    | 2000476448 | 2003-12-28 18:05:10 | TITLE_1762951489 | IMAGE_1201290847 | 2003-08-31 16:56:01 |
    |  955209697 | 2003-12-25 00:55:43 | TITLE_1064749344 | IMAGE_1335865507 | 2003-11-11 18:37:07 |
    | 1657429856 | 2003-12-19 01:03:13 | TITLE_1931852743 | IMAGE_905288424  | 2003-09-26 07:20:01 |
    |  759381001 | 2003-11-12 10:46:52 | TITLE_878255772  | IMAGE_2014780795 | 2003-09-30 06:54:40 |
    | 1269478951 | 2003-11-06 02:06:22 | TITLE_2026098781 | IMAGE_982272966  | 2003-07-28 11:59:10 |
    | 1049672131 | 2003-10-04 20:55:34 | TITLE_2043080215 | IMAGE_987859662  | 2003-11-19 05:26:37 |
    | 1429108729 | 2003-09-16 19:07:52 | TITLE_424483080  | IMAGE_35379150   | 2003-11-18 22:17:55 |
    | 1672198676 | 2003-09-13 11:49:52 | TITLE_1131552745 | IMAGE_875049630  | 2003-06-08 00:42:58 |
    | 1645878842 | 2003-08-24 13:42:04 | TITLE_1077302833 | IMAGE_702269538  | 2002-04-02 01:21:49 |
    |  172347180 | 2003-08-21 14:26:37 | TITLE_558691044  | IMAGE_1091183587 | 2003-08-13 08:30:22 |
    | 1137674509 | 2003-08-15 08:44:37 | TITLE_1982979709 | IMAGE_1234487941 | 2003-12-17 11:53:28 |
    |  282998112 | 2003-08-05 10:01:34 | TITLE_353831568  | IMAGE_738487608  | 2003-11-08 22:03:22 |
    |  246145344 | 2003-08-02 00:42:31 | TITLE_376954044  | IMAGE_1279375459 | 2003-12-05 02:30:49 |
    |  218409162 | 2003-07-14 02:55:16 | TITLE_1932540991 | IMAGE_1078689211 | 2003-07-14 15:59:37 |
    |  593263087 | 2003-07-12 22:47:01 | TITLE_1604012533 | IMAGE_834822870  | 2003-09-02 05:48:22 |
    | 2115914174 | 2003-07-06 03:06:31 | TITLE_1268165545 | IMAGE_1068632322 | 2003-04-28 16:29:01 |
    |  552557275 | 2003-07-01 16:45:22 | TITLE_2022112717 | IMAGE_1410588295 | 2003-11-01 01:55:16 |
    | 1500437041 | 2003-06-29 20:05:19 | TITLE_35559258   | IMAGE_159953586  | 2003-08-02 10:34:07 |
    | 1098371257 | 2003-06-09 07:29:37 | TITLE_1694076415 | IMAGE_1409619391 | 2004-01-07 01:00:13 |
    | 1570373503 | 2003-05-22 16:45:04 | TITLE_125157894  | IMAGE_723393492  | 2003-09-26 23:22:43 |
    | 1330507411 | 2003-05-05 21:40:07 | TITLE_1571250589 | IMAGE_701840418  | 2003-11-19 20:57:31 |
    | 1666035620 | 2003-04-26 02:51:40 | TITLE_504713706  | IMAGE_1410357553 | 2003-11-18 01:30:04 |
    | 1458179791 | 2003-04-19 05:34:25 | <NO_TITLE>       | <NO-IMAGE>       | <NO-IMAGE-DATE>     |
    | 1365758305 | 2003-03-28 10:09:58 | TITLE_704554170  | IMAGE_2085080137 | 2003-11-11 16:35:19 |
    | 2131082774 | 2003-03-26 16:43:25 | TITLE_1411034929 | IMAGE_303539208  | 2003-05-14 12:59:37 |
    |  103396632 | 2003-02-14 09:11:28 | TITLE_915927396  | IMAGE_1381045723 | 2003-12-28 18:26:28 |
    |  396479202 | 2003-02-01 15:51:40 | <NO_TITLE>       | <NO-IMAGE>       | <NO-IMAGE-DATE>     |
    | 2019916250 | 2003-01-30 00:44:46 | <NO_TITLE>       | <NO-IMAGE>       | <NO-IMAGE-DATE>     |
    |  431091906 | 2003-01-29 13:08:37 | <NO_TITLE>       | <NO-IMAGE>       | <NO-IMAGE-DATE>     |
    |  705166549 | 2003-01-23 21:37:07 | TITLE_1530318643 | IMAGE_257673696  | 2003-08-23 19:06:22 |
    | 1278327049 | 2002-12-31 16:39:40 | TITLE_772845324  | IMAGE_1355754913 | 2003-12-22 16:40:25 |
    | 1871174528 | 2002-12-30 19:03:40 | TITLE_65725764   | IMAGE_522904938  | 2003-09-06 07:08:01 |
    |  611892727 | 2002-12-22 10:19:07 | TITLE_333758274  | IMAGE_734815032  | 2003-09-22 19:16:43 |
    |  758497849 | 2002-12-04 15:05:10 | TITLE_129140574  | IMAGE_244407066  | 2003-07-15 12:38:37 |
    |  518111034 | 2002-10-17 16:38:10 | TITLE_1976498683 | IMAGE_2008599775 | 2004-01-06 16:37:34 |
    | 1737725786 | 2002-10-15 23:52:52 | TITLE_1441053871 | IMAGE_1595265847 | 2003-11-24 15:23:10 |
    | 1206055789 | 2002-10-03 22:07:52 | TITLE_562697952  | IMAGE_198940092  | 2003-08-23 11:56:46 |
    |  702790153 | 2002-08-31 17:37:16 | TITLE_1788304903 | IMAGE_1212944101 | 2003-12-19 22:56:01 |
    +------------+---------------------+------------------+------------------+---------------------+
    40 rows in set (0.01 sec)
    

    这是新的查询

      SELECT
        AAA.author_id,
        AAA.date_created,
        IFNULL(BBB.title,'<NO_TITLE>') title,
        IFNULL(CCC.filename,'<NO-IMAGE>') filename,
        IFNULL(CCC.date_added,'<NO-IMAGE-DATE>') image_date
      FROM
      (
        SELECT
          AA.id,
          AA.date_added,
          BB.author_id,
          BB.date_created
        FROM
        (
          SELECT
            A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
            FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
            LEFT JOIN article_images B ON A.id = B.article_id
            GROUP BY A.id
        ) AA
        INNER JOIN articles BB USING (id)
      ) AAA
      LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
      LEFT JOIN article_images CCC
      ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
      ORDER BY AAA.date_created DESC;
    

    我的查询做了一件别人的查询没有做的事情:

    在加入任何其他表之前,我的查询首先获取最后 40 篇文章。

    其他人的查询首先连接所有内容,然后尝试在大型临时表上执行 LIMIT 40。

    我的查询的运行时间与数据量无关。

    无论我创建的样本大小如何,我的查询都必须是最快的!!!

    【讨论】:

    • 我可能忽略了一些东西,但我必须在查询末尾添加“GROUP BY AAA.id”。否则,我会为附加到它们的每个图像获得重复的文章。看起来对吗?
    • 我创建了更大的样本。我将发布我的发现。
    • 我发布了我的发现。查询应该按原样工作。确保您按照我的原始答案中指定的方式对表格进行了索引。
    • 我将更改查询以显示文章最新图像的 date_added 字段。 BRB !!!
    • 随新列输出和查询发布的更改。
    【解决方案4】:

    这个应该非常有效,因为它不使用任何派生表。

    SELECT articles.id, articles.author_id, articles.date_created,
                SUBSTRING_INDEX(GROUP_CONCAT(filename ORDER BY article_images.date_added DESC SEPARATOR "\t"),"\t",1) as latestFilename
        FROM articles
            JOIN article_images
                ON articles.id = article_images.article_id
        GROUP BY articles.id, articles.author_id, articles.date_created
        ORDER BY articles.date_created DESC
        LIMIT 40;
    

    GROUP_CONCAT 返回组中所有文件名的字符串列表,按 article_images_date_added DESC 排序并用“\t”分隔。
    SUBSTRING_INDEX 将在第一个 "\t" 之前剪切字符串,因此只会返回最新的文件名。

    (显然,您还应该在article_images.article_id、article_images.date_created 和articles.date_created 上添加相关索引。)

    【讨论】:

      【解决方案5】:

      在这种情况下,内联子查询而不是连接可以很好地工作:

      select articles.*,
             article_contents.title,
             article_contents.content,
             (select article_images.filename
             from article_images
             where article_images.article_id = articles.id
             order by article_images.date_added desc
             limit 1
             ) as image_filename
      from articles
      join article_contents
      on article_contents.article_id = articles.id
      order by articles.date_created desc
      limit 40;
      

      性能方面,它会嵌套循环遍历前 40 行的文章,这是最快的计划;对于这些行中的每一行,子查询将嵌套循环到 article_images 中最适合的行,这也恰好是给定文章的最快计划。

      如果您需要从图像表中获取多个字段,我认为您有一个 image_id。假设是这样,取而代之的是获取 image_id,然后使用 in 子句进行第二次查询以检索您需要的行。

      另一种(而且速度稍快)的方法是使用触发器将最新的 image_id 保存在文章表中。这样做将允许您直接离开加入图像。

      【讨论】:

        【解决方案6】:
        SELECT a.id,ac.title,ac.content,ai.filename
        
        FROM articles a,article_contents ac,article_images ai
        
        WHERE a.id=ac.article_id and a.id=ai.article_id
        
        ORDER BY a.date_created desc
        
        LIMIT 40 
        

        使用上面的查询。它会为你工作

        【讨论】:

          【解决方案7】:

          试试这样的

          SELECT  A.author_id ,
                  Images.article_id ,
                  Images.filename ,
                  Images.date_added 
          FROM    articles AS A
                  LEFT JOIN ( SELECT  AI1.article_id ,
                                      AI1.filename ,
                                      AI1.date_added
                              FROM    article_images AS AI1
                              WHERE   AI1.date_added = ( SELECT   MAX(date_added)
                                                         FROM     article_images AS AI2
                                                         WHERE    AI2.article_id = AI1.article_id
                                                       )
                            ) AS Images ON A.id = Images.article_id 
          

          您还需要为 article_images 表添加索引

          (article_id ASC, date_added ASC)
          

          【讨论】:

            【解决方案8】:

            如果在搜索和显示结果时性能是主要问题,那么我建议您更改表结构,以便将最后一个图像 ID 存储在文章目录表中。这样检索速度是最佳的。

            牺牲插入速度以获得大大改进的搜索功能。

            【讨论】:

              【解决方案9】:

              基本上你想做select rows holding group-wise maximum of a field,这是由MySQL - Control which row is returned by a group by中的ʞɔıu链接的。

              在 MySQL 中,不能在 GROUP BY 之前执行 ORDER BY。您可以按照第一个链接答案中的描述或MySQL Group By ordering 中的说明进行子选择。

              在您的情况下,这很可能会导致性能下降,因此可以使用两个单独的查询。不是很优雅,但很可靠:

              SELECT a.id
              FROM articles a
              ORDER BY a.date_created DESC
              LIMIT 40
              

              对于 %ARTICLE_ID% 为当前行 ID 的 40 个结果行中的每一行:

              SELECT ai.filename
              FROM article_images ai
              WHERE ai.article_id = %ARTICLE_ID%
              ORDER BY ai.date_added DESC
              LIMIT 1
              

              我的建议是另一个:冗余。将最新的文章图像冗余地保存在文章表中。这样可以轻松快速地进行查询。

              【讨论】:

              • 将当前图像保留在文章表中是我一直在做的事情,我希望放弃它。但是,如果这就是答案,那就是答案。
              • 我完全阅读了这个答案,我理解你在做什么。您的步骤可以通过子查询组合。但是,从应用程序的角度来看,您的答案可以想象得到,而且工作得很快。从那里开始,开发人员必须自行决定实施。为您的回答 +1。
              【解决方案10】:
              SELECT a.id, ai.filename
                  FROM articles a
                      LEFT JOIN (SELECT article_id, MAX(date_added) AS MaxDate
                                     FROM article_images 
                                     GROUP BY article_id) maxai
                              INNER JOIN article_images ai
                                  ON maxai.article_id = ai.article_id
                                      AND maxai.MaxDate = ai.date_added
                          ON a.id = maxai.article_id
                  ORDER BY a.date_created DESC
                  LIMIT 40
              

              【讨论】:

              • 与 carmstrong 的解决方案一样,此查询的性能很差,我必须考虑到这一点。
              【解决方案11】:

              这样的东西有用吗?

              select * from articles left join (select article_id, filename from article_images group by article_id order by date_added desc) on id = article_id
              

              【讨论】:

              • 看起来这个解决方案行不通。即使在尝试了许多不同的索引方案之后,性能也很糟糕。
              猜你喜欢
              • 2021-04-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-09-14
              • 2020-09-30
              • 2019-09-15
              相关资源
              最近更新 更多