Как отсортировать в порядке, как введено в SQL Server? - PullRequest
0 голосов
/ 25 июня 2018

Я использую SQL Server и пытаюсь найти результаты, но я хотел бы получить результаты в том же порядке, в котором я ввел условия.

Мой код:

SELECT 
    AccountNumber, EndDate
FROM 
    Accounts
WHERE 
    AccountNumber IN (212345, 312345, 145687, 658975, 256987, 365874, 568974, 124578, 125689)   -- I would like the results to be in the same order as these numbers.

Ответы [ 5 ]

0 голосов
/ 18 июля 2018

Я предложу еще один подход, который я только что обнаружил, но для этого нужен v2016.К сожалению, разработчики забыли включить индекс в набор результатов STRING_SPLIT(), но это сработало бы и задокументировано:

Решение через FROM OPENJSON():

DECLARE @str VARCHAR(100) = 'val1,val2,val3';

SELECT *
FROM OPENJSON('["' +  REPLACE(@str,',','","') + '"]');

Результат

key value   type
0   val1    1
1   val2    1
2   val3    1

Документация ясно говорит:

Когда OPENJSON анализирует массив JSON, функция возвращает индексы элементов в тексте JSON в виде ключей.

0 голосов
/ 28 июня 2018

Хорошо, после некоторого переосмысления я предложу ультимативный, основанный на XML type-safe и sort-safe splitter:

Declare @List varchar(max)='212345, 312345, 145687, 658975, 256987, 365874, 568974, 124578, 125689';
DECLARE @delimiter VARCHAR(10)=', ';

WITH Casted AS
(
    SELECT (LEN(@List)-LEN(REPLACE(@List,@delimiter,'')))/LEN(REPLACE(@delimiter,' ','.')) + 1 AS ElementCount
           ,CAST('<x>' + REPLACE((SELECT @List AS [*] FOR XML PATH('')),@delimiter,'</x><x>')+'</x>' AS XML) AS ListXml
)
,Tally(Nmbr) As
(
    SELECT TOP((SELECT ElementCount FROM Casted)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values v1 CROSS JOIN master..spt_values v2
)
SELECT Tally.Nmbr AS Position
      ,(SELECT ListXml.value('(/x[sql:column("Tally.Nmbr")])[1]','int') FROM Casted) AS Item 
FROM Tally;

Хитростьэто создать список чисел с подходящим номером элемента (таблица чисел была еще лучше) и выбрать элементы в соответствии с их положением.

Подсказка: это довольно медленно ...

ОБНОВЛЕНИЕ: еще лучше:

WITH Casted AS
(
    SELECT (LEN(@List)-LEN(REPLACE(@List,@delimiter,'')))/LEN(REPLACE(@delimiter,' ','.')) + 1 AS ElementCount
           ,CAST('<x>' + REPLACE((SELECT @List AS [*] FOR XML PATH('')),@delimiter,'</x><x>')+'</x>' AS XML)
           .query('
                   for $x in /x
                   return <x p="{count(/x[. << $x])}">{$x/text()[1]}</x>
                  ') AS ListXml
)
SELECT x.value('@p','int') AS Position
      ,x.value('text()[1]','int') AS Item 
FROM Casted
CROSS APPLY Casted.ListXml.nodes('/x') AS A(x);

Элементы создаются как

<x p="99">TheValue</x>

К сожалению, функция XQuery position() недоступна для извлечения Значение.Но вы можете использовать трюк для подсчета всех элементов до данного узла.это плохо масштабируется, так как этот счет должен выполняться снова и снова.Чем больше элементов, тем хуже это идет ...

ОБНОВЛЕНИЕ2: При известном количестве элементов можно использовать это (гораздо лучшая производительность)

Используйте XQuery для итерации буквально заданного списка:

WITH Casted AS
(
    SELECT (LEN(@List)-LEN(REPLACE(@List,@delimiter,'')))/LEN(REPLACE(@delimiter,' ','.')) + 1 AS ElementCount
           ,CAST('<x>' + REPLACE((SELECT @List AS [*] FOR XML PATH('')),@delimiter,'</x><x>')+'</x>' AS XML)
           .query('
                   for $i in (1,2,3,4,5,6,7,8,9)
                   return <x p="{$i}">{/x[$i]/text()[1]}</x>
                  ') AS ListXml
)
SELECT x.value('@p','int') AS Position
      ,x.value('text()[1]','int') AS Item 
FROM Casted
CROSS APPLY Casted.ListXml.nodes('/x') AS A(x);
0 голосов
/ 25 июня 2018

Вы можете заменить IN на JOIN и установить поле для заказа, например:

SELECT AccountNumber , EndDate
FROM Accounts a
JOIN (
    SELECT 212345 AS Number, 1 AS SeqOrder
UNION ALL
    SELECT 312345 AS Number, 2 AS SeqOrder
UNION ALL
    SELECT 145687 AS Number, 3 AS SeqOrder
UNION ALL
    ... -- and so on
) AS inlist ON inlist.Number = a.AccountNumber
ORDER BY inlist.SeqOrder
0 голосов
/ 26 июня 2018

Это не ответ, просто какой-то тест-код для проверки подхода Джона Каппеллетти.

DECLARE @tbl TABLE(ID INT IDENTITY,SomeGuid UNIQUEIDENTIFIER);


--Create more than 6 mio rows with an running number and a changing Guid
WITH tally AS (SELECT ROW_NUMBER()OVER(ORDER BY (SELECT NULL)) AS Nmbr 
               FROM master..spt_values v1 
               CROSS JOIN master..spt_values v2)
INSERT INTO @tbl 
SELECT NEWID() from tally;

SELECT COUNT(*) FROM @tbl; --6.325.225 on my machine

--Create an XML with nothing more than a list of GUIDs in the order of the table's ID
DECLARE @xml XML=
(SELECT SomeGuid FRom @tbl ORDER BY ID FOR XML PATH(''),ROOT('root'),TYPE);

--Create one invalid entry
UPDATE @tbl SET SomeGuid = NEWID() WHERE ID=10000;

--Read all GUIDs out of the XML and number them
DECLARE @tbl2 TABLE(Position INT,TheGuid UNIQUEIDENTIFIER);
INSERT INTO @tbl2(Position,TheGuid)
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
      ,g.value(N'text()[1]',N'uniqueidentifier')
FROM @xml.nodes(N'/root/SomeGuid') AS A(g);

--then JOIN them via "Position" and check, 
--if there are rows, where not the same values get into the same row.
SELECT *
FROM @tbl t
INNER JOIN @tbl2 t2 ON t2.Position=t.ID
WHERE t.SomeGuid<>t2.TheGuid;

По крайней мере, в этом простом случае я всегда получаю только одну запись, которая была признана недействительной ...

0 голосов
/ 25 июня 2018

Вот линейный подход

Пример

Declare @List varchar(max)='212345, 312345, 145687, 658975, 256987, 365874, 568974, 124578, 125689'

Select A.AccountNumber 
      ,A.EndDate
 From  Accounts A
 Join (
        Select RetSeq = Row_Number() over (Order By (Select null))
              ,RetVal = v.value('(./text())[1]', 'int')
        From  (values (convert(xml,'<x>' + replace(@List,',','</x><x>')+'</x>'))) x(n)
        Cross Apply n.nodes('x') node(v)
      ) B on A.AccountNumber = B.RetVal
 Order By B.RetSeq

РЕДАКТИРОВАТЬ - подзапрос Возвращает

RetSeq  RetVal
1       212345
2       312345
3       145687
4       658975
5       256987
6       365874
7       568974
8       124578
9       125689
...