SQL-запрос для извлечения чисел перед определенным словом

У меня есть такие данные:

String 1: 'Random Text 3 Random 568 Text 5.5 Test Random Text 345'
String 2: 'Random Text 3 Test Text Random'
String 3: 'Random Text 777 Random Text'

Ожидаемый результат:

String 1: '5.5'
String 2: '3'
String 3: Nothing should output

Числа, которые должны быть выведены, всегда появляются перед словом Test.

Я хотел бы, чтобы SQL был совместим с Microsoft SSMS.

SSMS — это среда IDE, а не СУБД. Какой механизм базы данных (и версию) вы используете?

Thom A 03.04.2023 19:48

Задавая вопрос, вы должны предоставить минимальный воспроизводимый пример: (1) DDL и выборочное заполнение данных, т. е. таблицы CREATE плюс операторы INSERT T-SQL. (2) Что вам нужно сделать, т. е. логика и ваш код попытаются реализовать ее в T-SQL. (3) Желаемый результат, основанный на примерных данных в # 1 выше. (4) Ваша версия SQL Server (SELECT @@version;).

Yitzhak Khabinsky 03.04.2023 20:03

Сообщить нам, что вы используете SSMS, это все равно, что сказать нам, что вы ездите на машине Sony, потому что это имя вы видите на приборной панели. Для кого-то нормально, но для механика тревожно.

Joel Coehoorn 03.04.2023 20:26

Но я вожу Sony @JoelCoehoorn.

Thom A 03.04.2023 20:42

@ThomA сомнительно. Это все еще концепт-кар.

Joel Coehoorn 03.04.2023 20:43

Это ирония, @JoelCoehoorn, но суть остается неизменной; Sony делает автомобили (сейчас). Может, перейти на Панасоник? ;)

Thom A 03.04.2023 20:44
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
2
6
109
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Учитывая, что шаблон (с числами перед «тестом» согласован во всей строке, вот sql для извлечения числа:

SELECT 
    CASE 
        WHEN CHARINDEX(' Test ', [String 1]) > 0 
        THEN SUBSTRING([String 1], CHARINDEX(' ', [String 1], CHARINDEX(' Test ', [String 1]))+1, CHARINDEX(' ', [String 1], CHARINDEX(' ', [String 1], CHARINDEX(' Test ', [String 1]))+1) - CHARINDEX(' ', [String 1], CHARINDEX(' Test ', [String 1]))-1)
        ELSE ''
    END AS [String 1],

    CASE 
        WHEN CHARINDEX(' Test ', [String 2]) > 0 
        THEN SUBSTRING([String 2], CHARINDEX(' ', [String 2])+1, CHARINDEX(' ', [String 2], CHARINDEX(' Test ', [String 2])) - CHARINDEX(' ', [String 2])-1)
        ELSE ''
    END AS [String 2],

    CASE 
        WHEN CHARINDEX(' Test ', [String 3]) > 0 
        THEN SUBSTRING([String 3], CHARINDEX(' ', [String 3])+1, CHARINDEX(' ', [String 3], CHARINDEX(' Test ', [String 3])) - CHARINDEX(' ', [String 3])-1)
        ELSE ''
    END AS [String 3]

Надеюсь, это помогло!

Пожалуйста, попробуйте следующее решение.

Он использует токенизацию через XML и XQuery.

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

  • Предложение CROSS APPLY создает XML для каждой строки.
  • Выражение XQuery FLWOR проверяет числовые значения, за которыми следует слово «испытание».
  • Переменная $pos содержит позицию для каждого слова.

Образец XML для ID=1

<root>
  <r>Random</r>
  <r>Text</r>
  <r>3</r>
  <r>Random</r>
  <r>568</r>
  <r>Text</r>
  <r>5.5</r>
  <r>Test</r>
  <r>Random</r>
  <r>Text</r>
  <r>345</r>
</root>

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, tokens VARCHAR(MAX));
INSERT @tbl (tokens) VALUES
('Random Text 3 Random 568 Text 5.5 Test Random Text 345'),
('Random Text 3 Test Text Random'),
('Random Text 777 Random Text');
-- DDL and sample data population, end

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

SELECT *
    , c.query('for $x in /root/r
        let $pos := count(/root/r[. << $x]) + 1
        return if (xs:decimal($x) instance of xs:decimal (: filter out non-digits :)
            and /root/r[$pos+1]/text() = "Test") then $x
         else ()').value('.','VARCHAR(MAX)') AS result
FROM @tbl AS t
    CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' + 
        REPLACE(tokens, @separator, ']]></r><r><![CDATA[') + 
        ']]></r></root>' AS XML)) AS t1(c)
ORDER BY ID;

Выход

ИДЕНТИФИКАТОР жетоны результат 1 Случайный текст 3 Случайный текст 568 Текст 5.5 Тест Случайный текст 345 5,5 2 Случайный текст 3 Тестовый текст Случайный 3 3 Случайный текст 777 Случайный текст

Просто еще один вариант с использованием немного JSON и оконной функции lead() over()

Пример

DECLARE @YourTable Table (SomeCol VARCHAR(150));
INSERT @YourTable VALUES
('Random Text 3 Random 568 Text 5.5 Test Random Text 345'),
('Random Text 3 Test Text Random'),
('Random Text 777 Random Text');

Select A.SomeCol
      ,B.RetVal
 From  @YourTable A
 Outer Apply (
                Select RetVal
                 From  (
                        Select RetVal = Value
                              ,NxtVal = lead(Value,1) over (order by [Key])
                         From  OpenJSON( '["'+replace(string_escape(SomeCol,'json'),' ','","')+'"]' )
                       ) B1
                 Where NxtVal='Test'
                   and try_convert(money,RetVal) is not null
             ) B

Полученные результаты

SomeCol                                                   RetVal
Random Text 3 Random 568 Text 5.5 Test Random Text 345    5.5
Random Text 3 Test Text Random                            3
Random Text 777 Random Text                               NULL

Пожалуйста, попробуйте для ('Random Text 777 Random Test'), отсутствует проверка предшествующего числового значения.

Yitzhak Khabinsky 03.04.2023 21:35

@YitzhakKhabinsky А, теперь понятно. Небольшая доработка. Добавлена ​​ловушка. Хорошо поймал.

John Cappelletti 03.04.2023 21:42

Хороший ответ, +1 с моей стороны!

Yitzhak Khabinsky 03.04.2023 22:03
Ответ принят как подходящий

Вам нужно создать скалярную функцию, подобную этой (я сделал это в SQL Server)

CREATE FUNCTION GetNumberBeforeStringTest
(
    @stringToParse varchar(100)
)
RETURNS VARCHAR(100)
AS
BEGIN
    DECLARE @testIndex INT = PATINDEX('%test%', @stringToParse);
    
    IF @testIndex = 0 RETURN NULL;

    DECLARE @s1 VARCHAR(100) = REVERSE(TRIM(SUBSTRING(@stringToParse, 0, @testIndex)))
    
    DECLARE @s2 VARCHAR(100) = REVERSE(TRIM(SUBSTRING(@s1, 0, CHARINDEX(' ', @s1))))

    IF TRY_CAST(@s2 AS decimal) IS NULL
        RETURN NULL;

    RETURN @s2;
END
GO

Вы используете вот так выберите dbo.GetNumberBeforeStringTest()

например выберите dbo.GetNumberBeforeStringTest('Случайный текст 3 Случайный текст 568 5.5 Тестовый случайный текст 345')

Результат: 5,5

Привет @9jaCoder Я заметил, что когда есть строка со скобками и плавающей запятой, функция не работает. Примером может быть: «Случайный текст (тест 3.9) Случайный текст». Не могли бы вы сказать мне, как отредактировать функцию, чтобы также иметь возможность извлекать число из строки в этом формате? Или это должен быть новый вопрос? Спасибо

Lloyd Thomas 19.04.2023 13:30

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