2013-02-28 7 views
10

私は今日SQL Server(2008R2と2012の両方)で本当に奇妙な問題に遭遇しました。私はselectステートメントと組み合わせて連結を使用して文字列を構築しようとしています。nvarchar concatenation/index/nvarchar(max)不可解な動作

私は回避策を見つけましたが、ここで何が起こっているのか、なぜ私の期待した結果が得られないのか理解したいと思います。誰かが私にそれを説明することはできますか?リクエストに

http://sqlfiddle.com/#!6/7438a/1

、またここにコード:

-- base table 
create table bla (
    [id] int identity(1,1) primary key, 
    [priority] int, 
    [msg] nvarchar(max), 
    [autofix] bit 
) 

-- table without primary key on id column 
create table bla2 (
    [id] int identity(1,1), 
    [priority] int, 
    [msg] nvarchar(max), 
    [autofix] bit 
) 

-- table with nvarchar(1000) instead of max 
create table bla3 (
    [id] int identity(1,1) primary key, 
    [priority] int, 
    [msg] nvarchar(1000), 
    [autofix] bit 
) 

-- fill the three tables with the same values 
insert into bla ([priority], [msg], [autofix]) 
values (1, 'A', 0), 
     (2, 'B', 0) 

insert into bla2 ([priority], [msg], [autofix]) 
values (1, 'A', 0), 
     (2, 'B', 0) 

insert into bla3 ([priority], [msg], [autofix]) 
values (1, 'A', 0), 
     (2, 'B', 0) 
; 
declare @a nvarchar(max) = '' 
declare @b nvarchar(max) = '' 
declare @c nvarchar(max) = '' 
declare @d nvarchar(max) = '' 
declare @e nvarchar(max) = '' 
declare @f nvarchar(max) = '' 

-- I expect this to work and generate 'AB', but it doesn't 
select @a = @a + [msg] 
    from bla 
    where autofix = 0 
    order by [priority] asc 

-- this DOES work: convert nvarchar(4000) 
select @b = @b + convert(nvarchar(4000),[msg]) 
    from bla 
    where autofix = 0 
    order by [priority] asc 

-- this DOES work: without WHERE clause 
select @c = @c + [msg] 
    from bla 
    --where autofix = 0 
    order by [priority] asc 

-- this DOES work: without the order by 
select @d = @d + [msg] 
    from bla 
    where autofix = 0 
    --order by [priority] asc 

-- this DOES work: from bla2, so without the primary key on id 
select @e = @e + [msg] 
    from bla2 
    where autofix = 0 
    order by [priority] asc 

-- this DOES work: from bla3, so with msg nvarchar(1000) instead of nvarchar(max) 
select @f = @f + [msg] 
    from bla3 
    where autofix = 0 
    order by [priority] asc 

select @a as a, @b as b, @c as c, @d as d, @e as e, @f as f 
+0

これは良いことですが、問題の再現に必要なコードを含めることはできますか? SQLFiddleは非常に便利ですが、コードは存在してはいけません。 –

+0

どういう意味ですか?これはSQLの問題です。他のどこかではありません。そうですか? – bartlaarhoven

+0

私はあなたがSQLfiddleに持っているreproを意味しますが、質問のコードブロックにあります。 –

答えて

23

KB article集計連結クエリの正しい動作が 未定義であるライン

を含むありません。

しかし、確定的な動作が可能であるように思われる回避策を提供することによって、水面を少し濁らせてしまいます。

BY句SELECTリストではなく、順番に に列を任意のTransact-SQLの機能または発現を適用し、凝集 連結クエリから期待される結果を達成するために。

問題のあるクエリでは、ORDER BY句の列に式が適用されません。

2005記事Ordering guarantees in SQL Server...は、最上位の 範囲でBY ORDER ... SQL ServerがタイプSELECT @p = @p + 1の 割り当てのためのサポートを提供し、後方互換性の理由から状態

を行います。

期待通りに連結が機能するプランでは、式[Expr1003] = Scalar Operator([@x]+[Expr1004])の計算スカラーがソートの上に表示されます。

計算に失敗した計画では、計算スカラーがソートの下に表示されます。 2006年のthis connect itemで説明されているように、式@x = @x + [msg]がソートの下に表示されると、ソートは各行について評価されますが、すべての評価は事前割り当て値@xを使用して終了します。 2006年のanother similar Connect Itemでは、マイクロソフトからの回答が問題を「修正」していると述べました。

この問題に関する以降のすべての接続項目のマイクロソフトの対応、これは単に私たちはの正確さに保証されません

Example 1

保証されていない状態(そして多くあります)連結 クエリ( 特定の順序でデータ検索で変数割り当てを使用する場合など)。プランの選択、テーブル内のデータなどによって、クエリ出力が に変更される可能性があります。 は、この動作を一貫して有効にしてはいけません。 並べ替えられた行の検索と 変数の割り当て。

Example 2

あなたが見ている動作は仕様です。 ORDER BY句を持つクエリで (この例では連結)の代入操作を使用すると、未定義の動作が になります。これはリリースごとに変更することができ、クエリプランの変更により特定のサーバーバージョン内の さえも変更される可能性があります。 回避策がある場合でも、この動作に依存することはできません。
http://support.microsoft.com/kb/287515ザ・ONLYが メカニズムを保証する次のとおりです:

  1. 使用し、特定の順序で行をループにカーソルと
  2. 使用するXMLクエリでの値を連結詳細はKBの記事の下に を参照してください。連結された値
  3. 使用CLRの集計(これはORDER BY句では動作しません)
を生成するBY ORDER

Example 3

表示されている動作は実際の仕様です。これは、 と関連しています。SQLはセット操作言語です。 SELECT リストのすべての式(およびこれには割り当ても含まれます)は、出力行ごとに正確に1回実行される であるとは限りません。実際には、SQLクエリ オプティマイザは、できるだけ数回実行するのが難しいです。この は、テーブル内の一部のデータに基づいて 変数の値を計算するときに期待される結果を示しますが、 の値が同じ変数の以前の値に依存する場合、結果はかなり予期しない可能性があります。 。クエリオプティマイザが 式をクエリツリー内の別の場所に移動すると、 の評価時間が短くなる(または例のように1回だけ)ことがあります。この は、 に「繰り返し」タイプの割り当てを使用しないことをお勧めします。我々は、XMLベースの回避策を見つける...通常我々が保証するものではありません、でも、ORDER BYなし 顧客

Example 4

のために働くこと@var = @var + 複数の行に影響を与えるの文 ための連結値を生成します。式の右側はクエリ実行中に を1回または複数回評価し、 は私が言ったように動作が計画に依存することができます。

Example 5

複数の行が生成される場合、動作は未定義またはプラン依存であるSELECT文を使用して変数の割り当ては、独自の構文 (T-SQLのみ)です。文字列連結 を実行する必要がある場合は、SQLCLR集計またはFOR XMLクエリベースの連結または その他のリレーショナルメソッドを使用します。

2

は少しこの記事のように思える:VARCHAR(MAX) acting weird when concatenating string

が結論: 文字列の連結にこのアプローチは、通常動作しませんそれは保証されていません。同様の問題の The official line in the KB articleは、「集約連結クエリの正しい動作は未定義です」ということです。既にVanDerNorthによって連結

+0

うん。ありがとう。それは本当に私を満足させるものではありません。次に、参照しているKB記事がSQL Server 2000および7.0に適用されます。それは今までに修正されるべきではありませんか? – bartlaarhoven

+2

@bartlaarhoven - 行動は保証されていないので、修正することはありません。したがって、あなたはそれに頼るべきではありません。代替アプローチについては、[Transact-SQLの行値の連結](https://www.simple-talk.com/sql/t-sql-programming/concatenating-rowues-in-transact-sql/)を参照してください。 –

関連する問題