[英]How to get the first n sentences from a string in T-SQL?
我正在嘗試使用“完整描述”字段填充我們的“簡短描述”字段。 基本上,我想將ShortDescription
列設置為等於FullDescription
列中的前三個句子。
我知道如何在C#中執行此操作,但我在SQL查詢中完成它時遇到了一些麻煩。 我不關心性能 - 因為此查詢只會運行一次以生成此臨時數據。 因此,任何和所有解決方案都可以找到我們的工作!
我的嘗試:
UPDATE Product
SET ShortDescription = (
CASE
WHEN (LEN(FullDescription) - LEN(REPLACE(FullDescription, '.', ''))) >= 3 THEN
(
SELECT
LEFT(str, pos)
FROM (
SELECT
FullDescription AS str,
CHARINDEX('.', FullDescription) AS pos
) x
)
ELSE
FullDescription
END
)
WHERE FullDescription IS NOT NULL;
不幸的是,上面的查詢只得到第一句話。 我似乎無法弄清楚如何找到第三期的CHARINDEX。 有人知道找到這個角色的簡潔方法嗎?
另外,我是否認為句號確實是識別句子的唯一方法? 我擔心(在極少數情況下),句子中可能會有小數,這會提供一些可怕的描述,例如:“這個產品很棒。它有很棒的功能。它是2。”......
非常感謝任何方向或反饋! 謝謝!
您可以使用真正的正則表達式,這使得此任務更容易。 T-SQL本身不支持正則表達式,但您可以使用SQLCLR通過.NET訪問它們,在這種情況下,它們可以直接綁定到UPDATE
語句中。 例如:
DECLARE @Pattern NVARCHAR(4000) =
N'((?:Mr\.|Ms\.|Mrs\.|Sr\.|Jr\.|Dr\.|.)+?[.!?](?:\s+|$)){1,3}';
SELECT SQL#.RegEx_MatchSimple4k(tmp.[txt], @Pattern, 1, NULL)
FROM (VALUES (N'Sentence uno! Two, I think. Only 2.3 till 3:12 A.M. Numero 4. Y five.'),
(N'First one? Second one.'),
(N'Hello, this is Dr. Zhivago. Nice to meet you! I''m Mr. Mister. Really?')
) tmp(txt);
返回:
Sentence uno! Two, I think. Only 2.3 till 3:12 A.M.
First one? Second one.
Hello, this is Dr. Zhivago. Nice to meet you! I'm Mr. Mister.
一些說明:
您可以從多個位置獲取SQLCLR RegEx對象。 預編譯的RegEx函數的一個來源是我創建的SQL# SQLCLR庫。 免費版本包含大多數RegEx功能,包括上面示例中使用的功能。
正則表達式:
.
, ?
,和!
。 我想不出任何程序化的方式來知道標題的縮寫(例如Mr.
, Ms.
, Mrs.
, Dr.
等) 一般不是句子的結尾,除了有一個列表要檢查。 我提供了上面顯示的模式的簡短列表,它可以很容易地擴展。 但這些都有點容易,因為他們永遠不會結束一句話。 您還可以使用測量單位(例如lbs.
)的縮寫,可以在句子的中間或結尾處。
我有一個可能有幫助的TVF。 如果您不想使用UDF,則可以輕松地將代碼移植到Cross Apply中。
我應該注意。 此分隔符是一個句點,后跟一個空格。 剛才認為它不會捕獲其他標點符號(我,e。!)
Declare @String varchar(max) ='This is sentance one. This is sentance two. This is Sentence 3. This is sentecne 4.'
Declare @YourTable table (ID int,FullDescription varchar(max))
Insert Into @YourTable values
(1,'Some sentance with a decimal like 25.26 is OK. Sentance number two. Sentance number 3. Sentance number 4 would not be included.'),
(2,'I know how I would do this in C#. I am having a little trouble getting it done in my SQL query. I don''t care about performance. This query will only be ran one time.')
Select A.*
,B.ShortDescription
From @YourTable A
Cross Apply (Select ShortDescription=concat(Pos1,'. ',Pos2,'. ',Pos3,'.') From [dbo].[udf-Str-Parse-Row](A.FullDescription,'. ')) B
如果需要,UDF
CREATE FUNCTION [dbo].[udf-Str-Parse-Row] (@String varchar(max),@Delimiter varchar(10))
Returns Table
As
Return (
Select Pos1 = xDim.value('/x[1]','varchar(max)')
,Pos2 = xDim.value('/x[2]','varchar(max)')
,Pos3 = xDim.value('/x[3]','varchar(max)')
,Pos4 = xDim.value('/x[4]','varchar(max)')
,Pos5 = xDim.value('/x[5]','varchar(max)')
,Pos6 = xDim.value('/x[6]','varchar(max)')
,Pos7 = xDim.value('/x[7]','varchar(max)')
,Pos8 = xDim.value('/x[8]','varchar(max)')
,Pos9 = xDim.value('/x[9]','varchar(max)')
From (Select Cast('<x>' + Replace(@String,@Delimiter,'</x><x>')+'</x>' as XML) as xDim) A
)
--Select * from [dbo].[udf-Str-Parse-Row]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-Row]('John Cappelletti',' ')
嘗試這個簡單的技巧:
UPDATE Product
SET ShortDescription = (
CASE
WHEN (LEN(FullDescription) - LEN(REPLACE(FullDescription, '.', ''))) >= 3 THEN
(
SELECT
LEFT(str, pos)
FROM (
SELECT
FullDescription AS str,
CHARINDEX('.', FullDescription,
CHARINDEX('.', FullDescription,
CHARINDEX('.', FullDescription)+1)+1) AS pos
) x
)
ELSE
FullDescription
END
)
WHERE FullDescription IS NOT NULL;
說明: T-SQL CHARINDEX函數接受一個可選參數,指示開始搜索的索引。 因此,通過嵌套其中三個調用,每個嵌套調用的結果將用作搜索下一個調用的起始點。 最嵌套的調用將找到第一個句點,然后我們將超過該字符的一個字符前進並搜索第二個字符,然后再搜索第三個字符。
如果你想要超過三個句子,這將不是正確的答案,並且如果沒有CASE結構已經存在的保護條款,它將產生錯誤,但它應該根據你的基本策略起作用。
如果描述不是用會話英語寫的話,那么尋找句號的基本策略效果不佳; 包含對源代碼的引用或省略號的描述將破壞模型。 您可以切換到搜索". "
,但這需要三個句子成為同一段落的一部分以及第四個句子。 您需要升級到匹配句點的正則表達式搜索,后跟任何空白字符(包括換行符)或字符串末尾,以使其真正有效。
這是使用CURSOR的代碼。 (您可以將計算ShortDescription的部分放入單獨的函數中,以使其更具可讀性)。
DECLARE @fd NVARCHAR(500);
DECLARE @tmp NVARCHAR(500);
DECLARE @sd NVARCHAR(500);
DECLARE @count INT;
DECLARE @pos INT;
DECLARE update_cursor CURSOR FOR SELECT FullDescription FROM Product;
OPEN update_cursor;
FETCH NEXT FROM update_cursor INTO @fd;
WHILE (@@FETCH_STATUS= 0)
BEGIN
SET @sd = '';
IF (LEN(@fd) - LEN(REPLACE(@fd,'.','')) < 3)
BEGIN
SET @sd = @fd;
END;
ELSE
BEGIN
SET @tmp = @fd;
SET @count = 1;
SET @pos = 0;
WHILE (@count < 4)
BEGIN
SET @pos = CHARINDEX('.',@tmp, @pos);
SET @sd = CONCAT(@sd, SUBSTRING(@tmp,1,@pos));
SET @tmp = SUBSTRING(@tmp,@pos+1,LEN(@tmp));
SET @count = @count +1;
END;
END;
UPDATE Product
SET ShortDescription = @sd
WHERE CURRENT OF update_cursor;
FETCH NEXT FROM update_cursor INTO @fd;
END;
CLOSE update_cursor;
DEALLOCATE update_cursor;
這太臟了,試試看。
SET NOCOUNT ON;
DECLARE @t TABLE (i INT, sd NVARCHAR(MAX), ld NVARCHAR(MAX));
INSERT INTO @t VALUES (1, '', '1s. 2s. 3s. 4.');
INSERT INTO @t VALUES (2, '', '4s. 5s. 6s. 7.');
INSERT INTO @t VALUES (3, '', '6s. 7.');
DECLARE @sd NVARCHAR(MAX)
DECLARE @ld NVARCHAR(MAX)
DECLARE @i INT, @a INT;
DECLARE @ss TABLE (s NVARCHAR(MAX));
DECLARE @s NVARCHAR(MAX);
WHILE (SELECT COUNT(*) FROM @t) > 0
BEGIN
SELECT TOP 1 @i = i, @sd = sd, @ld = ld FROM @t;
DELETE FROM @ss;
SET @a = 1
WHILE LEN(@ld) > 0
BEGIN
IF @a > 3
BREAK;
SET @s = LEFT(@ld, CHARINDEX('.', @ld));
INSERT INTO @ss VALUES (@s);
SET @ld = REPLACE(@ld, @s, '');
SET @a = @a + 1;
END
WHILE (SELECT COUNT(*) FROM @ss) > 0
BEGIN
SELECT TOP 1 @s = s FROM @ss;
SET @sd = @sd + @s
DELETE FROM @ss WHERE s = @s
END
PRINT @sd;
DELETE FROM @t WHERE i = @i;
END
以下用戶定義的函數將使用SQL Server的PATINDEX
函數來匹配未跟隨數字的句點的第一個匹配項。 這將避免句子在句子中的某處包含小數值的情況。
CREATE FUNCTION GetFirstThreeSentences
(
@fullText NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE @finalPosition INT = 0;
DECLARE @patternIndex INT = 0;
DECLARE @textLenght INT = LEN(@fullText);
-- Get first sentence.
DECLARE @currentSentencePosition INT = PATINDEX('%.[^0-9]%', @fullText);
SET @finalPosition = @currentSentencePosition;
DECLARE @remainingText NVARCHAR(MAX) = RIGHT(@fullText, @textLenght - @finalPosition);
-- Get second sentence.
SET @currentSentencePosition = PATINDEX('%.[^0-9]%', @remainingText);
SET @finalPosition = @finalPosition + @currentSentencePosition;
SET @remainingText = RIGHT(@fullText, @textLenght - @finalPosition);
-- Get third sentence.
SET @currentSentencePosition = PATINDEX('%.[^0-9]%', @remainingText);
SET @finalPosition = @finalPosition + @currentSentencePosition;
SET @remainingText = RIGHT(@fullText, @textLenght - @finalPosition);
-- Return first three setences
RETURN LEFT(@fullText, @finalPosition)
END
GO
現在可以在查詢中調用該函數:
UPDATE Product
SET ShortDescription = dbo.GetFirstThreeSentences(FullDescription)
WHERE FullDescription IS NOT NULL;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.