如何从T-SQL中的字符串中获取前n个句子?
我正在尝试使用完整描述字段填充我们的简短描述字段。基本上,我想将ShortDescription列设置为FullDescription列的前三句话 我知道如何在C语言中实现这一点,但在SQL查询中实现这一点有点困难。我不关心性能,因为这个查询只会运行一次来生成这个临时数据。所以,任何和所有的解决方案都会为我们找到 我的尝试:如何从T-SQL中的字符串中获取前n个句子?,sql,sql-server,tsql,Sql,Sql Server,Tsql,我正在尝试使用完整描述字段填充我们的简短描述字段。基本上,我想将ShortDescription列设置为FullDescription列的前三句话 我知道如何在C语言中实现这一点,但在SQL查询中实现这一点有点困难。我不关心性能,因为这个查询只会运行一次来生成这个临时数据。所以,任何和所有的解决方案都会为我们找到 我的尝试: UPDATE Product SET ShortDescription = ( CASE WHEN (LEN(FullDes
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中可以做一些事情,那么就这样做吧。我认为没有大量的代码是不可能的。约翰的好办法。很高兴你喜欢:-。肮脏是我喜欢我的问题!我会给这一个镜头,并报告回来,为未来的窥视寻找像这样的东西。非常感谢。谢谢爱德蒙·昆顿!