按 SQLIN ()子句中值的顺序排序

我想知道是否有更好的方法可以按 IN ()子句中值的顺序排序。

问题是我有两个查询,一个获取所有 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,但是我认为记录下其他 DBs 的选项可能会很有用。

149983 次浏览

试试这个:

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 可能需要进行一些调整才能使相关的子查询正常工作,但是基本原则应该是合理的。

使用 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. order by case id when 123 then 1 when 456 then 2 else null end asc

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

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

我的第一个想法是编写一个查询,但您说这是不可能的,因为一个是由用户运行的,另一个是在后台运行的。如何存储从用户传递到后台进程的 id 列表?为什么不把它们放在一个临时表中,用一列来表示顺序。

这样吧:

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

IN 子句描述了一组值,而这些值没有顺序。

使用连接然后在 display_order列上排序的解决方案是最接近正确的解决方案; 其他可能是特定于 DBMS 的黑客(或者使用标准 SQL 中的 OLAP 函数执行某些操作)。当然,联接是最接近可移植的解决方案(尽管使用 display_order值生成数据可能会有问题)。请注意,您可能需要选择排序列; 这曾经是标准 SQL 中的一个要求,尽管我相信它在一段时间以前作为一个规则是放松的(可能早在 SQL-92之前)。

使用 MySQLFIND _ IN _ SET函数:

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

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

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

对于 Oracle 来说,John 使用 instr ()函数的解决方案是可行的 选择 ID 来自表1 其中 id 在(1,20,45,60) ORDER BY instr (’1,20,45,60’,id)

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)

效果非常好

获得排序数据。

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

我认为您应该设法以一种简单的连接方式来存储数据,这将是完美的,所以不会出现黑客攻击和复杂的事情。

例如,我有一个“最近播放”的曲目 ID 列表,在 SQLite i 上只需要做:

SELECT * FROM recently NATURAL JOIN tracks;

如果您想使用 MSSQLServer2008 + 中的查询输入的值对查询进行任意排序,可以通过动态创建一个表并执行类似的连接(使用 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,我必须按照该列显式排序,至少在2014年 SQL Server Express 是这样的。

我只是尝试这样做,这是 MS SQL 服务器,我们没有 FIELD () :

SELECT table1.id
...
INNER JOIN
(VALUES (10,1),(3,2),(4,3),(5,4),(7,5),(8,6),(9,7),(2,8),(6,9),(5,10)
) AS X(id,sortorder)
ON X.id = table1.id
ORDER BY X.sortorder

请注意,我也允许复制。