选择它周围的行和行

时间:2009-11-28 20:09:45

标签: mysql sql

好吧,假设我有一张带照片的桌子。

我想要做的是在页面上根据URI中的id显示照片。对照片感兴趣我希望有10张附近照片的缩略图,当前的照片应该在缩略图的中间。

这是我到目前为止的查询(这只是一个例子,我用7作为id):

SELECT
    A.*
FROM
  (SELECT
       *
   FROM media
   WHERE id < 7
   ORDER BY id DESC
   LIMIT 0, 4
   UNION
   SELECT
       *
   FROM media
   WHERE id >= 7
   ORDER BY id ASC
   LIMIT 0, 6
  ) as A
ORDER BY A.id

但是我收到了这个错误:

#1221 - Incorrect usage of UNION and ORDER BY

7 个答案:

答案 0 :(得分:7)

只能为ORDER BY'd查询定义一个UNION子句。使用UNIONUNION ALL无关紧要。 MySQL确实支持LIMIT d查询部分的UNION子句,但如果没有定义顺序的能力则相对无用。

MySQL还缺少排名功能,您需要处理数据中的空白(由于条目被删除而丢失)。唯一的选择是在SELECT语句中使用递增变量:

SELECT t.id, 
       @rownum := @rownum+1 as rownum 
  FROM MEDIA t, (SELECT @rownum := 0) r

现在我们可以获得连续编号的行列表,因此我们可以使用:

WHERE rownum BETWEEN @midpoint - ROUND(@midpoint/2) 
                 AND @midpoint - ROUND(@midpoint/2) +@upperlimit

使用7作为@midpoint的值,@midpoint - ROUND(@midpoint/2)返回值4。要获得总共10行,请将@upperlimit值设置为10.这是完整查询:

SELECT x.* 
  FROM (SELECT t.id, 
               @rownum := @rownum+1 as rownum 
          FROM MEDIA t, 
               (SELECT @rownum := 0) r) x
 WHERE x.rownum BETWEEN @midpoint - ROUND(@midpoint/2) AND @midpoint - ROUND(@midpoint/2) + @upperlimit

但如果您仍想使用LIMIT,则可以使用:

  SELECT x.* 
    FROM (SELECT t.id, 
                 @rownum := @rownum+1 as rownum 
            FROM MEDIA t, 
                 (SELECT @rownum := 0) r) x
   WHERE x.rownum >= @midpoint - ROUND(@midpoint/2)
ORDER BY x.id ASC
   LIMIT 10

答案 1 :(得分:4)

我使用以下代码解决了这个问题:

SELECT  A.* FROM  (
   (
      SELECT  *  FROM gossips
      WHERE id < 7
      ORDER BY id DESC
      LIMIT 2
   )
  UNION
   (
      SELECT * FROM gossips
      WHERE id > 7
      ORDER BY id ASC
      LIMIT 2
   )

 ) as A
ORDER BY A.id

答案 2 :(得分:2)

我不相信你可以在UNION的不同部分拥有“order by”。你能做这样的事吗:

SELECT * FROM media where id >= 7 - 4 and id <= 7 + 4 ORDER BY id

答案 3 :(得分:1)

我同意malonso(+1)提出的答案,但如果你尝试使用id = 1,你将只获得5个缩略图。我不知道你是否想要这种行为。如果你想要总是10个拇指,你可以尝试:

select top 10 * from media where id > 7 - 4

问题是select top与数据库有关(在本例中是sql server子句)。其他数据库也有类似的条款:

甲骨文:

SELECT *  media
FROM media
WHERE ROWNUM < 10
AND id > 7 - 4

MySQL的:

SELECT * 
FROM media
WHERE id > 7 - 4
LIMIT 10

所以也许你可以使用最后一个。

如果我们这样做,如果你想要最后10个拇指,我们会遇到同样的问题。例如,如果我们有90个拇指,我们给出一个id = 88 ...你可以解决它添加OR条件。在MySQL中将是:

SELECT * 
    FROM media
    WHERE id > 7 - 4
    OR (Id+5) > (select COUNT(1) from media)
    LIMIT 10

答案 4 :(得分:1)

如果您愿意使用临时表,可以细分原始查询以使用它们。

SELECT
    *
FROM media
WHERE id < 7
ORDER BY id DESC
LIMIT 0, 4
INTO TEMP t1;

INSERT INTO t1
SELECT
   *
FROM media
WHERE id >= 7
ORDER BY id ASC
LIMIT 0, 6;

select * from t1 order by id;
drop table t1;

答案 5 :(得分:1)

尝试 union all 。 Union要求服务器确保结果是唯一的,这与您的订购相冲突。

答案 6 :(得分:0)

我必须解决类似的问题,但需要考虑我们总是得到相同行数的情况,即使所需的行靠近结果集的顶部或底部(即不完全在中间)。

这个解决方案是OMG小马的一个调整&#39;响应,但rownum在所需的行最大值:

set @id = 7;
SELECT natSorted.id 
  FROM (
       SELECT gravitySorted.* FROM (
           SELECT Media.id, IF(id <= @id, @gravity := @gravity + 1, @gravity := @gravity - 1) AS gravity 
             FROM Media, (SELECT @gravity := 0) g
       ) AS gravitySorted ORDER BY gravity DESC LIMIT 10
  ) natSorted ORDER BY id;

这是对发生的事情的分解:

注意:在下面的示例中,我创建了一个包含20行的表,并删除了ID 6和9,以确保ID中的间隙不会影响结果

首先,我们为每一行分配一个重力值,该重力值以您正在寻找的特定行为中心(在这种情况下,id为7)。该行越靠近所需的行,该值就越高:

SET @id = 7;
SELECT Media.id, IF(id <= @id, @gravity := @gravity + 1, @gravity := @gravity - 1) AS gravity 
  FROM Media, (SELECT @gravity := 0) g

返回:

+----+---------+
| id | gravity |
+----+---------+
|  1 |       1 |
|  2 |       2 |
|  3 |       3 |
|  4 |       4 |
|  5 |       5 |
|  7 |       6 |
|  8 |       5 |
| 10 |       4 |
| 11 |       3 |
| 12 |       2 |
| 13 |       1 |
| 14 |       0 |
| 15 |      -1 |
| 16 |      -2 |
| 17 |      -3 |
| 18 |      -4 |
| 19 |      -5 |
| 20 |      -6 |
| 21 |      -7 |
+----+---------+

接下来,我们按重力值排序所有结果并限制所需的行数:

SET @id = 7;
SELECT gravitySorted.* FROM (
    SELECT Media.id, IF(id <= @id, @gravity := @gravity + 1, @gravity := @gravity - 1) AS gravity 
      FROM Media, (SELECT @gravity := 0) g
) AS gravitySorted ORDER BY gravity DESC LIMIT 10

返回:

    +----+---------+
    | id | gravity |
    +----+---------+
    |  7 |       6 |
    |  5 |       5 |
    |  8 |       5 |
    |  4 |       4 |
    | 10 |       4 |
    |  3 |       3 |
    | 11 |       3 |
    |  2 |       2 |
    | 12 |       2 |
    |  1 |       1 |
    +----+---------+

此时我们拥有所有想要的ID,我们只需要将它们排序回原来的顺序:

set @id = 7;
SELECT natSorted.id 
  FROM (
       SELECT gravitySorted.* FROM (
           SELECT Media.id, IF(id <= @id, @gravity := @gravity + 1, @gravity := @gravity - 1) AS gravity 
             FROM Media, (SELECT @gravity := 0) g
       ) AS gravitySorted ORDER BY gravity DESC LIMIT 10
  ) natSorted ORDER BY id;

返回:

+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
|  7 |
|  8 |
| 10 |
| 11 |
| 12 |
+----+