4

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

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

SQL:具有基于源表中列值的列名的动态视图

然而,初始加载非常慢(填充 514 行需要 27 分钟)。我认为有些事情似乎根本不正确,所以我使用 TOP 选择了 Client 表的部分。我得到了立竿见影的结果。我发现我可以通过这种方式立即将整个数据库排队。但是我发现了一个非常奇怪的警告。我最多可以选择 5250 条记录。

到目前为止,我仍然得到了立竿见影的效果。如果我尝试选择 5251,则查询挂起。我在测试服务器上尝试过,得到了相同的限制,但数量不同(我可以在那里选择最多 5321)。请记住,该表只有 514 条记录,所以我不知道为什么将 1 个数字添加到 TOP 选择会导致它挂起。有人对此有任何意见吗?下面是我的工作 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)

编辑:

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

执行计划 1(即时):
执行计划 1

执行计划 2(30 分钟以上): 执行计划 2

4

2 回答 2

1

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

但我可以告诉您的是,SQL Server 使用成本阈值来制定计划,以确定扫描表或执行大量搜索是否更有利。这里发生的情况是 SQL Server 似乎在 5250/5251 边界处越过了该阈值。

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

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

您在这里有几个可能的解决方案。

  1. 为您的连接添加一个 LOOP JOIN提示。呸。
  2. 对从数据库中询问的内容更具选择性——例如,一次只询问单个客户的值。
  3. 重新评估您使用 EAV 模型的原因并使用传统技术重新设计您的数据模型。

再说一次,我不太了解你对这个设计的推理,所以如果我在你的位置并且我的背靠在墙上,我可能会做 1) 和 3) 的组合。我会添加提示以“止血”,然后我会考虑重新评估完全使用实体属性值设计的决定。

于 2012-10-17T16:58:54.743 回答
0

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

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

目的是创建比问题中描述的更多的测试数据和更大的结果。使用显示的参数,初始化大约需要 306 秒,在我的笔记本上运行动态查询大约需要 87 秒。(Windows 7 Professional 64 位,16GB 内存,SQL Server 2008 R2 64 位。)我没有看到任何困难的迹象。Daniel,您是否看到任何明显的差异,例如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)'
于 2012-10-15T01:18:40.590 回答