Как очистить строку и извлечь числовые постфиксы в T-SQL - PullRequest
1 голос
/ 23 мая 2019

У меня есть строка, состоящая из имени, и в большинстве случаев она имеет постфикс с одним или двумя числами в конце. Этот номер-постфикс должен быть отрезан от имени. Одно число представляет статус и должно быть извлечено. Если есть два числа, это второй справа, если есть один номер, то это первый справа. Эти цифры разделены подчеркиванием. Подчеркивания также могут быть использованы в имени. Результатом должен быть столбец с чистым именем и извлеченным статусом.

Я пытался решить проблему с помощью стандартных строковых функций, таких как Substring, Charindex, Patindex, LEN и son on. Но мой подход стал очень громоздким и его трудно поддерживать. Интересно, есть ли элегантное решение с обычными возможностями SQl-Server (если возможно, без установки дополнений для регулярных выражений).

SELECT _data.myStr
    -- , ... AS clearname  /*String cleaned from number_postfixes*/
    -- , ... AS Status     /*second number from the right*/
FROM (
    SELECT 'tree_leafs_offer_2_1' AS myStr  --clearname: tree_leafs_offer; cut off: _2_1; extracted status: 2
        UNION
    SELECT 'tree_leafs_offer_2_10' AS myStr --clearname: tree_leafs_offer_2_10; cut off: _2_10; extracted status: 2
        UNION
    SELECT 'tree_leafs_offer_2_2' AS myStr  --clearname: tree_leafs_offer; cut off: _2_2; extracted status: 2
        UNION
    SELECT 'tree_leafs_offer_1150_1' AS myStr   --clearname: tree_leafs_offer; cut off: _1150_1; extracted status: 1150
        UNION
    SELECT 'tree_leafs_offer_1150_10' AS myStr  --clearname: tree_leafs_offer; cut off: _1150_10; extracted status: 1150
        UNION
    SELECT 'builder_bundle_less_xl_1' AS myStr  --clearname: builder_bundle_less_xl; cut off: _1; extracted status: 1
        UNION
    SELECT 'builder_bundle_less_xl_10' AS myStr --clearname: builder_bundle_less_xl; cut off: _10; extracted status: 10
        UNION
    SELECT 'static_components_wolves_10_4' AS myStr --clearname: static_components_wolves; cut off: _10_4; extracted status: 4
        UNION
    SELECT 'coke_0_boring_components_bundle_grant_1' AS myStr   --clearname: oke_0_boring_components_bundle_grant; cut off: _1; extracted status: 1
        UNION
    SELECT 'coke_0_soccer18_end_1_4h_101' AS myStr  --clearname: coke_0_soccer18_end_1_4h; cut off: _101; extracted status: 101
        UNION
    SELECT 'coke_0_late_downsell_bundle_high_114' AS myStr  --clearname: coke_0_late_downsell_bundle_high; cut off: _114; extracted status: 114
        UNION
    SELECT 'itembundle_mine_bundle_small' AS myStr  --clearname: itembundle_mine_bundle_small; cut off: <nothing>; extracted status: NULL
) AS _data
As-Is Result:
-----------------
myStr:
---------------------------------------
builder_bundle_less_xl_1
builder_bundle_less_xl_10
coke_0_boring_components_bundle_grant_1
coke_0_late_downsell_bundle_high_114
coke_0_soccer18_end_1_4h_101
itembundle_mine_bundle_small
static_components_wolves_10_4
tree_leafs_offer_1150_1
tree_leafs_offer_1150_10
tree_leafs_offer_2_1
tree_leafs_offer_2_10
tree_leafs_offer_2_2

To-Be Result (two new columns):
-------------------
clearname:                              |Status
----------------------------------------------
builder_bundle_less_xl                  |   1
builder_bundle_less_xl                  |  10
coke_0_boring_components_bundle_grant   |   1
coke_0_late_downsell_bundle_high        | 114
coke_0_soccer18_end_1_4h                | 101
itembundle_mine_bundle_small            |NULL
static_components_wolves                |  10
tree_leafs_offer                        |1150
tree_leafs_offer                        |1150
tree_leafs_offer                        |   2
tree_leafs_offer                        |   2
tree_leafs_offer                        |   2

Ответы [ 2 ]

3 голосов
/ 23 мая 2019

Если честно: этот формат ужасен!Если это не однократное действие , вы действительно должны попытаться изменить это до , вам придется иметь дело с этим.

Но - если вам нужно придерживаться этого - вы можете попробовать:

РЕДАКТИРОВАТЬ : исправлено неверное вычисление позиции статуса ...

DECLARE  @tbl TABLE(ID INT IDENTITY,myStr VARCHAR(1000));
INSERT INTO @tbl VALUES
 ('tree_leafs_offer_2_1')
,('tree_leafs_offer_2_10')
,('tree_leafs_offer_2_2')
,('tree_leafs_offer_1150_1')
,('tree_leafs_offer_1150_10')
,('builder_bundle_less_xl_1')
,('builder_bundle_less_xl_10')
,('static_components_wolves_10_4')
,('coke_0_boring_components_bundle_grant_1')
,('coke_0_soccer18_end_1_4h_101')
,('coke_0_late_downsell_bundle_high_114')
,('itembundle_mine_bundle_small');

Запрос

WITH cte AS
(
    SELECT t.ID
          ,t.myStr 
            ,A.[key] AS Position
            ,A.[value] AS WordFragment
            ,B.CastedToInt
    FROM @tbl t
    CROSS APPLY OPENJSON(N'["' + REPLACE(t.myStr,'_','","') + '"]') A
    CROSS APPLY(SELECT TRY_CAST(A.[value] AS INT)) B(CastedToInt)
) 
SELECT ID
      ,myStr
        ,STUFF(
        (SELECT CONCAT('_',cte2.WordFragment)
        FROM cte cte2
        WHERE cte2.ID=cte.ID
            AND cte2.Position<=A.PositionHighestNonInt
        ORDER BY cte2.Position
        FOR XML PATH('')
        ),1,1,'') AS ClearName
        ,(SELECT cte3.CastedToInt FROM cte cte3 WHERE cte3.ID=cte.ID AND cte3.Position=A.PositionHighestNonInt+1) AS [Status]
FROM cte
CROSS APPLY (
                 SELECT ISNULL(MAX(x.Position),1000) 
                 FROM cte x 
                 WHERE x.ID=cte.ID AND x.CastedToInt IS NULL
             ) A(PositionHighestNonInt)
GROUP BY ID,myStr,PositionHighestNonInt;

Результат

+----+---------------------------------------+--------+
| ID | ClearName                             | Status |
+----+---------------------------------------+--------+
| 1  | tree_leafs_offer                      | 2      |
+----+---------------------------------------+--------+
| 2  | tree_leafs_offer                      | 2      |
+----+---------------------------------------+--------+
| 3  | tree_leafs_offer                      | 2      |
+----+---------------------------------------+--------+
| 4  | tree_leafs_offer                      | 1150   |
+----+---------------------------------------+--------+
| 5  | tree_leafs_offer                      | 1150   |
+----+---------------------------------------+--------+
| 6  | builder_bundle_less_xl                | 1      |
+----+---------------------------------------+--------+
| 7  | builder_bundle_less_xl                | 10     |
+----+---------------------------------------+--------+
| 8  | static_components_wolves              | 10     |
+----+---------------------------------------+--------+
| 9  | coke_0_boring_components_bundle_grant | 1      |
+----+---------------------------------------+--------+
| 10 | coke_0_soccer18_end_1_4h              | 101    |
+----+---------------------------------------+--------+
| 11 | coke_0_late_downsell_bundle_high      | 114    |
+----+---------------------------------------+--------+
| 12 | itembundle_mine_bundle_small          | NULL   |
+----+---------------------------------------+--------+

Идея:

  • Предоставление ваших данных в макететаблица
  • Используйте трюк с OPENJSON, чтобы разбить строку и найти части, которые можно привести к INT.
  • Найдите самый высокий не-int фрагмент.Следующим индексом будет Status
  • С v2017 вы можете использовать STRING_AGG, но с v2016 мы должны использовать трюк на основе XML для объединения всех фрагментов до [Status].
1 голос
/ 23 мая 2019

Один из возможных подходов - использовать замену строк и JSON возможности SQL Server 2016+.Каждая строка инвертируется и преобразуется в действительный массив JSON ('tree_leafs_offer_2_1' преобразуется, например, в '["1","2","reffo","sfael","eert"]').Затем вы можете легко проверить, являются ли первый и второй элементы действительными числами, используя JSON_VALUE(<json_array>, '$[0]'), JSON_VALUE(<json_array>, '$[1]') и TRY_CONVERT().Это будет работать, если у вас есть максимум два числа справа.

Ввод:

CREATE TABLE #Data (
   myStr varchar(max)
)
INSERT INTO #Data 
   (MyStr)
VALUES   
   ('tree_leafs_offer_2_1'),
   ('tree_leafs_offer_2_10'),
   ('tree_leafs_offer_2_2'),
   ('tree_leafs_offer_1150_1'),
   ('tree_leafs_offer_1150_10'),
   ('builder_bundle_less_xl_1'),
   ('builder_bundle_less_xl_10'),
   ('static_components_wolves_10_4'),
   ('coke_0_boring_components_bundle_grant_1'),
   ('coke_0_soccer18_end_1_4h_101'),
   ('coke_0_late_downsell_bundle_high_114'),
   ('itembundle_mine_bundle_small')

T-SQL:

SELECT 
   LEFT(myStr, LEN(myStr) - CHARINDEX('_', REVERSE(myStr))) as ClearName,
   REVERSE(LEFT(REVERSE(myStr), CHARINDEX('_', REVERSE(myStr)) - 1)) AS Status
FROM (
   SELECT 
      CASE 
         WHEN 
            TRY_CONVERT(int, REVERSE(JSON_VALUE(CONCAT('["', REPLACE(STRING_ESCAPE(REVERSE(MyStr), 'json'), '_', '","'), '"]'), '$[1]'))) IS NULL AND
            TRY_CONVERT(int, REVERSE(JSON_VALUE(CONCAT('["', REPLACE(STRING_ESCAPE(REVERSE(MyStr), 'json'), '_', '","'), '"]'), '$[0]'))) IS NULL
            THEN CONCAT(myStr, '_0') 
         WHEN 
            TRY_CONVERT(int, REVERSE(JSON_VALUE(CONCAT('["', REPLACE(STRING_ESCAPE(REVERSE(MyStr), 'json'), '_', '","'), '"]'), '$[1]'))) IS NULL AND 
            TRY_CONVERT(int, REVERSE(JSON_VALUE(CONCAT('["', REPLACE(STRING_ESCAPE(REVERSE(MyStr), 'json'), '_', '","'), '"]'), '$[0]'))) IS NOT NULL
            THEN MyStr 
         ELSE LEFT(myStr, LEN(myStr) - CHARINDEX('_', REVERSE(myStr)))
      END AS myStr      
   FROM #Data
) fixed
ORDER BY MyStr

Выход:

----------------------------------------------
ClearName                               Status
----------------------------------------------
builder_bundle_less_xl                  1
builder_bundle_less_xl                  10
coke_0_boring_components_bundle_grant   1
coke_0_late_downsell_bundle_high        114
coke_0_soccer18_end_1_4h                101
itembundle_mine_bundle_small            0
static_components_wolves                10
tree_leafs_offer                        1150
tree_leafs_offer                        1150
tree_leafs_offer                        2
tree_leafs_offer                        2
tree_leafs_offer                        2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...