如何指定GROUP BY应保留哪些行以及哪些行应折叠

时间:2016-08-05 04:15:45

标签: mysql sql sql-order-by

我正在处理的应用程序包含用户可以创建相册并将图像上传到其中的部分。应用程序会创建几个图像调整大小,以便不向用户提供大版本。有关文件的所有信息都以以下结构存储在数据库中

  1. 相册表有一个ID和名称
  2. 照片桌上有一张照片列表,每张照片都知道它属于哪张专辑
  3. 还有一个photo_versions。每个photo_versions都存储Photo表中对象的ID。
  4. 此逻辑由以下架构表示:

    CREATE TABLE albums(`id` int, `name` varchar(255)); 
    INSERT INTO albums (id, name) VALUES 
    (1, "one"),
    (2, "two"),
    (3, "three");
    
    CREATE TABLE photos(`id` int, `albums_id` int, `title` varchar(255));
    INSERT INTO photos (id, albums_id, title) VALUES 
    (1, 1, "a"),
    (2, 1, "b"),
    (3, 1, "c");
    
    CREATE TABLE photos_versions(`id` int, `photos_id` int, `width` int, `height` int);
    INSERT INTO photos_versions (photos_id, width, height) VALUES 
    (1, 1000, 800),(1, 800, 600), (1, 600, 400),
    (2, 1000, 800), (2, 800, 600), (2, 600, 400),
    (3, 1000, 800), (3, 800, 600), (3, 600, 400);
    

    用户界面能够请求特定的高度,我正在处理的后端应该返回数据库中最接近的高度。我正在按照要求去做。它从加入所有这些表开始:

    SELECT *
    FROM albums a
    INNER JOIN photos p ON p.albums_id = a.id
    INNER JOIN photos_versions pv ON pv.photos_id = p.id;
    

    结果如下表所示:

    +------+------+----+-----------+-------+------+-----------+-------+--------+
    | id   | name | id | albums_id | title | id   | photos_id | width | height |
    +------+------+----+-----------+-------+------+-----------+-------+--------+
    |    1 | one  |  1 |         1 | a     | NULL |         1 |  1000 |    800 |
    |    1 | one  |  1 |         1 | a     | NULL |         1 |   800 |    600 |
    |    1 | one  |  1 |         1 | a     | NULL |         1 |   600 |    400 |
    |    1 | one  |  2 |         1 | b     | NULL |         2 |  1000 |    800 |
    |    1 | one  |  2 |         1 | b     | NULL |         2 |   800 |    600 |
    |    1 | one  |  2 |         1 | b     | NULL |         2 |   600 |    400 |
    |    1 | one  |  3 |         1 | c     | NULL |         3 |  1000 |    800 |
    |    1 | one  |  3 |         1 | c     | NULL |         3 |   800 |    600 |
    |    1 | one  |  3 |         1 | c     | NULL |         3 |   600 |    400 |
    +------+------+----+-----------+-------+------+-----------+-------+--------+
    9 rows in set (0.00 sec)
    

    现在,我们需要按照photos_id进行分组(因为我们希望得到最近的给定照片版本)。所以,请求变成了:

    SELECT *
    FROM albums a
    INNER JOIN photos p ON p.albums_id = a.id
    INNER JOIN photos_versions pv ON pv.photos_id = p.id
    GROUP BY photos_id;
    

    结果如下表所示:

    +------+------+----+-----------+-------+------+-----------+-------+--------+
    | id   | name | id | albums_id | title | id   | photos_id | width | height |
    +------+------+----+-----------+-------+------+-----------+-------+--------+
    |    1 | one  |  1 |         1 | a     | NULL |         1 |  1000 |    800 |
    |    1 | one  |  2 |         1 | b     | NULL |         2 |  1000 |    800 |
    |    1 | one  |  3 |         1 | c     | NULL |         3 |  1000 |    800 |
    +------+------+------+-----------+-------+------+-----------+-------+--------+
    3 rows in set (0.00 sec)
    

    但是,它不一定保留具有属性的行(具有最接近我指定的高度的行)。如何分组photos_id并选择距离最近的那个?

    P.S。附上了SQL小提琴 - http://sqlfiddle.com/#!9/84f4f/1

1 个答案:

答案 0 :(得分:1)

此处的一种方法是在查询中添加其他联接,这将限制为每个照片ID组具有最接近高度的照片。

SELECT a.*, p.*, pv1.*
FROM albums a
INNER JOIN photos p
    ON p.albums_id = a.id
INNER JOIN photos_versions pv1
    ON pv1.photos_id = p.id
INNER JOIN
(
    SELECT photos_id, MIN(ABS(height - SOME_HEIGHT)) AS diff
    FROM photos_versions
    GROUP BY photos_id
) pv2
    ON pv1.photos_id = pv2.photos_id AND
       MIN(ABS(pv1.height - SOME_HEIGHT)) = pv2.diff

您可以将SOME_HEIGHT替换为您从搜索高度获得的任何值。