SQL:内部连接两个大型表

SQL:内部连接两个大型表,sql,sql-server,sql-server-2008,query-optimization,inner-join,Sql,Sql Server,Sql Server 2008,Query Optimization,Inner Join,我有两个巨大的表,每个表大约有1亿条记录,我担心我需要在这两个表之间执行一个内部连接。现在,两个表都非常简单;以下是描述: 生物实体表: 生物实体ID(int) 名称(nvarchar 4000,尽管这是一个过度使用) 类型ID(int) EGM表(事实上,是批量导入操作产生的辅助表): EMGId(内部) PId(整数) 名称(nvarchar 4000,尽管这是一个过度使用) 类型ID(int) 上次修改(日期) 我需要获得一个匹配的名称,以便将BioEntityId与驻留在EGM表

我有两个巨大的表,每个表大约有1亿条记录,我担心我需要在这两个表之间执行一个内部连接。现在,两个表都非常简单;以下是描述:

生物实体表:

  • 生物实体ID(int)
  • 名称(nvarchar 4000,尽管这是一个过度使用)
  • 类型ID(int)
EGM表(事实上,是批量导入操作产生的辅助表):

  • EMGId(内部)
  • PId(整数)
  • 名称(nvarchar 4000,尽管这是一个过度使用)
  • 类型ID(int)
  • 上次修改(日期)
我需要获得一个匹配的名称,以便将BioEntityId与驻留在EGM表中的PId相关联。最初,我尝试使用一个内部联接来完成所有操作,但查询似乎耗时太长,数据库的日志文件(在简单恢复模式下)设法占用了所有可用的磁盘空间(当数据库占用18GB时,刚好超过200GB),等待两天后查询将失败,如果我没弄错的话。我设法阻止了日志的增长(现在只有33MB),但查询已经连续运行了6天,看起来不会很快停止

我在一台相当不错的计算机上运行它(4GB RAM,Core 2 Duo(E8400)3GHz,Windows Server 2008,SQL Server 2008),我注意到计算机偶尔会每隔30秒(给或拿)几秒钟发生一次堵塞。这使得它很难用于其他任何事情,这真的让我感到不安

现在,问题是:

 SELECT EGM.Name, BioEntity.BioEntityId INTO AUX
 FROM EGM INNER JOIN BioEntity 
 ON EGM.name LIKE BioEntity.Name AND EGM.TypeId = BioEntity.TypeId
我已经手动设置了一些索引;EGM和BioEntity都有一个包含TypeId和Name的非聚集覆盖指数。但是,该查询运行了五天,并且也没有结束,因此我尝试运行数据库调优顾问以使其正常工作。它建议删除我以前的索引,并创建统计信息和两个聚集索引(每个表上一个,只包含TypeId,我觉得这很奇怪,或者很愚蠢,但我还是尝试了一下)

它已经运行了6天了,我仍然不知道该怎么办。。。 有什么想法吗,伙计们?我怎样才能使它更快(或者,至少是有限的)

更新: -好的,我已经取消了查询并重新启动了服务器,以使操作系统重新启动并运行 -我正在使用您建议的更改重新运行工作流,特别是将nvarchar字段裁剪为更小的大小,并将“like”替换为“=”。这至少需要两个小时,所以我稍后会发布更多更新

更新2(格林威治时间2009年11月18日下午1点):
-估计的执行计划显示了67%的表扫描成本以及33%的哈希匹配。接下来是0%并行(这不是很奇怪吗?这是我第一次使用估计执行计划,但这一特殊事实让我大吃一惊),0%哈希匹配,更多0%并行,0%top,0%表插入,最后是另一个0%select into。似乎索引是垃圾,正如预期的那样,所以我将制作手动索引并丢弃垃圾建议索引

对于大型连接,有时明确选择
循环连接
会加快速度:

SELECT EGM.Name, BioEntity.BioEntityId INTO AUX
FROM EGM 
INNER LOOP JOIN BioEntity 
    ON EGM.name LIKE BioEntity.Name AND EGM.TypeId = BioEntity.TypeId
一如既往,公布您的预计执行计划可以帮助我们提供更好的答案

编辑:如果两个输入都已排序(应该是,带有覆盖索引),您可以尝试:


1亿张唱片是巨大的。我想说,要使用这么大的数据库,您需要一个专用的测试服务器。在执行类似的查询时,使用同一台机器做其他工作是不实际的


您的硬件功能相当强大,但要让这么大的连接正常运行,您需要更多的电源。8GB的四核系统将是一个良好的开端。除此之外,您还必须确保索引设置正确。

我可能会尝试删除“LIKE”操作符;因为您似乎没有进行任何通配符匹配。

我不是SQL调优专家,但在我所知道的任何数据库系统中,在VARCHAR字段上加入数亿行听起来都不是一个好主意


您可以尝试向每个表中添加一个整数列,并在NAME字段上计算一个哈希值,在引擎必须查看实际的VARCHAR数据之前,该哈希值应能将可能的匹配项设置为合理的数字。

根据建议,我将对名称进行哈希,以使联接更合理。如果可能的话,我将强烈考虑通过查找查找批处理中的ID分配,因为这将消除以后加入连接的必要性(并且潜在地重复执行这样低效的连接)。 我看到你在TypeID上有这个索引-如果这是有选择性的,这将非常有帮助。此外,将具有名称哈希的列添加到同一索引中:

SELECT EGM.Name
       ,BioEntity.BioEntityId
INTO AUX 
FROM EGM 
INNER JOIN BioEntity  
    ON EGM.TypeId = BioEntity.TypeId -- Hopefully a good index
    AND EGM.NameHash = BioEntity.NameHash -- Should be a very selective index now
    AND EGM.name LIKE BioEntity.Name

我可能提供的另一个建议是尝试获取数据的子集,而不是一次处理所有100m行来优化查询。这样,您就不必花费太多时间等待查询何时完成。然后你可以考虑检查查询执行计划,它也可以提供对手头问题的一些洞察力。

< P>因为你不要求DB做任何奇特的关系操作,所以你可以很容易地编写这个脚本。尝试导出这两个表(您能从备份中获得脱机副本吗?),而不是通过一个庞大但简单的查询杀死数据库

导出表后,编写一个脚本来执行这个简单的连接。它将花费大约相同的时间来执行,但不会杀死数据库

由于数据的大小和查询运行所需的时间长度,您不会经常这样做,因此脱机批处理是有意义的


对于脚本,您需要为较大的数据集编制索引,然后遍历较小的数据集并查找较大的数据集索引。运行它将是O(n*m)。

您有任何主键或索引吗
SELECT EGM.Name
       ,BioEntity.BioEntityId
INTO AUX 
FROM EGM 
INNER JOIN BioEntity  
    ON EGM.TypeId = BioEntity.TypeId -- Hopefully a good index
    AND EGM.NameHash = BioEntity.NameHash -- Should be a very selective index now
    AND EGM.name LIKE BioEntity.Name
CREATE TABLE BioEntity
 (
   BioEntityId  int
  ,Name         nvarchar(4000)
  ,TypeId       int
  ,NameLookup  AS checksum(Name) persisted
 )
CREATE clustered INDEX IX_BioEntity__Lookup on BioEntity (NameLookup, TypeId)
SELECT EGM.Name, BioEntity.BioEntityId INTO AUX
 FROM EGM INNER JOIN BioEntity 
 ON EGM.NameLookup = BioEntity.NameLookup
  and EGM.name = BioEntity.Name
  and EGM.TypeId = BioEntity.TypeId