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

问题描述 投票:0回答:14

我想知道是否有(可能是更好的方法)按 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,但我认为让它记下其他数据库的选项可能会很有用。

mysql sql sql-order-by
14个回答
221
投票

使用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()
函数中,这将完全符合您的要求。


18
投票

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

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

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

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


10
投票

如果您想使用 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 中。


6
投票
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)

效果真的很棒


6
投票

Ans 获取排序数据。

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

4
投票

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

您使用连接然后在

display_order
列上排序的解决方案是最接近正确的解决方案;其他任何事情都可能是特定于 DBMS 的 hack(或者是使用标准 SQL 中的 OLAP 函数做一些事情)。 当然,连接是最接近可移植的解决方案(尽管使用
display_order
值生成数据可能会出现问题)。 请注意,您可能需要选择排序列;这曾经是标准 SQL 中的一项要求,尽管我相信不久前它已经作为一项规则放宽了(也许早在 SQL-92 之前)。


3
投票

使用MySQL FIND_IN_SET函数:

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

2
投票

试一试:

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


2
投票

对于 Oracle,John 使用 instr() 函数的解决方案有效。这是一个稍微不同的解决方案 -

SELECT id 
FROM table1 
WHERE id IN (1, 20, 45, 60) 
ORDER BY instr('1, 20, 45, 60', id)


2
投票

我只是尝试在 MS SQL Server 上执行此操作,但我们没有 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

请注意,我也允许重复。


1
投票

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

那么这个怎么样:

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

1
投票

我认为你应该设法以一种简单地进行连接的方式存储数据,这将是完美的,因此不会发生黑客攻击和复杂的事情。

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

SELECT * FROM recently NATURAL JOIN tracks;

0
投票

选择名称、描述...其中 id IN (SELECT id FROM table1 WHERE ... ORDER BY display_order, name)

© www.soinside.com 2019 - 2024. All rights reserved.