У меня есть такие данные:
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.
Задавая вопрос, вы должны предоставить минимальный воспроизводимый пример: (1) DDL и выборочное заполнение данных, т. е. таблицы CREATE плюс операторы INSERT T-SQL. (2) Что вам нужно сделать, т. е. логика и ваш код попытаются реализовать ее в T-SQL. (3) Желаемый результат, основанный на примерных данных в # 1 выше. (4) Ваша версия SQL Server (SELECT @@version;).
Сообщить нам, что вы используете SSMS, это все равно, что сказать нам, что вы ездите на машине Sony, потому что это имя вы видите на приборной панели. Для кого-то нормально, но для механика тревожно.
Но я вожу Sony @JoelCoehoorn.
@ThomA сомнительно. Это все еще концепт-кар.
Это ирония, @JoelCoehoorn, но суть остается неизменной; Sony делает автомобили (сейчас). Может, перейти на Панасоник? ;)
Учитывая, что шаблон (с числами перед «тестом» согласован во всей строке, вот 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 для каждой строки.Образец 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;
Выход
Просто еще один вариант с использованием немного 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')
, отсутствует проверка предшествующего числового значения.
@YitzhakKhabinsky А, теперь понятно. Небольшая доработка. Добавлена ловушка. Хорошо поймал.
Хороший ответ, +1 с моей стороны!
Вам нужно создать скалярную функцию, подобную этой (я сделал это в 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) Случайный текст». Не могли бы вы сказать мне, как отредактировать функцию, чтобы также иметь возможность извлекать число из строки в этом формате? Или это должен быть новый вопрос? Спасибо
SSMS — это среда IDE, а не СУБД. Какой механизм базы данных (и версию) вы используете?