按SQL IN()子句中的值顺序排序

时间:2008-12-28 22:09:50

标签: mysql sql sql-order-by

我想知道是否存在(可能更好的方式)按IN()子句中的值的顺序排序。

问题是我有2个查询,一个获取所有ID,第二个查询所有信息。第一个创建我想要第二个订购的ID的顺序。 ID以正确的顺序放入IN()子句中。

所以它就像(非常简化):

SELECT id FROM table1 WHERE ... ORDER BY display_order, name

SELECT name, description, ... WHERE id IN ([id's from first])

问题是第二个查询不会按照将ID放入IN()子句的顺序返回结果。

我找到的一个解决方案是将所有ID放入带有自动递增字段的临时表中,然后将其加入第二个查询。

有更好的选择吗?

注意:由于第一个查询是“由用户”运行而第二个是在后台进程中运行,因此无法使用子查询将2合1查询组合。

我正在使用MySQL,但我认为让它注意到其他数据库的选项可能也很有用。

12 个答案:

答案 0 :(得分:175)

使用MySQL的FIELD()功能:

SELECT name, description, ...
FROM ...
WHERE id IN([ids, any order])
ORDER BY FIELD(id, [ids in order])

FIELD()将返回第一个参数的索引,该索引等于第一个参数(第一个参数本身除外)。

FIELD('a', 'a', 'b', 'c')

将返回1

FIELD('a', 'c', 'b', 'a')

将返回3

如果您将id粘贴到IN()子句和FIELD()函数中,则可以按照相同的顺序执行此操作。

答案 1 :(得分:14)

请参阅以下如何获取排序数据。

SELECT ...
  FROM ...
 WHERE zip IN (91709,92886,92807,...,91356)
   AND user.status=1
ORDER 
    BY provider.package_id DESC 
     , FIELD(zip,91709,92886,92807,...,91356)
LIMIT 10

答案 2 :(得分:11)

浮现在脑海中的两个解决方案:

  1. order by case id when 123 then 1 when 456 then 2 else null end asc

  2. order by instr(','||id||',',',123,456,') asc

  3. instr()来自Oracle;也许您有locate()charindex()或类似的东西)

答案 3 :(得分:5)

获取排序数据的答案。

SELECT ...
FROM ...
ORDER  BY FIELD(user_id,5,3,2,...,50)  LIMIT 10

答案 4 :(得分:5)

如果您想使用 MS SQL Server 2008 + 中的查询输入的值对查询进行任意排序,可以通过动态创建表并执行类似的连接来完成(使用OP的命名法)。

SELECT table1.name, table1.description ... 
FROM (VALUES (id1,1), (id2,2), (id3,3) ...) AS orderTbl(orderKey, orderIdx) 
LEFT JOIN table1 ON orderTbl.orderKey=table1.id
ORDER BY orderTbl.orderIdx

如果将VALUES语句替换为执行相同操作的其他语句,但在ANSI SQL中,那么这应该适用于任何SQL数据库。

注意: 查询大于100的记录集时,创建的表中的第二列(orderTbl.orderIdx)是必需的。我最初没有orderIdx列,但发现结果集大于100我必须按该列显式排序;无论如何,在SQL Server Express 2014中。

答案 5 :(得分:4)

IN子句描述了一组值,并且集合没有顺序。

您的解决方案通过联接然后在display_order列进行排序是最接近正确的解决方案;其他任何东西都可能是特定于DBMS的黑客(或者在标准SQL中使用OLAP函数做一些事情)。当然,连接是最接近可移植的解决方案(尽管生成具有display_order值的数据可能会有问题)。请注意,您可能需要选择排序列;曾经是标准SQL中的一个要求,尽管我相信它在不久前已经放宽了(可能早在SQL-92之前)。

答案 6 :(得分:3)

SELECT ORDER_NO, DELIVERY_ADDRESS 
from IFSAPP.PURCHASE_ORDER_TAB 
where ORDER_NO in ('52000077','52000079','52000167','52000297','52000204','52000409','52000126') 
ORDER BY instr('52000077,52000079,52000167,52000297,52000204,52000409,52000126',ORDER_NO)

工作真的很棒

答案 7 :(得分:2)

使用MySQL FIND_IN_SET功能:

  SELECT * 
    FROM table_name 
   WHERE id IN (..,..,..,..) 
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);

答案 8 :(得分:2)

对于Oracle,John使用instr()函数的解决方案可行。这是有点不同的解决方案 - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)

答案 9 :(得分:1)

我的第一个想法是写一个查询,但你说这是不可能的,因为一个是由用户运行而另一个是在后台运行。如何存储要从用户传递到后台进程的ID列表?为什么不将它们放在带有列的临时表中以表示订单。

那怎么样:

  1. 用户界面位运行并将值插入到您创建的新表中。它会插入id,position和某种作业号标识符)
  2. 作业编号将传递给后台进程(而不是所有ID)
  3. 后台进程会从步骤1中的表中进行选择,然后您加入以获取所需的其他信息。它使用WHERE子句中的作业号,并按位置列进行排序。
  4. 后台进程完成后,将根据作业标识从表中删除。

答案 10 :(得分:1)

我认为你应该设法存储你的数据,你只需要做一个连接,这将是完美的,所以没有黑客和复杂的事情发生。

我有一个“最近播放”的音轨ID列表,在SQLite上我只是这样做:

SELECT * FROM recently NATURAL JOIN tracks;

答案 11 :(得分:0)

试一试:

SELECT name, description, ...
WHERE id IN
    (SELECT id FROM table1 WHERE...)
ORDER BY
    (SELECT display_order FROM table1 WHERE...),
    (SELECT name FROM table1 WHERE...)

WHERE可能需要稍微调整才能使相关的子查询正常工作,但基本原则应该是合理的。