按IN的顺序对SQL查询进行排序

4

我正在编写一个查询

SELECT * FROM EMPLOYEES WHERE EMP_ID IN (10,5,3,9,2,8,6)

我希望结果按照以下顺序呈现。
Emp_id   Emp_Name
10       John
5        Joe
3        Tippu
9        Rich
2        Chad
8        Chris
6        Rose

基本上和IN Clause的顺序一样。这个可行吗?请告诉我。

PS: 我可以在SQL中完成,或者在获得结果集后,如果我可以使用LINQ或其他东西在前端选项中进行排序也可以(我在前端有Emp IDs数组)

谢谢


1
哇,今晚有两个关于这个问题的提问。为了保证特定的顺序,你必须按某种方式进行排序。 - Andrew
这里有一些想法:https://dev59.com/K3RC5IYBdhLWcg3wJNmf - TGH
@Andrew - 你能否提供我另一个问题的链接? - Tippu
http://stackoverflow.com/questions/19531632/order-by-listagg-is-not-ascending-according-the-list-data/19531688#comment28978176_19531688 - Andrew
2个回答

4

字符串评论答案;这将产生与原始答案相同的结果,但匹配字符串:

  string orgList = "John,Joe,Tippu,Rich,Chad,Chris,Rose";
  List<string> orderArray = new List<string>(orgList.Split(",".ToCharArray()));

  // the linq to do the ordering
  var result = ourList.OrderBy(e => {
     int loc = orderArray.IndexOf(e.Name);
     return loc == -1? int.MaxValue: loc;
  });

作为一个附注,原始答案可能会更好,如果加上这两行:

顺便说一句,原始答案加上以下两行可能会更好:

  string orgList = "10,5,3,9,2,8,6";
  List<int> orderArray = new List<int>(orgList.Split(",".ToCharArray()));

使用整数常量的替代方法。使用上面的代码将按照任意逗号分隔的整数列表进行排序。

Linq 中以下的解决方案给出了这个结果:

enter image description here

void Main()
{
  // some test data
  List<Person> ourList = new List<Person>() 
  {
    new Person() { ID = 1, Name = "Arron" },
    new Person() { ID = 2, Name = "Chad" },
    new Person() { ID = 3, Name = "Tippu" },
    new Person() { ID = 4, Name = "Hogan" },
    new Person() { ID = 5, Name = "Joe" },
    new Person() { ID = 6, Name = "Rose" },
    new Person() { ID = 7, Name = "Bernard" },
    new Person() { ID = 8, Name = "Chris" },
    new Person() { ID = 9, Name = "Rich" },
    new Person() { ID = 10, Name = "John" }
  };

  // what we will use to order
  List<int> orderArray = new List<int>(){10,5,3,9,2,8,6};

  // the linq to do the ordering
  var result = ourList.OrderBy(e => {
     int loc = orderArray.IndexOf(e.ID);
     return loc == -1? int.MaxValue: loc;
  });

  // good way to test using linqpad (get it at linqpad.com
  result.Dump();
}

// test class so we have some thing to order
public class Person
{
   public int ID { get; set; }
   public string Name { get; set; }
}

糟糕的 SQL 原始答案

WITH makeMyOrder
(
   SELECT 10 as ID, 1 as Ord
     UNION ALL
   SELECT 5 as ID, 2 as Ord
     UNION ALL
   SELECT 3 as ID, 3 as Ord
     UNION ALL
   SELECT 9 as ID, 4 as Ord
     UNION ALL
   SELECT 2 as ID, 5 as Ord
     UNION ALL
   SELECT 8 as ID, 6 as Ord
     UNION ALL
   SELECT 6 as ID, 7 as Ord
),
SELECT * 
FROM EMPLOYEES E
JOIN makeMyOrder O ON E.EMP_ID = O.ID
ORDER BY O.Ord

如果列表中有500个项目会怎样? - Aaron Bertrand
@AaronBertrand - 我知道,但是OP中没有500个项目。你的答案好了无数倍,但是当我写这篇文章时它还不存在。也许我会为那个人添加linq位... - Hogan
没错,只是指出潜在的问题,因为这个答案不仅仅适用于楼主(甚至不仅仅适用于楼主所述的问题 - 他们很可能有500个项目,但为了我们的理智而简化了问题)。 - Aaron Bertrand
@AaronBertrand - 那个Linq解决方案是否更好? - Hogan
1
我并不是说UNION是不好的 - 只是指出随着列表大小的增加,它会变得更糟。 - Aaron Bertrand
@Hogan 非常感谢您提供的LINQ版本。它运行良好。您知道如何处理字符串(例如empname)的IN子句吗? - Tippu

2

什么?Linq-To-SQL没有一个神奇的按钮可以按下让它完成这个功能吗? :-)

要在SQL Server中完成此操作,您需要一个函数将列表转换为集合并且保持顺序。有许多方法可以解决这个问题,这里是其中之一:

CREATE FUNCTION dbo.SplitInts_Ordered
(
    @List       VARCHAR(MAX),
    @Delimiter  VARCHAR(255)
)
RETURNS TABLE
AS
    RETURN (SELECT [Index] = ROW_NUMBER() OVER (ORDER BY Number), Item 
    FROM (SELECT Number, Item = CONVERT(INT, SUBSTRING(@List, Number, 
        CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number))
      FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
        FROM sys.all_objects) AS n(Number)
    WHERE Number <= CONVERT(INT, LEN(@List))
      AND SUBSTRING(@Delimiter + @List, Number, LEN(@Delimiter)) = @Delimiter
    ) AS y);
GO

现在你可以直接说:
DECLARE @list VARCHAR(MAX);

SET @list = '10,5,3,9,2,8,6';

SELECT e.Emp_Id, e.Emp_Name -- never use * in production code
  FROM dbo.Employees AS e -- always use schema prefix
  INNER JOIN dbo.SplitInts_Ordered(@list, ',') AS x
  ON x.Item = e.Emp_Id
  ORDER BY x.[Index];

一个更好的方法是完全停止传递逗号分隔列表,而是使用表值参数。这是一组事物,而不是字符串或某些 JSON 词汇。在 C# 代码中创建一个 DataTable,其中包含两列,列表和顺序。然后创建一个表类型:

CREATE TYPE dbo.SortedList AS TABLE(ID INT, [Order] INT);

然后是一个以此参数为输入的存储过程:

CREATE PROCEDURE dbo.GetTheList
  @x dbo.SortedList READONLY
AS
BEGIN
  SET NOCOUNT ON;

  SELECT e.Emp_Id, e.Emp_Name
    FROM dbo.Employees AS e
    INNER JOIN @x AS x
    ON x.ID = e.Emp_Id
    ORDER BY x.[Order];
END
GO

我不确定你是否可以使用Linq-To-SQL来完成这个任务,人们似乎很快就会加入Linq的阵营,因为它使事情变得简单。只要你不需要实际做任何事情。

太棒了!我特别喜欢“或一些 JSON 的淫秽内容”。 - Hogan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接