簡體   English   中英

如何從T-SQL中的字符串中獲取前n個句子?

[英]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功能,包括上面示例中使用的功能。

  • 正則表達式:

    • 查找以下列任何字符結尾的“句子”: . ? ,和!
    • 在這種情況下,“句子”是:“先生”,“女士”,“太太”,“老年人”,“小”,“博士”或任何其他人的出現次數最少在找到句號,感嘆號或問號之前的字符。
    • 可以處理包含少於3個“句子”的字符串(如示例所示)
    • 可以處理用作小數位或縮寫的句點(如示例所示)
  • 我想不出任何程序化的方式來知道標題的縮寫(例如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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM