Tsql 查找精确的FK匹配

Tsql 查找精确的FK匹配,tsql,sql-server-2008-r2,Tsql,Sql Server 2008 R2,拥有非常大的表(超过2亿行) sID int,wordID int(主键sID,wordID) 要查找具有完全相同wordID的sID(并且没有附加项) 对于一个ID超过100字的sID,精确匹配的几率会下降,因此愿意将其限制在100 (但我想去1000) 如果这是学校,sID是班级,wordID是学生。 然后我想找到学生完全相同的班级 sID,wordID 1,1 1,2 1,3 2,2 2,3 3,1 3,4 5,1 5,2 6,2 6,3 7,1 7,2 8,1 8,1 sID 6和2具有

拥有非常大的表(超过2亿行)
sID int,wordID int(主键sID,wordID)

要查找具有完全相同wordID的sID(并且没有附加项)
对于一个ID超过100字的sID,精确匹配的几率会下降,因此愿意将其限制在100
(但我想去1000)

如果这是学校,sID是班级,wordID是学生。
然后我想找到学生完全相同的班级

sID,wordID
1,1
1,2
1,3
2,2
2,3
3,1
3,4
5,1
5,2
6,2
6,3
7,1
7,2
8,1
8,1

sID 6和2具有完全相同的wordID
sID 5、7和8具有完全相同的wordID

这就是我目前所拥有的
我想删除两个delete#temp3_sID1_sID2,并在上面的插入中处理它
但我会尝试任何想法
这不像您可以轻松创建一个包含2亿行的表来进行测试

  drop table #temp_sID_wordCount
  drop table #temp_count_wordID_sID 
  drop table #temp3_wordID_sID_forThatCount
  drop table #temp3_sID1_sID2
  drop table #temp3_sID1_sID2_keep
  create table #temp_sID_wordCount  (sID int primary key, ccount int not null)
  create table #temp_count_wordID_sID  (ccount int not null, wordID int not null, sID int not null, primary key (ccount, wordID, sID)) 
  create table #temp3_wordID_sID_forThatCount  (wordID int not null, sID int not null, primary key(wordID, sID))
  create table #temp3_sID1_sID2_keep  (sID1 int not null, sID2 int not null, primary key(sID1, sID2))
  create table #temp3_sID1_sID2  (sID1 int not null, sID2 int not null, primary key(sID1, sID2))
  insert into #temp_sID_wordCount 
  select sID, count(*) as ccount 
   FROM [FTSindexWordOnce] with (nolock)
   group by sID 
   order by sID;
  select count(*) from #temp_sID_wordCount where ccount <= 100;  -- 701,966
  truncate table #temp_count_wordID_sID
  insert into #temp_count_wordID_sID 
  select #temp_sID_wordCount.ccount, [FTSindexWordOnce].wordID, [FTSindexWordOnce].sID 
    from #temp_sID_wordCount
    join [FTSindexWordOnce] with (nolock) 
      on [FTSindexWordOnce].sID = #temp_sID_wordCount.sID
     and ccount >= 1 and ccount <= 10
   order by #temp_sID_wordCount.ccount, [FTSindexWordOnce].wordID, [FTSindexWordOnce].sID;
  select count(*) from #temp_sID_wordCount;  -- 34,860,090

    truncate table #temp3_sID1_sID2_keep
    declare cur cursor for 
    select top 10 ccount from #temp_count_wordID_sID group by ccount order by ccount

    open cur
    declare @count int, @sIDcur int
    fetch next from cur into @count
    while (@@FETCH_STATUS = 0)
    begin
      --print (@count)
      --select count(*), @count from #temp_sID_wordCount where #temp_sID_wordCount.ccount = @count
      truncate table #temp3_wordID_sID_forThatCount
      truncate table #temp3_sID1_sID2

      -- wordID and sID for that unique word count 
      -- they can only be exact if they have the same word count
      insert into #temp3_wordID_sID_forThatCount 
      select       #temp_count_wordID_sID.wordID
                 , #temp_count_wordID_sID.sID
      from #temp_count_wordID_sID
      where #temp_count_wordID_sID.ccount = @count
      order by  #temp_count_wordID_sID.wordID, #temp_count_wordID_sID.sID 

      -- select count(*) from  #temp3_wordID_sID_forThatCount

      -- this has some duplicates 
      -- sID1 is the group 
      insert into #temp3_sID1_sID2
      select w1.sID, w2.sID
        from #temp3_wordID_sID_forThatCount as w1 with (nolock)
        join #temp3_wordID_sID_forThatCount as w2 with (nolock)
          on w1.wordID = w2.wordID
         and w1.sID <= w2.sID         
       group by w1.sID, w2.sID
       having count(*) = @count
       order by w1.sID, w2.sID

      -- get rid of the goups of 1      
      delete #temp3_sID1_sID2  
      where  sID1 in (select sID1 from #temp3_sID1_sID2 group by sID1 having count(*) = 1)

      -- get rid of the double dips         
      delete #temp3_sID1_sID2
       where #temp3_sID1_sID2.sID1 in 
              (select distinct s1del.sID1 -- these are the double dips 
                from #temp3_sID1_sID2 as s1base with (nolock) 
                join #temp3_sID1_sID2 as s1del with (nolock)
                  on s1del.sID1 > s1base.sID1 
                 and s1Del.sID1 = s1base.sID2)

      insert into #temp3_sID1_sID2_keep      
      select #temp3_sID1_sID2.sID1
           , #temp3_sID1_sID2.sID2
        from #temp3_sID1_sID2 with (nolock)
        order by #temp3_sID1_sID2.sID1, #temp3_sID1_sID2.sID2

    fetch next from cur into  @count
    end
    close cur
    deallocate cur

 select *
 FROM #temp3_sID1_sID2_keep  with (nolock)
 order by 1,2
drop table#temp_sID_wordCount
下拉表#临时(临时)计数(字ID)sID
将表格#temp3_wordID_sID_用于计数
升降台#temp3_sID1_sID2
升降台#临时3 sID1 sID2 keep
创建表#temp_sID_wordCount(sID int主键,ccount int非空)
创建表#temp#count#wordID#sID(ccount int not null,wordID int not null,sID int not null,主键(ccount,wordID,sID))
创建表#temp3_wordID_sID_forThatCount(wordID int不为null,sID int不为null,主键(wordID,sID))
创建表#temp3_sID1_sID2_keep(sID1 int不为null,sID2 int不为null,主键(sID1,sID2))
创建表#temp3_sID1_sID2(sID1 int不为null,sID2 int不为null,主键(sID1,sID2))
插入到#temp#u sID#u wordCount中
选择sID,将(*)计数为ccount
从[FTSindexWordOnce]到(nolock)
按sID分组
按sID排序;

从#temp_sID_wordCount中选择count(*),其中ccount=1和ccount,如我所见,任务是找到相等的子集

首先,我们可以找到一对相等的子集:

;with tmp1 as (select sID, cnt = count(wordID) from [Table] group by sID)
select s1.sID, s2.sID
from tmp1 s1
    cross join tmp1 s2
    cross apply (
        select count(1)
        from [Table] d1
            join [Table] d2 on d2.wordID = d1.wordID
        where d1.sID = s1.sID and d2.sID = s2.sID
    ) c(cnt)
where s1.cnt = s2.cnt
    and s1.sID > s2.sID
    and s1.cnt = c.cnt
输出为:

sID        sID
----------- -----------
6           2
7           5
8           5
8           7
然后,如有必要,可以将对组合成组:

sID         gNum
----------- -----------
2           1
6           1
5           2
7           2
8           2
请参阅下面SQLFIDLE示例中的详细信息


另一种方法是为每个子集数据计算哈希函数:

;with a as (
    select distinct sID from [Table]
)
select sID,
    hashbytes('sha1', (
        select cast(wordID as varchar(10)) + '|'
        from [Table]
        where sID = a.sID
        order by wordID
        for xml path('')))
from a
然后可以根据散列值对子集进行分组


最后一个测试在我的机器上花费了不到一分钟的时间来获取大约1000万行的测试数据(每个测试数据行的sID值为20k,最高可达1k个wordID)。您还可以通过排除没有wordID计数匹配的SID来优化它。

因此,正如我所见,任务是找到相等的子集

首先,我们可以找到一对相等的子集:

;with tmp1 as (select sID, cnt = count(wordID) from [Table] group by sID)
select s1.sID, s2.sID
from tmp1 s1
    cross join tmp1 s2
    cross apply (
        select count(1)
        from [Table] d1
            join [Table] d2 on d2.wordID = d1.wordID
        where d1.sID = s1.sID and d2.sID = s2.sID
    ) c(cnt)
where s1.cnt = s2.cnt
    and s1.sID > s2.sID
    and s1.cnt = c.cnt
输出为:

sID        sID
----------- -----------
6           2
7           5
8           5
8           7
然后,如有必要,可以将对组合成组:

sID         gNum
----------- -----------
2           1
6           1
5           2
7           2
8           2
请参阅下面SQLFIDLE示例中的详细信息


另一种方法是为每个子集数据计算哈希函数:

;with a as (
    select distinct sID from [Table]
)
select sID,
    hashbytes('sha1', (
        select cast(wordID as varchar(10)) + '|'
        from [Table]
        where sID = a.sID
        order by wordID
        for xml path('')))
from a
然后可以根据散列值对子集进行分组


最后一个测试在我的机器上花费了不到一分钟的时间来获取大约1000万行的测试数据(每个测试数据行的sID值为20k,最高可达1k个wordID)。您还可以通过排除没有wordID计数匹配的SID来优化它。

它可以处理示例数据,但在大表上,它没有在7小时内完成,因此我无法验证。这种方法很棒,但我有一个小问题。我需要去最大字数为600,否则我得到的字符串或二进制数据将被截断错误。如果我去掉hashbytes,我仍然会得到错误,因此它在for XML中。但在限制为600的情况下,它在不到两分钟的时间内运行。@Blam,我记得我遇到了这个问题,因为xml的一些内部
限制已经存在,您可以尝试将SID分成50-200-500-1000k行的批。另外,
hashbytes
输入限制为8000字节。所以,你可能需要把它的输入分成8k个部分,超过8k的部分,然后计算每个部分的哈希值和concat哈希值。实际上我可以在.NET中完成。这就是我在.NET中解析和加载的数据。它可以处理样本数据,但在大表上,它没有在7小时内完成,所以我无法验证。方法很棒,但我有一个小问题。我需要去最大字数为600,否则我得到的字符串或二进制数据将被截断错误。如果我去掉hashbytes,我仍然会得到错误,因此它在for XML中。但在限制为600的情况下,它在不到两分钟的时间内运行。@Blam,我记得我遇到了这个问题,因为xml的一些内部
限制已经存在,您可以尝试将SID分成50-200-500-1000k行的批。另外,
hashbytes
输入限制为8000字节。所以,你可能需要把它的输入分成8k个部分,超过8k的部分,然后计算每个部分的哈希值和concat哈希值。实际上我可以在.NET中完成。这就是我在.NET中解析和加载的数据。