向TOP Select添加1个号码会挂起查询

时间:2012-10-08 23:14:04

标签: sql sql-server view

好的,首先是免责声明。我在几张表中使用了实体属性值方法。所以基本上我在一个表中的单个列中有一个属性列表,然后我想在单独的视图中将它填充到一行中。

我找到了这个解决方案并且效果很好:

SQL: Dynamic view with column names based on column values in source table

然而,初始负载非常慢(填充514行需要27分钟)。我觉得有些东西看起来不对,所以我搞砸了使用TOP选择Client表的部分内容。我立刻得到了结果。我发现我可以通过这种方式立即排队整个数据库。但是我发现了一个非常奇怪的警告。我能选择的最多是5250条记录。

到目前为止,我仍然得到了即时结果。如果我尝试选择5251,查询将挂起。我在测试服务器上尝试了它并获得了相同的限制但是使用了不同的数字(我可以选择最大值为5321)。请记住,该表只有514条记录,所以我不知道为什么在TOP选择中添加1个数字会导致它挂起。有没有人对此有任何意见?这是我下面的工作SQL查询:

DECLARE @cols AS NVARCHAR(MAX)
DECLARE @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(a.AttributeName) 
                from AttributeCodes a                                   
        FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')


set @query = 'SELECT TOP 5250 ClientID, ' + @cols + ' from 
         (
             select c.ClientID
                    , c.[Value]
                    , a.AttributeName                                           
                from Client c
                inner join AttributeCodes a
                    on a.AttributeCodeId = c.AttributeCodeID

        )x
        pivot 
        (
            min([Value])
            for AttributeName in (' + @cols + ')
        )p'

execute(@query)

编辑:

好吧,问题似乎是通过添加另一个数字完全改变了执行计划。我将在下面发布两个结果。我仍然不知道它为什么会改变,如果有任何方法我可以阻止它使用哈希匹配而不是内部联接。

Execution Plan 1(即时):
Execution Plan 1 http://i49.tinypic.com/2921r14.jpg

Execution Plan 2(30多分钟): Execution Plan 2 http://i46.tinypic.com/358qu4n.jpg

2 个答案:

答案 0 :(得分:1)

如果不仔细查看确切的索引设计并确切知道您选择的内容的大小和范围以及数据库中的内容,我就无法给出确切的原因。

但我可以告诉你的是,SQL Server使用的成本阈值可以提出关于扫描表格或执行大量搜索更有利的计划。这里发生的事情是SQL Server似乎在5250/5251边界越过了这个阈值。

如果要向主表添加更多数据行,重建统计信息,然后重新查询,您可能会发现SQL Server将停止扫描和散列并返回基于查找的计划,因为它必须重复寻找的行数比例将再次降低。

那么,如何消除这个问题呢?我首先要说的是,在某些情况下,EAV对于某些类型的设计可能没什么问题,但是当你从系统中提取大量数据,或者想要从这些模型中获取报告时,EAV就会出现问题,而这些是你往往会看到的问题类型。既然你有选择性

这里有几种可能的解决方案。

  1. 在您的加入中添加LOOP JOIN hint。呸。
  2. 对您从数据库中提出的要求更具选择性 - 例如,一次只询问一个客户的值。
  3. 重新评估使用EAV模型的原因,并使用传统技术重新设计数据模型。
  4. 同样,我对你对这个设计的推理知之甚少,所以如果我在你的位置而我的背靠墙,我可能会做1)和3)的组合。我会添加暗示“坚定地止血”,然后我会考虑重新评估完全使用实体属性值设计的决定。

答案 1 :(得分:0)

以下是格式化的评论,而不是答案。

出于病态的好奇心,我一直在使用以下测试代码。我意识到没有正确的代码来检查测试表的存在,随机数的边缘情况无疑是错误的,.......它就是这样。

目的是创建比问题中描述的更多的测试数据和更大的结果。使用显示的参数,初始化大约需要306秒,在笔记本上运行动态查询需要87秒。 (Windows 7 Professional 64位,16GB内存,64位SQL Server 2008 R2。)我没有看到任何困难的迹象。丹尼尔,你看到任何明显的差异,例如Value列的其他数据类型可能更大? SQL Server的版本?有效内存?我是否完全破坏了您的EAV表示?

-- Test parameters.
declare @NumberOfAttributes as Int = 1000
declare @NumberOfClients as Int = 1000
declare @NumberOfSampleRows as Int = 1000000
declare @NumberOfTopRows as Int = 10000
declare @Start as DateTime = GetDate()

-- Houseclean any prior data.
if Object_Id( 'AttributeCodes' ) is not NULL
  drop table AttributeCodes
if Object_Id( 'Client' ) is not NULL
  drop table Client

-- Create the tables.
create table AttributeCodes (
  AttributeCodeId Int Identity(1,1) not NULL,
  AttributeName VarChar(64) not NULL )
create table Client (
  ClientId Int not NULL,
  AttributeCodeId Int not NULL,
  [Value] VarChar(64) not NULL )

set nocount on

-- Generate some sample attributes.
declare @Count as Int
set @Count = @NumberOfAttributes
while ( @Count > 0 )
  begin
  insert into AttributeCodes ( AttributeName ) values
    ( 'Attr_' + Right( '000' + Cast( @Count as VarChar(8) ), 4 ) )
  set @Count = @Count - 1
  end

-- Generate some sample client data.
declare @ClientId as Int
declare @AttributeCodeId as Int
set @Count = @NumberOfSampleRows
while ( @Count > 0 )
  begin
  set @ClientId = 1 + Cast( Rand() * @NumberOfClients as Int )
  set @AttributeCodeId = 1 + Cast( Rand() * @NumberOfAttributes as Int )
  insert into Client ( ClientId, AttributeCodeId, Value ) values
    ( @ClientId, @AttributeCodeId, Replicate( 'i', Cast( Rand() * 64 as Int ) ) )
  set @Count = @Count - 1
  end

-- Build the list of columns.
declare @Cols as NVarChar(Max)
select @Cols = Stuff(
  ( select ',' + QuoteName( AttributeName ) from AttributeCodes order by AttributeName for XML path(''), type).value( '.[1]', 'NVarChar(max)' ), 1, 1, '' );

-- Build an execute the summary query.
declare @Query as NVarChar(Max)
set @Query = 'select top (' + Cast( @NumberOfTopRows as VarChar(8) ) + ') ClientId, ' + @Cols +
  ' from ( select C.ClientId, C.[Value], A.AttributeName from Client as C inner join AttributeCodes as A on A.AttributeCodeId = C.AttributeCodeId ) as X' +
  ' pivot ( Min( [Value] ) for AttributeName in (' + @cols + ') ) as P' 
declare @InitializationComplete as DateTime = GetDate()
execute( @Query )
select DateDiff( ms, @Start, @InitializationComplete ) as 'Initialization (ms)',
  DateDiff( ms, @InitializationComplete, GetDate() ) as 'Query (ms)',
  DateDiff( mi, @Start, GetDate() ) as 'Total (min)'