Подстрока SQL из столбца строк

У меня есть таблица, содержащая около 100 000 статей.

Каждая статья имеет описание.

короткий пример:

АРТИКЕЛЬНР ОПИСАНИЕ 104009400031900 S235JRG2C+C EN 10277/10 80 x 8 мм Заглушка Flachstahl 104009800002950 Верксаттест по EN 10204 105009400092360 EN 10060 Inox Rund Ø 28 мм WNr1.4034 Warmgefertigt 105009400068571 90 мм WNr1.4404 Warmgefertigt, EN 10060, Geschmiedet 105009400004420 WNr1.4301 Inox Vierkantstahl 6 x 6 мм EN 10278, бланкгезоген 105009400008800 WNr1.4301 Лента Inox, гладкая, 25 x 4 мм, Breitentol. 25 м 105009400068600 WNr1.4112 Inox Rund 8,00 мм согласно EN 10088-3 + A+C+SL, имеется 105009400068700 WNr1.4301 Inox Rund 36,00 мм по EN 10088-3, гладкий, T 105009400068800 WNr1.4104 Inox Rund 25,00 мм по EN 10088-3, гладкий, T 105009400010035 WNr1.4404 Нержавеющая сталь Vierkantstahl 30 x 30 мм, гезоген EN 10278

Ни у кого нет одинакового формата.

Что я хотел бы сделать, так это извлечь «НОРМУ» из описания.

Итак, я хотел бы получить следующий результат

АРТИКЕЛЬНР НОРМА 104009400031900 ЕН 10277/10 104009800002950 ЭН 10204 105009400092360 ЭН 10060 105009400068571 ЭН 10060 105009400004420 ЭН 10278 105009400008800 105009400068600 ЭН 10088-3 105009400068700 ЭН 10088-3 105009400068800 ЭН 10088-3 105009400010035 ЭН 10278

какую систему sql и какую версию вы используете?

Bending Rodriguez 14.03.2024 08:29

Microsoft SQL Server Standard (64-разрядная версия) v11.0.7507.2 У меня просто нет прав администратора для изменения COMPATIBILITY_LEVEL

ouboma 14.03.2024 08:31

@ouboma, ты не меняешь схему таблицы. Я думаю, просто выберите не требуется менять разрешение COMPATIBILITY_LEVEL. Поправьте меня, я ошибаюсь.

502_Geek 14.03.2024 08:44

@502_Geek, когда я использую string_split(), я получаю эту ошибку: «STRING_SPLIT» не является распознаваемым именем встроенной функции, которую можно решить, изменив уровень совместимости. чего я действительно не могу сделать!

ouboma 14.03.2024 08:48

К вашему сведению, поддержка версии 11 (2012 г.) полностью закончилась почти 2 года назад, @ouboma. Вам действительно нужно как можно скорее получить последнюю и поддерживаемую версию.

Thom A 14.03.2024 09:41

Я думаю, вы забыли упомянуть, как следует извлекать эту «Норму». Вы сидите на формате статьи, можно ли предположить, что это всегда EN<пробел>номер<что-то><запятая> или нет и т.д.

siggemannen 14.03.2024 09:44

Что касается данных, то кажется, что их следует нормализовать еще до того, как они попадут в вашу таблицу. Если разные части DESCRIPTION означают разные вещи, храните их отдельно.

Thom A 14.03.2024 09:45

@ThomA Я полностью с тобой согласен. Но я не отвечаю за получение новой версии SQL и не могу контролировать нормализацию данных. Мне придется работать с тем, что у меня есть. Но я с вами полностью согласен!

ouboma 14.03.2024 09:50

@siggemannen нет. иногда это EN<пробел>номер<что-то>номер, иногда EN<пробел>номер, иногда EN<что-то>номер<что-то>номер... это действительно варьируется. но EN всегда заглавная и после нее всегда идут цифры.

ouboma 14.03.2024 09:52

Так как же узнать, когда значение прекратится, @ouboma. У вас есть одна строка с EN 10277/10 80, откуда вы знаете, что 80 не является частью нужного вам значения?

Thom A 14.03.2024 10:00

Похоже, вам нужен список этих значений «НОРМ», а затем вы можете проверить, соответствует ли значение LIKE вашему DESCRIPTION столбцу, поскольку нет шаблона, который можно было бы найти. Или, опять же, исправьте свой дизайн (это настоящее решение).

Thom A 14.03.2024 10:02

@ThomA хороший вопрос. Это ВСЕГДА EN + первый набор цифр. Итак, в вашем случае это будет EN 10277/10, второй пробел после EN - это место, где норма заканчивается.

ouboma 14.03.2024 10:03

Давайте продолжим обсуждение в чате.

ouboma 14.03.2024 10:04
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
13
128
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Это может быть подсказка:

SELECT 
    ARTIKELNR,
    CASE 
        WHEN CHARINDEX('EN ', DESCRIPTION) > 0 THEN
            SUBSTRING(DESCRIPTION, CHARINDEX('EN ', DESCRIPTION), PATINDEX('%[0-9/.]%', SUBSTRING(DESCRIPTION, CHARINDEX('EN ', DESCRIPTION), LEN(DESCRIPTION))))
        ELSE
            ''
    END AS NORM
FROM 
    YourTableName

В этом запросе предполагается, что «EN», за которым следуют цифры, точки или косые черты, однозначно идентифицирует начало NORM.

да, но для первой статьи: 104009400031900 он вернул только EN 1 как норму. однако мне нужен EN 10277/10. есть идеи, как этого добиться? Спасибо

ouboma 14.03.2024 08:45

Думаю, это может решить вашу проблему %[0-9]+[-/]?[0-9]*%

502_Geek 14.03.2024 09:02

@502_Geek Не совсем. оба скрипта дают пробел для artikelnr: 105009400068800

ouboma 14.03.2024 09:10

В самых последних версиях SQL Server это на самом деле довольно просто благодаря встроенным функциям. ВЫ можете использовать STRING_SPLIT, чтобы разделить значение и убедиться, что вы возвращаете порядковые номера, чтобы вы могли использовать LEAD или LAG для получения следующего/последнего значения с разделителями и фильтрации по вашим значениям EN. Наконец, вы можете использовать TRIM, чтобы удалить конечные запятые:

WITH CTE AS(
    SELECT V.ARTIKELNR,
           V.DESCRIPTION,
           SS.value,
           LEAD(SS.value) OVER (PARTITION BY V.ARTIKELNR ORDER BY SS.ordinal) AS NextValue
    FROM (VALUES(104009400031900,N'S235JRG2C+C EN 10277/10 80 x 8 mm Blanker Flachstahl'),
                (104009800002950,N'Werksattest nach EN 10204'),
                (105009400092360,N'EN 10060 Inox Rund Ø 28 mm WNr1.4034 warmgefertigt'),
                (105009400068571,N'90 mm WNr1.4404 warmgefertigt, EN 10060, geschmiedet'),
                (105009400004420,N'WNr1.4301 Inox Vierkantstahl 6 x 6 mm EN 10278, blankgezogen'),
                (105009400008800,N'WNr1.4301 Inox Band geschnitten 25 x 4 mm, Breitentol. 25 m'),
                (105009400068600,N'WNr1.4112 Inox Rund 8.00 mm nach EN 10088-3 + A+C+SL, gesch'),
                (105009400068700,N'WNr1.4301 Inox Rund 36.00 mm nach EN 10088-3, geschliffen, T'),
                (105009400068800,N'WNr1.4104 Inox Rund 25.00 mm nach EN 10088-3, geschliffen, T'),
                (105009400010035,N'WNr1.4404 Inox Vierkantstahl 30 x 30 mm, gezogen EN 10278'))V(ARTIKELNR,DESCRIPTION)
         CROSS APPLY STRING_SPLIT(V.DESCRIPTION,' ',1) SS)
SELECT C.ARTIKELNR,
       TRIM(',' FROM CONCAT_WS(' ',C.value,C.NextValue)) AS Norm
FROM CTE C
WHERE C.value = 'EN';

db<>рабочий пример

В 2012 году (напоминаем, в 2022 году это вообще перестало поддерживаться) немного сложнее. Вы можете заменить STRING_SPLIT другим не встроенным разделителем, который возвращает порядковый номер, например DelimitedN4KSplit_LEAD (N4K, поскольку ваши строки выглядят как nvarchar). У вас по-прежнему есть доступ к LEAD, но нет TRIM, поэтому вам придется использовать REPLACE и RTRIM. Это дает вам что-то вроде этого:

WITH CTE AS(
    SELECT V.ARTIKELNR,
           V.DESCRIPTION,
           DS.item,
           LEAD(DS.Item) OVER (PARTITION BY V.ARTIKELNR ORDER BY DS.ItemNumber) AS nextitem
    FROM (VALUES(104009400031900,N'S235JRG2C+C EN 10277/10 80 x 8 mm Blanker Flachstahl'),
                (104009800002950,N'Werksattest nach EN 10204'),
                (105009400092360,N'EN 10060 Inox Rund Ø 28 mm WNr1.4034 warmgefertigt'),
                (105009400068571,N'90 mm WNr1.4404 warmgefertigt, EN 10060, geschmiedet'),
                (105009400004420,N'WNr1.4301 Inox Vierkantstahl 6 x 6 mm EN 10278, blankgezogen'),
                (105009400008800,N'WNr1.4301 Inox Band geschnitten 25 x 4 mm, Breitentol. 25 m'),
                (105009400068600,N'WNr1.4112 Inox Rund 8.00 mm nach EN 10088-3 + A+C+SL, gesch'),
                (105009400068700,N'WNr1.4301 Inox Rund 36.00 mm nach EN 10088-3, geschliffen, T'),
                (105009400068800,N'WNr1.4104 Inox Rund 25.00 mm nach EN 10088-3, geschliffen, T'),
                (105009400010035,N'WNr1.4404 Inox Vierkantstahl 30 x 30 mm, gezogen EN 10278'))V(ARTIKELNR,DESCRIPTION)
         CROSS APPLY fn.DelimitedSplitN4K_LEAD(V.DESCRIPTION,' ') DS)
SELECT C.ARTIKELNR,
       RTRIM(REPLACE(CONCAT(C.item,' ',C.nextitem),',',' ')) AS Norm
FROM CTE C
WHERE C.item = 'EN';

спасибо, но DelimitedN4KSplit_LEAD не поддерживается

ouboma 14.03.2024 10:45

Что значит «не поддерживается», @ouboma? ВЫ сказали, что используете SQL Server v11.0.7507.2, то есть 2012 года выпуска; DelimitedN4KSplit_LEAD работает в 2012 году. Совместимость вашей базы данных ниже, чем 110?

Thom A 14.03.2024 10:46

Если вы используете более низкий уровень совместимости, @ouboma, вы можете использовать DelimitedSpitN4k, если ваш уровень 100. Если он 90 или ниже, вам действительно нужно поговорить со своим администратором базы данных; это уровни совместимости SQL Server 2005! Вам также следовало упомянуть в вопросе, что мы были ограничены совместимостью с SQL Server 2005; некоторые люди больше не имеют доступа к такой среде.

Thom A 14.03.2024 10:54

совместимость базы данных — 110, но когда я запускаю ваш код, первая ошибка, которую я получаю: «CONCAT_WS» не распознается как имя интегрированной функции.

ouboma 14.03.2024 10:54

Ага, я почему-то думал, что CONCAT_WS был добавлен в 2012 году, это был 2017 год. Ну, тогда просто переключись на CONCAT, @ouboma ; это несложное изменение (я надеялся, что вы с этим справитесь).

Thom A 14.03.2024 10:57

тогда я получаю эту ошибку: Недопустимое имя объекта «fn.DelimitedSplitN4K_LEAD».

ouboma 14.03.2024 10:58

Значит, ты еще не создал объект, @ouboma ; эта ошибка говорит вам о проблеме.

Thom A 14.03.2024 11:01
Ответ принят как подходящий

Попробуйте следующее решение, использующее функции SQL Server XML и XQuery.

Он будет работать, начиная с SQL Server 2012.

Примечательные моменты:

  • CROSS APPLY токенизирует столбец DESCRIPTION как XML.
  • Метод XQuery .query() ищет токен «EN» с помощью выражения FLWOR и следующий токен с помощью предиката XPath ...r[position()=($pos, $pos + 1)].
  • Метод XQuery .value() дает нам желаемую выходную строку.
  • Функция REPLACE() удаляет необязательную конечную запятую.

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, ARTIKELNR CHAR(15), DESCRIPTION NVARCHAR(1024));
INSERT INTO @tbl (ARTIKELNR, DESCRIPTION) VALUES
(104009400031770,N'725528 MOTORKONSOLE KM35 EN SENKKLAPPFENSTER WICSKY 3'), -- Exception case, no digits after EN
(104009400031900,N'S235JRG2C+C EN10088-3 80 x 8 mm Blanker Flachstahl'),    -- Exception case, no space after EN
(104009400031900,N'S235JRG2C+C EN 10277/10 80 x 8 mm Blanker Flachstahl'),
(104009800002950,N'Werksattest nach EN 10204'),
(105009400092360,N'EN 10060 Inox Rund Ø 28 mm WNr1.4034 warmgefertigt'),
(105009400068571,N'90 mm WNr1.4404 warmgefertigt, EN 10060, geschmiedet'),
(105009400004420,N'WNr1.4301 Inox Vierkantstahl 6 x 6 mm EN 10278, blankgezogen'),
(105009400008800,N'WNr1.4301 Inox Band geschnitten 25 x 4 mm, Breitentol. 25 m'),
(105009400068600,N'WNr1.4112 Inox Rund 8.00 mm nach EN 10088-3 + A+C+SL, gesch'),
(105009400068700,N'WNr1.4301 Inox Rund 36.00 mm nach EN 10088-3, geschliffen, T'),
(105009400068800,N'WNr1.4104 Inox Rund 25.00 mm nach EN 10088-3, geschliffen, T'),
(105009400010035,N'WNr1.4404 Inox Vierkantstahl 30 x 30 mm, gezogen EN 10278');
-- DDL and sample data population, end

DECLARE @separator CHAR(1) = SPACE(1);

SELECT *
    , REPLACE(c.query('
          for $x in /root/r[text() = "EN"]
          let $pos := count(root/r[. << $x]) + 1
          return if (xs:int(substring((/root/r[$pos + 1]/text())[1],1,5)) instance of xs:int) then
                data(/root/r[position()=($pos, $pos + 1)])
            else data(/root/r[$pos])
        ').value('text()[1]', 'NVARCHAR(30)')
        ,',', '') AS NORM
FROM @tbl AS t
CROSS APPLY (SELECT PATINDEX('%EN[0-9][0-9][0-9][0-9][0-9]%', DESCRIPTION)) AS t2(pos)  -- to handle Exception cases
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' + 
    REPLACE(IIF(pos > 0, STUFF(DESCRIPTION,pos + 2,0, SPACE(1)), DESCRIPTION), @separator, ']]></r><r><![CDATA[') + 
    --REPLACE(DESCRIPTION, @separator, ']]></r><r><![CDATA[') + 
    ']]></r></root>' AS XML)) AS t1(c)
ORDER BY ID;

Выход

ИДЕНТИФИКАТОР АРТИКЕЛЬНР ОПИСАНИЕ НОРМА 1 104009400031770 725528 МОТОРКОНСОЛЬ KM35 EN SENKKLAPPFENSTER WICSKY 3 RU 2 104009400031900 S235JRG2C+C EN10088-3 80 x 8 мм Заглушка Flachstahl ЭН 10088-3 3 104009400031900 S235JRG2C+C EN 10277/10 80 x 8 мм Заглушка Flachstahl ЕН 10277/10 4 104009800002950 Верксаттест по EN 10204 ЭН 10204 5 105009400092360 EN 10060 Inox Rund Ø 28 мм WNr1.4034 Warmgefertigt ЭН 10060 6 105009400068571 90 мм WNr1.4404 Warmgefertigt, EN 10060, Geschmiedet ЭН 10060 7 105009400004420 WNr1.4301 Inox Vierkantstahl 6 x 6 мм EN 10278, бланкгезоген ЭН 10278 8 105009400008800 WNr1.4301 Лента Inox, гладкая, 25 x 4 мм, Breitentol. 25 м НУЛЕВОЙ 9 105009400068600 WNr1.4112 Inox Rund 8,00 мм согласно EN 10088-3 + A+C+SL, имеется ЭН 10088-3 10 105009400068700 WNr1.4301 Inox Rund 36,00 мм по EN 10088-3, гладкий, T ЭН 10088-3 11 105009400068800 WNr1.4104 Inox Rund 25,00 мм по EN 10088-3, гладкий, T ЭН 10088-3 12 105009400010035 WNr1.4404 Нержавеющая сталь Vierkantstahl 30 x 30 мм, гезоген EN 10278 ЭН 10278

и снова здравствуйте. если у меня есть описание типа: (725528 MOTORKONSOLE KM35 EN SENKKLAPPFENSTER WICSKY 3), результатом будет (EN SENKKLAPPFENSTER), что неверно, есть ли обходной путь для этого? ценить это!

ouboma 17.04.2024 09:48

Каким будет правильный результат для такого сценария?

Yitzhak Khabinsky 17.04.2024 14:06

это должно быть только EN. Меня интересует только EN, за которым следуют несколько цифр.

ouboma 17.04.2024 14:19

Я скорректировал ответ. Проверьте это.

Yitzhak Khabinsky 17.04.2024 15:16

Он проверяет первые 5 символов как цифры в следующем токене.

Yitzhak Khabinsky 17.04.2024 15:22

Я протестировал несколько случаев, и все работает отлично. Если что-то появится, я сообщу вам. Кроме того, знаете ли вы какой-нибудь учебник или веб-сайт, на котором объясняется этот метод корня, значения и запроса? интересно исследовать! еще раз спасибо.

ouboma 17.04.2024 15:35

Итак, начнем: Learn.microsoft.com/en-us/sql/xquery/…

Yitzhak Khabinsky 17.04.2024 15:46

Также, пожалуйста, проголосуйте за мое предложение в Microsoft: Feedback.azure.com/d365community/idea/…

Yitzhak Khabinsky 17.04.2024 15:51

Другие вопросы по теме