いくつかの動的SQLプロダクションコードの作業中に、一連の文字列とサブクエリを連結し、さらに文字列を連結しているコードに遭遇しました。コードの目標は、SQL変数に値を入力して、最終的にEXEC(@sql)を呼び出すことです。私たちが気付いたのは、文字列連結とCOALESCEチェックにもかかわらず、最後のレコードだけが変数に入力していることです。
以下のコードは、結果を再現するための簡略化されたバージョンです。
IF OBJECT_ID('tempdb..#test') IS NOT NULL
BEGIN
DROP TABLE #test
END
CREATE TABLE #test
(
tmp_id INT IDENTITY(1,1),
error VARCHAR(MAX),
error2 VARCHAR(MAX),
object_id INT
)
DECLARE @sql VARCHAR(MAX) = ''
INSERT INTO #test(error, error2, object_id)
SELECT TOP 100 CONVERT(VARCHAR(MAX), name), CONVERT(VARCHAR(MAX), name), object_id
FROM sys.columns
SELECT @sql +=
' | '+(SELECT xC.name
FROM sys.columns xC
WHERE xC.name = T.error
AND xC.object_id = T.object_id) + ' | '
+ CAST(T.error2 AS VARCHAR(MAX))
FROM #test T
ORDER BY T.tmp_id
SELECT @sql
奇妙なことに、ここにはさまざまな要因が絡んでいるようです。サブクエリsys.columns、order by、またはerror2列を削除すると、期待どおりに機能します。また、tmp_idの代わりにエラーで並べ替えられるように順序を変更することもできます。
既に量産コードを書き直しているので、私は解決策を探していませんが、これが起こっている「理由」に非常に興味があります。 SQL Server 2008、2016、2017、2019サーバーでテストしました。
誰かが原因について何か考えがありますか?
_ORDER BY
_と組み合わせたその形式の文字列連結は定義されていません(不安定で信頼性がありません)。同じ症状が出て、1つの結果しか得られません。すべての行が必要な場合は、_ORDER BY
_をドロップします(順序は保証されませんが、_TOP 100
_には_ORDER BY
_がないため、それは保証されませんどちらかが確定的であること)。 _ORDER BY
_が必要な場合は、バージョンに応じて、代わりに_XML PATH
_またはSTRING_AGG()
を使用してください。
_SELECT @sql = STUFF((
SELECT ',' + t.error + CONVERT(varchar(12), xC.[object_id]) + ','
+ t.error + ',' + t.error + ',' + t.error + ','
+ t.error2 + ',' -- carriage returns are good for the soul!
+ t.error + ',' + t.error + ',' + t.error + ','
FROM #test AS t
INNER JOIN sys.columns AS xC
ON t.object_id = xC.object_id
ORDER BY t.tmp_id -- this is the key to ordering
FOR XML PATH, TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'');
_
2017以降では、以下を使用できます。
_SELECT @sql = STRING_AGG(t.error + CONVERT(varchar(11), xC.[object_id]) + ','
+ t.error + ',' + t.error + ',' + t.error + ','
+ t.error2 + ','
+ t.error + ',' + t.error + ',' + t.error, ',')
WITHIN GROUP (ORDER BY t.tmp_id)
FROM #test AS t
INNER JOIN sys.columns AS xC
ON t.object_id = xC.object_id;
_
詳細については、 (Order by 1は結果セットを1行に短縮します とその中のリンクを参照してください。