我在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
答案 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。
清理完当前值后,您可以使用相同的方法拦截新笔记,然后再将这些新笔记放入数据库。