简体   繁体   中英

Sort the SQL Query in the order of IN

I am writing a query

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

I want the result should be in the following order

Emp_id   Emp_Name
10       John
5        Joe
3        Tippu
9        Rich
2        Chad
8        Chris
6        Rose

Basically in the same order of IN Clause. Is it possible to do that? Please let me know.

PS: I can either do it in SQL or after I get the resultset if I can use LINQ or something to sort in front end option also will work for me (I have the Emp IDs in array in front end)

Thanks

String comment answer; this will give the same result as original answer but matching on strings:

  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;
  });

as a side note the original answer would probably had been better with these two lines:

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

instead of using integer constants. Using the code above will order by an arbitrary comma separated list of integers.

The solution below in Linq gives this result:

在此输入图像描述

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; }
}

Original bad SQL answer

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

What, Linq-To-SQL doesn't have a magic button you can press to make it do this? :-)

To do this in SQL Server, you need a function that will turn your list into a set and maintain the order. There are many ways to skin this cat; here's one:

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

Now you can just say:

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];

A much, much, much, much, much better approach is to stop passing a comma-separated list at all, and use Table-Valued Parameters. This is a set of things, not a string or some JSON obscenity. Create a DataTable in your C# code, with two columns, the list and the order. Then create a table type:

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

Then a stored procedure that takes this as a parameter:

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

Whether you can do this with Linq-To-SQL, I'm not sure; people seem to jump on the Linq bandwagon very quickly, because it makes things so easy. Well, as long as you don't need to actually do anything.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM