2012-10-08 69 views
3

好吧,首先聲明一個免責聲明。我在一些表中使用了一個實體屬性值方法。所以基本上我在一個表中的單個列中有一個屬性列表,然後我想在單獨的視圖中將其填充到一行中。將1個數字添加到TOP選擇掛起查詢

我發現這個解決方案,它的偉大工程:

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

但是初始加載是極爲緩慢的(花了超過27分鐘,填充514行)。我覺得有些東西看起來不太合適,所以我搞砸了使用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) 

編輯:

確定它好像問題是,執行計劃是完全通過增加另一個數字改變。我會在下面發佈兩個結果。我仍然不知道它爲什麼會改變,如果有什麼辦法可以阻止它使用哈希匹配而不是Inner Join。

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

+0

檢查第5251行的列大小總數。如果它太大,我會期待一個錯誤,而不是一個掛起。 – HABO

+0

儘管如此,只有514行,所以沒有第5251行,由於某種原因,儘管一旦我越過了該任意閾值,我立即從結果轉到加載10-20分鐘的結果。 –

+0

514行中有多少個不同的屬性名稱? – HABO

回答

0

以下是格式化的評論,不是答案。

出於病態的好奇心,我一直在擺弄下面的測試代碼。我意識到這是沒有適當的代碼來檢查測試表的存在,對於隨機數的邊緣情況無疑是錯誤的......。所以就這樣了。

目的是創建比問題中描述的更多的測試數據和更大的結果。使用顯示的參數,它需要大約306秒的初始化時間和87秒的時間才能在我的筆記本上運行動態查詢。 (Windows 7 Professional 64位,16GB內存,SQL Server 2008 R2 64位)。我沒有看到任何困難。丹尼爾,你是否看到任何明顯的差異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)' 
1

沒有看準確的指數設計,並確切知道你選擇什麼和數據庫中的內容的大小和範圍,我不能給你確切的理由。

但是我可以告訴你的是,SQL Server用來提出有關掃描表或執行大量搜索更有利的計劃的成本閾值。這裏發生的事情是,SQL Server似乎在5250/5251邊界處超過了該閾值。

如果您要向主表中添加更多數據行,重新生成統計信息並重新查詢,您很可能會發現SQL Server將停止掃描和哈希,並且會返回到基於查找的計劃,因爲它不得不重複尋找的行數會再次降低。

那麼,如何消除這個問題呢?我首先要說的是,在某些情況下EAV可以適用於某些類型的設計,但是當您從系統中提取大量數據,或者希望從這些模型中提取報告時,EAV會變得有問題,而且這些是你傾向於看到的問題類型。既然你是有選擇的

你有幾個可能的解決方案在這裏。

  1. 添加LOOP JOIN hint到您的加入。呸。
  2. 對於從數據庫中提出的要求,您可以選擇更具選擇性的方法 - 例如,一次詢問單個客戶端的值。
  3. 重新評估使用EAV模型的原因,並使用常規技術重新設計您的數據模型。

再說一遍,我對這個設計的理由不太瞭解,所以如果我處於你的位置,背對着牆壁,我可能會做1)和3)的組合。我會添加暗示以「堅決止血」,然後我會考慮重新評估決定使用實體屬性值設計。

+0

好的,謝謝你的輸入。我一定會完全放棄EAV,但我不確定我們如何才能將常規方法塑造成我們的需求。基本上,我們有數十個網站收集了特定的數據集。其中很多數據都是我們想要全面報告的數據,但其他數據則是特定於站點的。所需的數據經常根據資金和其他方面而變化,所以這就是爲什麼EAV非常有吸引力。使用傳統的方法,我們會不斷更新專欄和表格,但我同意報告會更容易。任何建議/鏈接歡迎! :) –