消除相似项的SQL查询

消除相似项的SQL查询,sql,algorithm,duplicates,combinations,Sql,Algorithm,Duplicates,Combinations,我正在处理SQL Server 2008中的一个问题 我有一个有六列的表: PK INT dOne SmallINT dTwo SmallINT dThree SmallINT dFour SmallINT dFiveSmallINT dSix SmallINT 这张表包含大约一百万条记录。可能值得注意的是,第n+1列中的值>第n列中的值,即97、98、99、120、135。我试图消除所有共有5位数字的行,忽略主键,即: 76, 89, 99, 102, 155, 122 11, 89, 99

我正在处理SQL Server 2008中的一个问题

我有一个有六列的表:

PK INT
dOne SmallINT
dTwo SmallINT
dThree SmallINT
dFour SmallINT
dFiveSmallINT
dSix SmallINT
这张表包含大约一百万条记录。可能值得注意的是,第n+1列中的值>第n列中的值,即97、98、99、120、135。我试图消除所有共有5位数字的行,忽略主键,即:

76, 89, 99, 102, 155, 122
11, 89, 99, 102, 155, 122
89, 99, 102, 155, 122, 130
在这种情况下,算法应从第一行开始,删除第二行和第三行,因为它们包含5个匹配的数字。第一行仍然存在

我曾试图强制解决方案,但只找到第一条记录的所有重复项需要25秒以上,这意味着处理整个表将需要。。。太长了,这应该是一个可重复的过程

我对SQL相当陌生,但这就是我提出的我提出了一些解决方案,但没有一个是足够的。。。这是最新的尝试:

我不会包括所有的代码,但我会解释方法,如果有帮助,我可以粘贴更多

将记录n的数字保存到变量中。从largeTable中选择与记录n有一个共同数字的所有记录

将所有选定的数字插入到一个匹配中,并将[matchingOne]与匹配的数字一起包括在内

从临时表中选择与记录n有一个共同数字的所有记录,其中“共同数字”!=[匹配]。将所有选定的数字插入twoMatch,并包括[matchingOne]和[matchingTwo]

重复此操作,直到插入fiveMatch。从largeTable中删除fiveMatch并移动到记录n+1

我在执行此解决方案时遇到问题。如何根据WHERE子句分配匹配变量

-- SELECT all records with ONE matching field:
INSERT INTO #oneMatch (ID_pk, dOne, dTwo, dThree, dFour, dFive, dSix, mOne)
SELECT ID_pk, dOne, dTwo, dThree, dFour, dFive, dSix
FROM dbo.BaseCombinationsExtended
WHERE  ( [dOne] IN (@dOne, @dTwo, @dThree, @dFour, @dFive, @dSix) **mOne = dOne?
      OR [dTwo] IN (@dOne, @dTwo, @dThree, @dFour, @dFive, @dSix) **mOne = dTwo?
      OR [dTwo] IN (@dOne, @dTwo, @dThree, @dFour, @dFive, @dSix) **mOne = dThree?
...
      OR [dSix] IN (@dOne, @dTwo, @dThree, @dFour, @dFive, @dSix) **mOne = dSix?
    )
我可以用六个查询来“伪造”上面的内容,但是效率太低了


对不起,描述得太长了。如果您能提供任何帮助,我们将不胜感激,因为这个问题已经困扰我一段时间了……

编辑-根据优化器的不同,以下方法可能比N平方性能更好。如果所有5列都被索引,那么每行只需要6次索引查找,仍然是N*logN。不过,这看起来确实有点傻

您可以根据5个匹配项的所有排列生成where条件:因此要删除的记录将由以下公式给出:

SELECT * FROM SillyTable ToDelete WHERE EXISTS
(
    SELECT PK From SillyTable Duplicate 
    WHERE (   (
            (Duplicate.dOne=ToDelete.dOne) 
            AND (Duplicate.dTwo=ToDelete.dTwo) 
            AND (Duplicate.dThree=ToDelete.dThree)
            AND (Duplicate.dFour=ToDelete.dFour)
            AND (Duplicate.dFive=ToDelete.dFive)
        ) OR (
            (Duplicate.dOne=ToDelete.dTwo) 
            AND (Duplicate.dTwo=ToDelete.dThree) 
            AND (Duplicate.dThree=ToDelete.dFour)
            AND (Duplicate.dFour=ToDelete.dFive)
            AND (Duplicate.dFive=ToDelete.dSix)
        ) OR (
            (Duplicate.dTwo=ToDelete.dOne) 
            AND (Duplicate.dThree=ToDelete.dTwo) 
            AND (Duplicate.dFour=ToDelete.dThree)
            AND (Duplicate.dFive=ToDelete.dFour)
            AND (Duplicate.dSix=ToDelete.dFive)
        ) OR (
            (Duplicate.dTwo=ToDelete.dTwo) 
            AND (Duplicate.dThree=ToDelete.dThree) 
            AND (Duplicate.dFour=ToDelete.dFour)
            AND (Duplicate.dFive=ToDelete.dFive)
            AND (Duplicate.dSix=ToDelete.dSix)
        ) ...                       

这将覆盖所有36个组合,在连接的每一侧,6个可能的列中有一个不匹配,因此6*6给出了所有可能的结果。我会编写代码来生成它,因为它需要大量的输入,如果明天你想要6场比赛中的4场,但我想你可以手工编写。

我不能保证性能,但你可以试试这个。我要做的第一件事是将数据放入更规范化的结构中

CREATE TABLE dbo.Test_Sets_Normalized (my_id INT NOT NULL, c SMALLINT NOT NULL)
GO

INSERT INTO dbo.Test_Sets_Normalized (my_id, c)
SELECT my_id, c1 FROM dbo.Test_Sets UNION ALL
SELECT my_id, c2 FROM dbo.Test_Sets UNION ALL
SELECT my_id, c3 FROM dbo.Test_Sets UNION ALL
SELECT my_id, c4 FROM dbo.Test_Sets UNION ALL
SELECT my_id, c5 FROM dbo.Test_Sets UNION ALL
SELECT my_id, c6 FROM dbo.Test_Sets
GO

SELECT DISTINCT
    T2.my_id
FROM
    (SELECT DISTINCT my_id FROM dbo.Test_Sets_Normalized) T1
INNER JOIN (SELECT DISTINCT my_id FROM dbo.Test_Sets_Normalized) T2 ON T2.my_id > T1.my_id
WHERE
    (
    SELECT
        COUNT(*)
    FROM
        dbo.Test_Sets_Normalized T3
    INNER JOIN dbo.Test_Sets_Normalized T4 ON
        T4.my_id = T2.my_id AND
        T4.c = T3.c
    WHERE
        T3.my_id = T1.my_id) >= 5
这样你就可以得到你需要的身份证了。一旦您确认它做了您想要的事情,您就可以连接回原始表并按ID删除


可能有一个改进的地方,不需要明显的。我会再仔细考虑一下。

除非我遗漏了什么,否则这应该会产生正确的结果

declare @T table 
(
  PK INT identity primary key,
  dOne SmallINT,
  dTwo SmallINT,
  dThree SmallINT,
  dFour SmallINT,
  dFive SmallINT,
  dSix SmallINT
)

insert into @T values
(76, 89, 99, 102, 155, 122),
(11, 89, 99, 102, 155, 122),
(89, 99, 102, 155, 122, 130)

;with q1(PK, d1, d2, d3, d4, d5) as
(
  select PK, dTwo, dThree, dFour, dFive, dSix
  from @T
  union all
  select PK, dOne, dThree, dFour, dFive, dSix
  from @T
  union all
  select PK, dOne, dTwo, dFour, dFive, dSix
  from @T
  union all
  select PK, dOne, dTwo, dThree, dFive, dSix
  from @T
  union all
  select PK, dOne, dTwo, dThree, dFour, dSix
  from @T
  union all
  select PK, dOne, dTwo, dThree, dFour, dFive
  from @T
),
q2 as
(
  select PK,
         row_number() over(partition by d1, d2, d3, d4, d5 order by PK) as rn
  from q1
),
q3 as
(
  select PK
  from q2
  where rn = 1
  group by PK
  having count(*) = 6   
)
select T.*
from @T as T
  inner join q3 as Q
    on T.PK = Q.PK  

这是一个正常化的好例子。你能为我详细说明一下吗?RDB不是我的强项,所以请原谅我的无知,当只有一个表而没有关系时,我如何规范化?如果您将同一行上的字段相互比较,则必须存在关系。这正是问题所在。应该有多个表或至少两个表具有关系。或者甚至有一个表具有复合PK。您可以从dbo.Test_集合获取my_id,而无需DistinctHanks Tom和psr提供建议。很抱歉,我无法在工作中立即实施这些措施,但一旦我有空,我会尝试一下,并让你知道进展如何。再次感谢。我一直在玩类似的想法,我想我可以做出承诺,表现将绝对糟糕。@gbn-真的。我想在新表中充分展示它,以防他能够规范化数据库。你解决了它。转化为一个简单的登录问题。我没看见。干得好。@user-谢谢。它使用了这样一个事实,即在从左到右的列中,值是按递增的值排序的。但我不确定这是OP真正想要的算法。这将级联删除。对于像这样的三行,1,2,3,4,5,6,1,2,3,4,4,5,7,1,2,3,4,7,8只保留第1行,但如果我们先删除与第1行的所有匹配,然后再删除第2行等。。。第1行和第3行都将保留。如果需要的话,恐怕我们又回到了^2。关于级联删除的观点很好。这就是我在最后一次对这个问题的评论中想问的问题。很抱歉反应太慢。我刚完成这项工作,工作很忙。这个解决方案非常好,但不幸的是,我确实需要执行“顺序”删除。我想我们是根据之前的一些建议和建议得到的。。。通过分区 将表分成25K的块,并处理每个分区的前10K,在90分钟内只运行一个集,从而大大提高了11K删除的性能。处理每组25K后,用新的25K组重复该过程。最后,我们对剩下的记录运行一次