Извлечение части строки с множеством сценариев с использованием T-SQL - PullRequest
0 голосов
/ 11 октября 2018

Мне нужен запрос T-SQL, чтобы получить вывод (указанный ниже под ожидаемым), охватывающий все случаи.Я пытался сделать это, но безуспешно:

select 
    code,
    substring(code, patindex('%[0-9]%', code),
                    case 
                       when patindex('%[. ,/-]%', substring(code, patindex('%[0-9]%', code), len(code))) <> 0
                          then patindex('%[. ,/-]%', substring(code, patindex('%[0-9]%', code), len(code))) - 1 
                          else patindex('%[. ,/-]%', substring(code, patindex('%[0-9]%', code), len(code)))
                     end) 
from 
    table

Это ожидаемый вывод

input                           output 
------------------------------------------------------
AB 123456.123                   123456
AB 123456/123                   123456
AB 123456-123                   123456
AB B0-23456.123                 0-23456
AB 1234 5678 9545 3214.123      1234 5678 9545 3214
AB 123456 123                   123456 
AB.123456 123                   123456 
AB..123456 123                  123456 
AB..1C23456 123                 1C23456 

Правила

  1. Начинается с первого появления числа
  2. Разделить строку после специальных символов (, /.-) действительный регистр
    2.1, если строка имеет - после 3 действительных чисел, например: AB B0-23456.123 ---- 0-23456
    2.2, если строкаимеет более 3 цифр после пробела, например: AB 1234 5678 9545 3214.123 ---- 1234 5678 9545 3214

Ответы [ 2 ]

0 голосов
/ 12 октября 2018

Если честно: это кошмар.T-SQL - абсолютно неправильный инструмент для этого!

Только потому, что я поставил недостаточный (из-за вашего недостаточного вопроса) ответ, мне настоятельно рекомендуется как-то решить эту проблему.Это вопрос спортивного мастерства ...

DECLARE @mockup TABLE(ID INT IDENTITY, YourString VARCHAR(1000));
INSERT INTO @mockup VALUES
 ('AB 123456.123')                 
,('AB 123456/123')                 
,('AB 123456-123')                 
,('AB B0-23456.123')               
,('AB 1234 5678 9545 3214.123')    
,('AB 123456 123')                 
,('AB.123456 123')                 
,('AB..123456 123')                
,('AB..1C23456 123')
,('AB 1234 5678 954 3214-12345.123');

- Кошмар

WITH CutForRules AS
(
    SELECT t.ID
          ,t.YourString
          ,ROW_NUMBER() OVER(PARTITION BY t.ID ORDER BY (SELECT (NULL))) FragmentIndex
          ,c AsXml
          ,d.value('text()[1]','varchar(100)') Fragment
          ,ISNUMERIC(d.value('text()[1]','varchar(100)')) FragmentIsNum
          ,LEN(d.value('text()[1]','varchar(100)')) FragmentLength
          ,d.value('@dlmt','varchar(10)') Delimiter
    FROM @mockup t
    CROSS APPLY(SELECT REVERSE(SUBSTRING(t.YourString,PATINDEX('%[0-9]%',t.YourString),1000))) A(a) 
    CROSS APPLY(SELECT REVERSE(SUBSTRING(a,PATINDEX('%[ /.-]%',a)+1,1000))) B(b)    
    CROSS APPLY(SELECT CAST('<x>' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(b,'/','|'),' ','</x><x dlmt=" ">'),'.','</x><x dlmt=".">'),'-','</x><x dlmt="-">'),'|','</x><x dlmt="/">') + '</x>' AS XML)) C(c)
    CROSS APPLY c.nodes('/x') D(d)
)
SELECT t1.ID
      ,t1.YourString
      ,(
        SELECT CONCAT(t2.Delimiter,t2.Fragment)
        FROM CutForRules t2
        WHERE t1.ID=t2.ID
          AND (t2.FragmentIndex<(SELECT MIN(t3.FragmentIndex) 
                                 FROM CutForRules t3 
                                 WHERE t3.ID=t1.ID
                                   AND t3.FragmentIndex>t2.FragmentIndex
                                   AND t3.Delimiter=' '
                                   AND t3.FragmentLength<4
                                   AND t3.FragmentIsNum=1)
               OR NOT EXISTS(SELECT 1 FROM CutForRules t4 WHERE t4.ID=t1.ID AND t4.Delimiter=' ' AND t4.FragmentLength<4)
              )
        ORDER BY t2.FragmentIndex
        FOR XML PATH('')
       )
FROM CutForRules t1
GROUP BY t1.ID,t1.YourString
ORDER BY t1.ID;

Вы можете поместить SELECT * FROM CutForRules, чтобы увидеть промежуточный набор результатов, который я использую для этого.

Но я почти уверен, что вы придумаете О да, это работает, но есть еще один случай ...

Просто чтобы прояснить это:Я в этой точке; -)

ОБНОВЛЕНИЕ: Некоторые объяснения

Cte CutForRules вернет этот набор для моих тестовых данных:

+----+---------------------------------+---------+---+---+------+
|    | YourString                      |Fragment | N | L | Delm |
+----+---------------------------------+---------+---+---+------+
| 1  | AB 123456.123                   | 123456  | 1 | 6 | NULL |
+----+---------------------------------+---------+---+---+------+
| 2  | AB 123456/123                   | 123456  | 1 | 6 | NULL |
+----+---------------------------------+---------+---+---+------+
| 3  | AB 123456-123                   | 123456  | 1 | 6 | NULL |
+----+---------------------------------+---------+---+---+------+
| 4  | AB B0-23456.123                 | 0       | 1 | 1 | NULL |
+----+---------------------------------+---------+---+---+------+
| 4  | AB B0-23456.123                 | 23456   | 1 | 5 | -    |
+----+---------------------------------+---------+---+---+------+
| 5  | AB 1234 5678 9545 3214.123      | 1234    | 1 | 4 | NULL |
+----+---------------------------------+---------+---+---+------+
| 5  | AB 1234 5678 9545 3214.123      | 5678    | 1 | 4 |      |
+----+---------------------------------+---------+---+---+------+
| 5  | AB 1234 5678 9545 3214.123      | 9545    | 1 | 4 |      |
+----+---------------------------------+---------+---+---+------+
| 5  | AB 1234 5678 9545 3214.123      | 3214    | 1 | 4 |      |
+----+---------------------------------+---------+---+---+------+
| 6  | AB 123456 123                   | 123456  | 1 | 6 | NULL |
+----+---------------------------------+---------+---+---+------+
| 7  | AB.123456 123                   | 123456  | 1 | 6 | NULL |
+----+---------------------------------+---------+---+---+------+
| 8  | AB..123456 123                  | 123456  | 1 | 6 | NULL |
+----+---------------------------------+---------+---+---+------+
| 9  | AB..1C23456 123                 | 1C23456 | 0 | 7 | NULL |
+----+---------------------------------+---------+---+---+------+
| 10 | AB 1234 5678 954 3214-12345.123 | 1234    | 1 | 4 | NULL |
+----+---------------------------------+---------+---+---+------+
| 10 | AB 1234 5678 954 3214-12345.123 | 5678    | 1 | 4 |      |
+----+---------------------------------+---------+---+---+------+
| 10 | AB 1234 5678 954 3214-12345.123 | 954     | 1 | 3 |      |
+----+---------------------------------+---------+---+---+------+
| 10 | AB 1234 5678 954 3214-12345.123 | 3214    | 1 | 4 |      |
+----+---------------------------------+---------+---+---+------+
| 10 | AB 1234 5678 954 3214-12345.123 | 12345   | 1 | 5 | -    |
+----+---------------------------------+---------+---+---+------+

Предоставленное SELECT сгруппирует это по ID,YourString.Это означает: 1 строка на идентификатор.

Возвращенные столбцы - это столбцы группировки плюс большой вычисляемый.

Это коррелированный подзапрос .Он извлечет все строки для текущего идентификатора и обработает их.И его результат возвращается FOR XML PATH, что является уловкой для объединения всех результатов.

Сложная часть в WHERE: если после пробела с длиной есть хотя бы один числовой фрагмент1038 *, строка не будет включать этот и все последующие фрагменты.

Как получить элемент перед текущим элементом ?

Это снова коррелированный подзапрос , извлекающий элемент в ID-группе с FragmentIndex большим, чем текущий.

0 голосов
/ 11 октября 2018

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...