2016-05-28 40 views
0

在sp_executesql中,select語句用於連接變量本身和臨時表中的列值。將索引添加到臨時表導致單行結果集

當我將此索引應用於此臨時表並在上述select語句中使用排序時,會給出奇怪的結果。

這隻發生在臨時表的行數大於50行時。

我不喜歡粘貼大代碼示例,但我無法進一步減少它。

如果@maxjob> = 8,則生成的@htmllog包含67個字符。這是意想不到的結果。

如果@maxjob < 8,則生成的@htmllog始終包含超過67個字符。這是預期的結果。

此外,

當我從#acl_log表中刪除索引idx_key,那麼問題消失時@maxjob> = 8 或 當我從刪除「通過[鍵] ASC順序」 @執行sql_sql,那麼問題也會消失。

爲什麼?

declare @logtable as varchar(max) 
set @logtable = '#acl_log' 

if (OBJECT_ID('[tempdb]..#acl_log')) is not null 
    drop table #acl_log 

create table #acl_log(
    [key]  int    identity, 
    [message] nvarchar(max) not null, 
    index idx_key ([key]) 
) 

declare @job as int 
declare @maxjob as int 

set @job = 0 
set @maxjob = 8 

while (@job < @maxjob + 1) 
begin 
    insert into #acl_log([message]) 
    values 
     ('Internet Explorer is currently running without add-ons') 
     ,('All Internet Explorer add-ons, such as ActiveX controls or toolbars, are turned off. Some webpages might not display correctly.') 
     ,('To continue to your home page, click the Home button.') 
     ,('To browse using add-ons, close Internet Explorer and then start it again.') 
     ,('Forward Arrow Check for the latest Windows updates. ') 
     ,('Question Icon How do browser add-ons affect my browsing experience? ') 

    set @job = @job + 1 
end 

declare @executesql_sql as nvarchar(max) 
declare @executesql_param as nvarchar(max) 

declare @htmllog as varchar(max) 
set @executesql_sql = ' 
    set @htmllog = '''' 
    select @htmllog = @htmllog + [message] 
    from ' + @logtable + ' 
    order by [key] asc' 
set @executesql_param = '@htmllog varchar(max) output' 

exec master..sp_executesql @executesql_sql, @executesql_param, @htmllog = @htmllog output 

select len(@htmllog), @htmllog 
+0

索引應該對輸出的長度沒有影響。我懷疑還有其他事情正在進行 - 可能是因爲你一遍又一遍地運行相同的代碼,它使用了可能存在的表。但是,當你可以使用'for xml path'時,爲什麼使用動態SQL來連接字符串呢? –

+0

我看到了'for xml path'的例子,但是如果我想讓一個單元在發生錯誤時點亮,則無法添加bgcolor屬性。所以我擴展了'for xml路徑'並去了一個更可讀的關係選擇語句。 – Ronald

+0

。 。如果你需要幫助,你可以問另一個問題。 –

回答

3

總字符串連接的行爲是不確定的,因爲它是計劃依賴性根據微軟在this connect item評論:

即使沒有ORDER BY,我們不保證@v​​ar = @var + 將生成影響多行的任何語句 的連接值。表達式的右邊可以在查詢執行期間被評估一次或多次,並且如我所說的行爲是與計劃相關的。

一個解決方法是在FOR XML子句:

DECLARE @logtable AS varchar(MAX); 
SET @logtable = '#acl_log'; 

IF (OBJECT_ID('[tempdb]..#acl_log')) IS NOT NULL 
    DROP TABLE #acl_log; 

CREATE TABLE #acl_log(
    [key]  int    IDENTITY, 
    [message] nvarchar(max) not null, 
    INDEX idx_key ([key]) 
); 

DECLARE @job as int; 
DECLARE @maxjob as int; 

SET @job = 0; 
SET @maxjob = 8; 

WHILE (@job < @maxjob + 1) 
BEGIN 
    INSERT INTO #acl_log([message]) 
    VALUES 
     ('Internet Explorer is currently running without add-ons') 
     ,('All Internet Explorer add-ons, such as ActiveX controls or toolbars, are turned off. Some webpages might not display correctly.') 
     ,('To continue to your home page, click the Home button.') 
     ,('To browse using add-ons, close Internet Explorer and then start it again.') 
     ,('Forward Arrow Check for the latest Windows updates. ') 
     ,('Question Icon How do browser add-ons affect my browsing experience? '); 

    SET @job = @job + 1; 
END 

DECLARE @executesql_sql AS nvarchar(MAX); 
DECLARE @executesql_param AS nvarchar(MAX); 

DECLARE @htmllog AS varchar(max); 
SET @executesql_sql = ' 
    SET @htmllog = (SELECT [message] 
    FROM ' + @logtable + ' 
    ORDER BY [key] 
    FOR XML PATH(''''), TYPE).value(''.'', ''varchar(MAX)'');'; 

set @executesql_param = '@htmllog varchar(max) OUTPUT'; 

EXEC master..sp_executesql @executesql_sql, @executesql_param, @htmllog = @htmllog output; 

SELECT LEN(@htmllog), @htmllog; 
GO 
+0

您的結果有效。謝謝。但是,您提到字符串連接未定義。但是,這並不是關於這與索引和排序有關的真正解釋。 – Ronald

+1

@羅納德,我在我的回答中引用了錯誤的連接項目,所以我對其進行了更正,幷包含相關摘錄。關鍵在於行爲取決於執行計劃,這可能會根據像索引和'ORDER BY'這樣的細節而改變。行爲無法保證,並且可能會在SQL版本和補丁級別之間發生變化,所以即使它現在可行,它在將來也可能會崩潰。 –