Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从T-SQL中的字符串中获取前n个句子?_Sql_Sql Server_Tsql - Fatal编程技术网

如何从T-SQL中的字符串中获取前n个句子?

如何从T-SQL中的字符串中获取前n个句子?,sql,sql-server,tsql,Sql,Sql Server,Tsql,我正在尝试使用完整描述字段填充我们的简短描述字段。基本上,我想将ShortDescription列设置为FullDescription列的前三句话 我知道如何在C语言中实现这一点,但在SQL查询中实现这一点有点困难。我不关心性能,因为这个查询只会运行一次来生成这个临时数据。所以,任何和所有的解决方案都会为我们找到 我的尝试: UPDATE Product SET ShortDescription = ( CASE WHEN (LEN(FullDes

我正在尝试使用完整描述字段填充我们的简短描述字段。基本上,我想将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;
不幸的是,上面的查询只得到第一句话。我似乎不知道如何找到第三个时期的战车。有人知道一种简单明了的方法来定位这个角色吗

另外,句点是识别句子的唯一方法,我的假设正确吗?我担心在一些罕见的情况下,句子中可能会出现小数,这会提供一些糟糕的描述,比如:这个产品很棒。它有很棒的功能。是2


非常感谢您的指导或反馈!谢谢大家!

我有一个TVF可能会有帮助。如果您不需要UDF,那么可以很容易地将代码移植到交叉应用程序中

我应该注意到。此分隔符是后跟空格的句点。刚才认为它不会捕获其他标点符号,例如

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',' ')

这太脏了,但是试试看

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

您可以使用一个真正的正则表达式,这使得这项任务更加容易。T-SQL本机不支持正则表达式,但您可以使用SQLCLR通过.NET访问它们,在这种情况下,它们可以直接绑定到UPDATE语句中。例如:

声明@Pattern nvarch4000= N’?:先生\女士\女士\资深女士\小博士\博士+?[!?]?:\s+${1,3}; 选择SQL.RegEx_MatchSimple4ktmp.[txt],@Pattern,1,NULL 从价值观出发!我想是两个。凌晨2点3分至3点12分,第四名。Y五, 不是第一个吗?第二个。”, 你好,我是日瓦戈医生。很高兴认识你!我是先生。真的吗?” tmptxt; 返回:

乌诺!我想是两个。凌晨2点3分至3点12分。 第一个?第二个。 你好,我是日瓦戈医生。很高兴认识你!我是先生。 一些注意事项:

您可以从多个位置获取SQLCLR正则表达式对象。预编译正则表达式函数的一个来源是我创建的SQLCLR库。免费版本包含大多数正则表达式函数,包括上面示例中使用的函数

正则表达式:

查找以下列任一字符结尾的句子:、、?和!。 在这种情况下,句子是:在找到句号、感叹号或问号之前,先生、女士、女士、老先生、小先生、博士或任何其他字符出现的最少次数。 可以处理包含少于3个句子的字符串,如示例所示 可以处理用作小数点或首字母缩写的句点,如示例所示 我想不出任何程序化的方式来知道,除了有一个清单可供核对之外,头衔的缩写,例如先生、女士、女士、博士等,一般来说都不是一句话的结尾。我以上面所示的模式提供了一个简短的列表,它可以很容易地扩展。但这有点容易,因为它们永远不会结束一句话。你也可以有一些测量单位的缩写,例如LBS,可以是句子的中间部分,也可以是结尾。 尝试以下简单技巧:

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;
说明:接受一个可选参数,指示开始搜索的索引。因此,通过嵌套其中三个调用,每个嵌套调用的结果将用作搜索下一个调用的起点。嵌套次数最多的调用将找到第一个句点,然后我们再向前搜索一个字符,搜索第二个,然后再次搜索第三个

如果你想要更多的三句话,这将不是正确的答案,如果没有案例结构中已有的保护条款,它将产生错误,但考虑到你的基本策略,它应该起作用


如果描述不是用英语会话写的,寻找句点的基本策略就不起作用;包含对源代码的引用或省略号的描述将破坏模型。您可以切换到搜索,但这要求这三句话与第四句话必须是同一段落的一部分。您需要升级到一个正则表达式搜索,该搜索匹配一个句点,后跟任何空格字符(包括换行符)或字符串的结尾,这样才能真正正常工作。

以下是使用游标的代码。您可以将计算简短描述的部分放入单独的函数中,以使其更具可读性

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;

以下用户定义函数将使用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;


如果您知道如何在C语言中实现这一点,那么您为什么不知道?您使用的是什么版本的sql server?如果您不关心性能,请使用游标。@smj-2014和2008R2@JosephYoung-因为我想学习如何在T-SQL中实现这一点…这一个假设“.”是唯一的句子分隔符,但是如果你已经有了C语言中一个句子的定义,我想把它放在这里应该不会有问题。很好,我尝试了这个,并对其他的句子定界符做了一些修改,这似乎很有效。谢谢你的时间!谢谢我很高兴你觉得它很有用。这太棒了!我喜欢没有第三方图书馆的需要。谢谢你,约翰@derekmx271很乐意帮忙。忍不住注意到这不是选定的答案。谢谢@KeithS!这就完成了任务,这就是我想要的。谢谢你的时间@derekmx271好吧,如果这是有效的,如果你这么好的话,我会打勾的-哈哈,是的,我真希望我能有更多的东西可以付出@没问题。很高兴它能工作:。直到现在我才忘记,我将尝试更新正则表达式,以获得一个常见标题缩写的列表—先生、太太、女士、博士等等—以减少边缘案例的数量。这仍然是一种愿望吗?虽然我相信这是可能的,但我不确定这有多容易/难做。但是如果你不需要/不想要它,那么我就不担心了。@derekmx271我知道你改变了接受的答案,这很好,但我仍然知道如何处理标题缩写,并用改进的正则表达式模式更新了我的答案。是的,你们两个的答案对我来说都很有用。我必须选择其中一个——我这样做只是因为John Cappelletti的答案不需要第三方库。像我这样的一些人并不总是能够利用这些LIB。如果我能把两个答案都标出来,我会的!您改进的模式有助于实现一些一次性价值。感谢您的解决方案和出色的库@德里克,欢迎你。我很欣赏想要两者兼而有之的感觉,我完全理解,在两个类似的好选择中,你会选择一个依赖性较小的。这对我来说是一个很好的机会来尝试一些新的正则表达式。如果在T-SQL中可以做一些事情,那么就这样做吧。我认为没有大量的代码是不可能的。约翰的好办法。很高兴你喜欢:-。肮脏是我喜欢我的问题!我会给这一个镜头,并报告回来,为未来的窥视寻找像这样的东西。非常感谢。谢谢爱德蒙·昆顿!