4

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

Tippu
  • 1,191
  • 4
  • 16
  • 36
  • 1
    Wow, two questions on this tonight. To guarantee a specific order, you must sort by something. – Andrew Oct 23 '13 at 02:41
  • Some ideas here http://stackoverflow.com/questions/396748/ordering-by-the-order-of-values-in-a-sql-in-clause – TGH Oct 23 '13 at 02:41
  • @Andrew - Can you please provide me the link of other question? – Tippu Oct 23 '13 at 02:44
  • http://stackoverflow.com/questions/19531632/order-by-listagg-is-not-ascending-according-the-list-data/19531688#comment28978176_19531688 – Andrew Oct 23 '13 at 02:45

2 Answers2

4

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:

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

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
Hogan
  • 69,564
  • 10
  • 76
  • 117
  • What if there are 500 items in the list? – Aaron Bertrand Oct 23 '13 at 02:51
  • @AaronBertrand - I know, but there weren't 500 items in the OP. Your answer is a gazillion times better, but it didn't exist when I wrote this. Maybe I'll add the linq bit for the guy... – Hogan Oct 23 '13 at 02:55
  • Right, just pointing out the potential issue because this answer isn't just for the OP (or even just for the OP's stated problem - they very well could have 500 items but wanted to simplify the question for our sanity). – Aaron Bertrand Oct 23 '13 at 02:58
  • @AaronBertrand - There... Linq solution... that better? – Hogan Oct 23 '13 at 03:15
  • 1
    I wasn't saying the UNION was *bad* - just pointing out that it gets worse as the size of the list increases. – Aaron Bertrand Oct 23 '13 at 03:27
  • @Hogan Thank you so much for LINQ version. It is working fine. Do you know how to do if the IN clause is with string (for example empname)? – Tippu Oct 23 '13 at 18:37
2

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.

Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490