使用重复句子识别SQL字符串中的唯一句子

时间:2018-03-08 19:30:44

标签: sql-server tsql parsing split varchar

我在SQL中有一个VARCHAR(MAX)字符串字段,在某些情况下,在LEN中包含最多100k个字符。让我们称之为[Notes_Comments]。

该字段的内容[Notes_Comments]是注释和注释,在多个实例中,这些注释和注释多次复制/粘贴。关于旧零件和新零件的开始/结束位置没有描述。标点符号也会受到影响。

我想弄清楚如何在[Notes_Comments]字段中识别唯一的句子或字符串/子字符串,从而获得纯粹的,重复数据删除的内容视图。

我构建了一个SplitString函数,并且能够开始解析2,3和5个单词短语,但它并没有真正让我到达我需要获得完整句子的位置。

示例[Notes_Comments]:

我想得到这个

10:46 AM谈到等等等等。他们说他们不知道我的意思12:46 PM构建一个SplitString函数并将单词解析成一个带有单词索引的表格1:46 PM好进展2:46 PM删除干扰词3:46 PM删除已知类别的词语4:46 PM试图弄清楚如何删除一个字符串字段的重复部分。备注有些重复的东西。现在增加了一些新东西。注释添加了一点。必须对多次出现的文本部分进行重复数据删除。备注还有一节。

从此

10:46 AM谈到等等等等。他们说他们不知道我的意思12:46 PM构建一个SplitString函数并将单词解析成一个带有单词索引的表格1:46 PM好进展删除噪音词3:46 PM删除已知类别的词语注意一些重复的东西 10:46 AM谈到等等等等等等等等等他们说他们不知道我的意思12:46 PM建立一个SplitString函数并将单词解析成一个带有单词索引的表格1:46 PM识别单词但不是完整字符串的良好进展2:46 PM删除噪音单词3:46 PM删除已知类别的单词下午4:46试图找出如何删除一个字符串字段的重复部分。备注有些重复的东西。现在增加了一些新东西。注释添加了一点。必须对多次出现的文本进行重复删除。 10:46 AM谈到等等等等等等等等他们说他们不知道我的意思12:46 PM构建了一个SplitString函数并将其解析为一个带有单词索引的表格1:46 PM识别单词但不是完整字符串的良好进展2:46 PM删除噪音单词3:46 PM删除已知类别的单词4:46 PM试图弄清楚如何删除重复的部分一个字符串字段。备注有些重复的东西。现在增加了一些新东西。注释添加了一点。必须对多次出现的文本部分进行重复数据删除。 备注另外一节。

这是我的SplitString函数:

CREATE FUNCTION dbo.SplitString 
(
    @str NVARCHAR(max), 
    @separator CHAR(1)
)
RETURNS TABLE
AS
RETURN (
WITH tokens(p, a, b) AS (
    SELECT 
        CAST(1 AS bigint), 
        CAST(1 AS bigint), 
        CHARINDEX(@separator, @str)
    UNION all
    SELECT
        p + 1, 
        b + 1, 
        CHARINDEX(@separator, @str, b + 1)
    FROM tokens
    WHERE b > 0
)
SELECT
    p-1 ItemIndex,
    SUBSTRING(
        @str, 
        a, 
        CASE WHEN b > 0 THEN b-a ELSE LEN(@str) END) 
    AS word
FROM tokens
);

GO

2 个答案:

答案 0 :(得分:1)

为了帮助你继续前进,我建议你去做#34; Notes"当前正在关闭的表格列,并创建一个新表格" EntityId"和"注意"。 EntityId是您要将注释关联到的父记录的EntityId。通过这种方式,您的实体与笔记之间存在一对多的关系。

这需要相当多的工作才能进行转换,但维护它会更容易,并且可以防止与您当前的问题类似的问题。

对于纯粹的SQL方法,我会先改变表关系。执行拆分,并将每条消息插入新表中。完成后,您可以编写一个单独的过程来识别重复项,并对除其中一个之外的所有文件执行删除操作。

我应该注意以下是一种应用方法。

如果您选择更新表关系,请先实施,然后重复数据删除将分为两步,但实施起来相对简单。

对于重复数据删除,您必须考虑到您说标点符号被击中或错过的事实。

我会分解你的时间戳的:?? AM和:?? PM语法。您必须确定语法,但这应该可以帮助您确定条目的开始和结束位置。将它们分成一个列表,在添加它时将每个分成Trim(),如果你根本不需要标点符号,请在"上进行替换。"和","以及您在添加到收藏集时不想要的其他标点字符。

现在您有两个选项,如果您使用表更新,您可以插入所有值,然后在sql或代码中过滤重复项。

或者您可以创建新列表,执行一些循环并仅向此新列表添加唯一项。如果您将单个列连接成一个字符串并更新数据库,或者如果您使用一对多设计,则为每个唯一条目执行插入。

如果要进行不太精确的匹配,请为每个子字符串构建一个哈希值。也许"时间戳" +" " +第一个X字符+最后Y个字符。然后比较你的哈希的平等,再保持一个。我推荐这种模式,因为它从您的示例中看出重复的注释具有重复的时间戳。如果不是只尝试前x和后y作为哈希。

我强烈建议您更新表结构,以便将来避免类似的麻烦。其余的是一点正则表达式,字符串拆分和比较。此外,如果您尝试使用散列进行调试,请尝试使用它来确保在将其提交到数据库之前不要过多地删除。

答案 1 :(得分:0)

如果要在代码中拆分字符串,则需要先将正则表达式放在一起。我会匹配子串":.. AM"或":.. PM"。只匹配":"考虑到这是一个音符字段,用户可以输入他们想要的任何内容,这似乎有点危险。

请注意这是未经测试的,但逻辑应该是合理的。您可能需要修改匹配对象访问器,并测试索引值是否正好分割到您想要的位置等。

首先将字段读入字符串。然后利用正则表达式查找字符串中的消息。如下所示。

string pattern = @":.?*M";
Regex rgx = new Regex(pattern, RegexOptions.None);
MatchCollection matches = rgx.Matches(input);

matches集合中的匹配对象包含找到匹配项的起始点的索引。您将要遍历它们并将子字符串提取为单个字符串。类似的东西:

List<string> allNotes = new List<string>();    

for(i = 0, i < matches.Count, i++)
{
    string nextNote = string.Empty;
    int currentIndex = matches[i].index - 2;
    int endIndex = -1;
    if(matches[i+1] != null)
    { 
        endIndex = matches[i + 1].index - 2;
    }

    if(endIndex != -1)
    {
        nextNote = substring(currentIndex, (endIndex - currentIndex);
    }
    else
    {
        nextNote = substring(currentIndex);
    }

    if(!string.IsNullOrEmpty(nextNote))
    {
        allNotes.Add(nextNote);
    }
}

这些子字符串应跨越时间戳的开头,通过下一个时间戳之前的字符。

然后,您可以执行其他功能来删除列表。 Somethinglike:

Dictionary <string, string> dedupedDict = new Dictionary<string, string>();

foreach(string note in allNotes)
{
    string noteHash = string.concat(note.substring(0, 6), note.substring(note.length - 7));

    if(!dedupedDict.Keys.Contains(noteHash))
    {   
        dedupedDict.Add(noteHash, note);
    }
}

return dedupedDict.Values().ToList();

您可能需要包含一些检查字符串的最小长度,以避免索引超出范围异常,和/或更改散列大小(我的示例应该是第一个和最后6个字符)。如果您认为标点符号有助于您的匹配,您可能还希望在标点上修剪()和/或替换(&#34;。&#34;,&#34;&#34;)。

然后使用已删除的备注列表填充新的CallNotes表。您也需要您的通话ID。

清理完当前值后,您可以使用相同的方法拦截新笔记,然后再将这些新笔记放入数据库。