2017-01-19 5 views
1

Имеет ли T-SQL возможность произвольного сравнения строк (для сортировки), что эквивалентно .NET IComparer?Пользовательский заказ T-SQL By (IComparer)

Что-то вроде способности дать Order By пользовательской функции, которая принимает 2 строки и возвращает значение, представляющее, как они сравниваются (больше, чем это, равно)?

В настоящее время у меня есть реализация C# ICompararer, которая используется для сортировки элементов в коде, но теперь мне нужно создать такой же отсортированный вывод из хранимой процедуры.

Для справки это IComparer, который я пытаюсь реализовать в TSQL.

public class SemanticComparer : IComparer<string> 
{ 
    private static Regex _splitter = new Regex("\\W+"); 

    public int Compare(string x, string y) 
    { 
     string[] partsX = _splitter.Split(x); 
     string[] partsY = _splitter.Split(y); 

     int shortest = Math.Min(partsX.Length, partsY.Length); 

     for (int index = 0; index < shortest; index++) 
     { 
      int intX, intY; 
      int result; 

      if (int.TryParse(partsX[index], out intX) && int.TryParse(partsY[index], out intY)) 
      { 
       result = intX.CompareTo(intY); 
      } 
      else 
      { 
       result = string.Compare(partsX[index], partsY[index], StringComparison.Ordinal); 
      } 

      if (result != 0) 
      { 
       return result; 
      } 
     } 

     return 0; 
    } 
} 

Он должен был бы иметь возможность сортировать вещи, которые выглядят так (в порядке, как они должны быть выведены):

  • 2-101.11 (A) (B)
  • 9,1
  • 9,2
  • 9.2.1
  • 9.02.2
  • 9.02.3
  • 9,3
  • 9.3.1
  • 9.3.2
  • 10.
  • 11.
  • 11,1
  • 11.2.a
  • 11.2.b
  • 11.2.c
  • 11a.2.a
  • 11b.2.b
  • 21 CFR 110,10
  • 046,981 (е) (м)

Где каждый не-буквенный символ разбивает строку в отрезкам, пытается сравнить их численно, если это возможно, а затем в виде строк, если нет. В число сегментов может быть любая «глубина».

CLR Хранимые процедуры, к сожалению, не являются вариантом.

+0

Вероятно 'выражение case'. – shawnt00

+0

Не могли бы вы подробнее остановиться на 'case'? Я сделал некоторые поиски, и я вижу, как это можно использовать для некоторого уровня программной сортировки, но я не вижу, как использовать его для получения результата, который я ищу. –

+0

Итак, у вас есть два строковых значения, которые вы хотите рассматривать как векторы и сортировать их по алфавиту. Как выглядят ваши ценности? – shawnt00

ответ

2
with data as (
    select c from (values 
     ('9.1'), ('9.2'), ('9.2.1'), ('9.02.2'), ('9.02.3'), ('9.3'), ('9.3.1'), ('9.3.2'), 
     ('10.'), ('11.'), ('11.1'), ('11.2.a'), ('11.2.b'), ('11.2.c'), ('11a.2.a'), ('11b.2.b') 
    ) t(c) 
) 
select c, '[' + 
    right('00000' + 
     substring(c, 1, charindex('.', c + '.0.0', 1) - 1) + 
      case when substring(c + '.0.0', charindex('.', c + '.0.0', 1) - 1, 1) between '0' and '9' then ' ' else '' end, 
     6 
    ) + '.' + 
    right('00000' + 
     substring(c, charindex('.', c + '.0.0', 1) + 1, charindex('.', c + '.0.0', charindex('.', c + '.0.0', 1) + 1) - charindex('.', c + '.0.0', 1) - 1) + 
      case when right('0' + 
       substring(c, charindex('.', c + '.0.0', 1) + 1, charindex('.', c + '.0.0', charindex('.', c + '.0.0', 1) + 1) - charindex('.', c + '.0.0', 1) - 1), 
       1 
      ) between '0' and '9' then ' ' else '' end, 
     6 
    ) + '.' + 
    right('00000' + 
     substring(c, charindex('.', c + '.0.0', charindex('.', c + '.0.0', 1) + 1) + 1, 10) + 
      case when right('0' + 
       substring(c, charindex('.', c + '.0.0', charindex('.', c + '.0.0', 1) + 1) + 1, 10), 
       1 
      ) between '0' and '9' then ' ' else '' end, 
     6 
    ) + ']' 
from data 
order by 2; 

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

Идея состоит в том, что вы можете преобразовать свои значения в нормализованном формате, который - это, сортируемый с помощью обычного алфавитного сорта. Передняя цифровая часть имеет нулевое значение. В конце каждого «поля» допускается единый алфавитный символ, иначе добавляется пробел. В качестве одного из примеров 11.2.a становится [00011 .00002 .00000a]

Это довольно гибкий подход, и вы можете наверняка завершить эту логику скалярной функцией. Я позволю вам решить, все ли это хорошая идея.

http://rextester.com/KZX77690

http://rextester.com/LYL6977 (Немного улучшилось?)

+0

Думаю, я могу работать с этим и расширять его до того, что мне нужно, спасибо! –

Смежные вопросы