在数百万行上优化自联接

时间:2012-12-29 22:36:22

标签: sql sql-server optimization sql-server-2012

我有一个表,它是我的SQL Server 2012数据库(annonsid, annonsid2)中对象的链接表。此表用于创建三角形甚至矩形链,以查看谁可以与谁交换。

这是我在表Matching_IDs上使用的查询,其中包含150万行,使用此查询生成了1400万条可能的链:

SELECT COUNT(*)
FROM Matching_IDs AS m
  INNER JOIN Matching_IDs AS m2
     ON m.annonsid2 = m2.annonsid
  INNER JOIN Matching_IDs AS m3
     ON m2.annonsid2 = m3.annonsid
       AND m.annonsid = m3.annonsid2

我必须提高性能,花费1秒或更短的时间,有更快的方法吗?查询在我的计算机上大约需要1分钟。我通常使用WHERE m.annonsid=x,但它只需要相同的时间,因为它必须经历所有可能的组合。

更新:最新的查询计划

|--Compute Scalar(DEFINE:([Expr1006]=CONVERT_IMPLICIT(int,[globalagg1011],0)))
   |--Stream Aggregate(DEFINE:([globalagg1011]=SUM([partialagg1010])))
        |--Parallelism(Gather Streams)
             |--Stream Aggregate(DEFINE:([partialagg1010]=Count(*)))
                  |--Hash Match(Inner Join, HASH:([m2].[annonsid2], [m2].[annonsid])=([m3].[annonsid], [m].[annonsid2]), RESIDUAL:([MyDatabase].[dbo].[Matching_IDs].[annonsid2] as [m2].[annonsid2]=[MyDatabase].[dbo].[Matching_IDs].[annonsid] as [m3].[annonsid] AND [MyDatabase].[dbo].[Matching_IDs].[annonsid2] as [m].[annonsid2]=[MyDatabase].[dbo].[Matching_IDs].[annonsid] as [m2].[annonsid]))
                       |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([m2].[annonsid2], [m2].[annonsid]))
                       |    |--Index Scan(OBJECT:([MyDatabase].[dbo].[Matching_IDs].[NonClusteredIndex-20121229-133207] AS [m2]))
                       |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([m3].[annonsid], [m].[annonsid2]))
                            |--Merge Join(Inner Join, MANY-TO-MANY MERGE:([m].[annonsid])=([m3].[annonsid2]), RESIDUAL:([MyDatabase].[dbo].[Matching_IDs].[annonsid] as [m].[annonsid]=[MyDatabase].[dbo].[Matching_IDs].[annonsid2] as [m3].[annonsid2]))
                                 |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([m].[annonsid]), ORDER BY:([m].[annonsid] ASC))
                                 |    |--Index Scan(OBJECT:([MyDatabase].[dbo].[Matching_IDs].[NonClusteredIndex-20121229-133152] AS [m]), ORDERED FORWARD)
                                 |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([m3].[annonsid2]), ORDER BY:([m3].[annonsid2] ASC))
                                      |--Index Scan(OBJECT:([MyDatabase].[dbo].[Matching_IDs].[NonClusteredIndex-20121229-133207] AS [m3]), ORDERED FORWARD)

5 个答案:

答案 0 :(得分:3)

一些想法:

尝试两个索引(annonsid,annonsid2)和(annonsid2,annonsid)

您是否尝试过列存储索引?它使表只读,但可能会提高性能。

此外,查询的某些变体可能会有所帮助。例子:

SELECT COUNT(*)
FROM Matching_IDs AS m
  INNER JOIN Matching_IDs AS m2
     ON m.annonsid2 = m2.annonsid
  INNER JOIN Matching_IDs AS m3
     ON m2.annonsid2 = m3.annonsid
where m.annonsid = m3.annonsid2

SELECT COUNT(*)
FROM Matching_IDs AS m, Matching_IDs AS m2, Matching_IDs AS m3
where m2.annonsid2 = m3.annonsid
  and m.annonsid2 = m2.annonsid
  and m.annonsid = m3.annonsid2

您检查了CPU / IO负载吗?如果IO-Load很高,那么服务器不是处理数字而是交换=>更多RAM解决了这个问题。

此查询的速度有多快?

SELECT COUNT(*)
FROM Matching_IDs AS m
  INNER JOIN Matching_IDs AS m2
     ON m.annonsid2 = m2.annonsid

如果速度非常快但添加下一个连接速度慢,那么你可能需要更多RAM。

答案 1 :(得分:1)

好像你已经很好地索引了这个。您可以尝试通过添加正确的多列索引将哈希转换为合并连接,但它不会为您提供所需的60x加速。

我认为这个索引会在annonsid, annonsid2,但我可能在这里犯了错误。

实现所有这一切会很好,但索引视图不支持自联接。您可以尝试将此查询(unaggregated)具体化为新表。每当您对基表执行DML时,也要更新第二个表(使用应用程序逻辑或触发器)。这样你就可以快速查询。

答案 2 :(得分:1)

您应该使此查询更加分离。我想首先你应该创建一个表,你可以存储主键+ annonsid,annonsid2 - 如果annosid不是主键本身

DECLARE @AnnonsIds TABLE
(
primaryKey int,
-- if you need later more info from the original rows like captions  
-- AND it is not (just) the annonsid
annonsid int,
annonsid2 int
)

如果您声明了一个表,并且此列上有索引,则WHERE annonsid = @annonsid OR annonsid2 = @annosid

获取指定行的速度非常快

在第一步之后,您可以使用更小的(我猜)和“瘦”表。然后你只需要在这里使用连接或在其上制作临时表和CTE。

我认为它应该更快,取决于WHERE中条件的选择性,如果110万行适合它,那么它没有意义,但如果只是几百或者tousend那你应该试一试!

答案 3 :(得分:0)

您可以通过添加RelatedIdsAnnonsIdRelatedAnnonId的表Distance来对数据进行非规范化。对于AnnonsId的每个值,该表将包含每个RelatedAnnonId的行以及需要遍历以达到它的关系数,即Distance。现有MatchingIds表上的触发器将维护新表,其中Distance具有一些已配置的最大值,例如AnnonsId。 3处理矩形份额。将表格编入索引(DistanceDistance)。

编辑:AnnonsIdMaxDistance)上的索引将允许您快速查找具有足够相关条目的行以形成特定形状。如果您希望能够根据(例如,具有三角形但矩形关系)排除行,则添加inner join RelatedIds as RI on RI.AnnonsId = m.AnnonsId and RI.Distance <= @MaxDistance列可能很有用。

新查询@MaxDistance将使用所需的“形状”来指示select的值。

它应该在MatchingIds上提供更好的性能。下行是另一个表,在更改Matching_IDs表时会有大量行和触发器的开销。

示例:select * from RelatedIds where AnnonsId=RelatedAnnonId and Distance=3 中有两个条目:(1,2)和(2,3)。 新表将包含3个条目:
1→ 2:距离= 1
1→ 3:距离= 2(一个中间'节点'从1变为3)
2→ 3:距离= 1

向匹配的ID(3,1)添加一个条目将导致另一个条目:
1→ 1:距离= 3

瞧,你找到了一个三角形(距离= 3)。

现在,要查找所有三角形,只需执行以下操作:

{{1}}

答案 4 :(得分:0)

1 - 将选择Count(*)更改为Count(1)Count(id)

2 - 在您的存储过程的第一个或查询的第一个位置写set Nocount on

3 - 在annonsidannonsid2

上使用索引

4 - 在表格中的主键后面有索引