3

値から入力する必要のあるローカルテーブルと文字列があります。

    DECLARE @#SomeTable  TABLE ( some columns ..)
    DECLARE @SomeString  varchar(8000) = 'init string'

それを繰り返しながら

    WHILE EXISTS(SELECT * FROM  @#SomeTable)
     BEGIN
        // [somecolumn] is declared temp variable
        SELECT TOP 1 @somecolumn = somecolumn FROM  @#SomeTable

        PRINT 'before ' + @SomeString // 'init string'
        PRINT [some values from SomeTable]  // this OK
        SET @SomeString += [some values from SomeTable] 
        PRINT 'after ' +  @SomeString //'init string' UPDATE NOT TAKE PLACE!!!!

        DELETE  @#SomeTable Where somecolumn = @somecolumn
     END

連結が失敗することがわかりました。なんで?

編集:

これが元のコードの一部です。

    /*Represents [WHERE] clause for retrieving values from specifyed range*/
DECLARE @WHEREclause nchar(1000) = 'WHERE '
/*Represents [ORDER BY] clause for sorting in right order and direction {ASC|DESC}*/
DECLARE @ORDERBYclause nchar(1000) = 'ORDER BY '
/*Dynamic query that returns end result*/
DECLARE @sqlCmd varchar(8000) = 
'SELECT 
        img,
        capacity,
        price,
        Id
 FROM HDD  '
/* -a- filling table for input values*/
INSERT INTO @#SequenceTable(columnName,columnValue,comparator,isASC,columnOrder)
SELECT 
    columnName,
    columnValue,
    comparator,
    isASC,
    ROW_NUMBER() OVER (ORDER BY  outOrder) AS columnOrder
FROM
(
        SELECT 'buffer' as columnName, CAST(@buffer AS nchar(20)) as columnValue, @bufferCmp as comparator, @bufferASC as isASC, @bufferOrder as outOrder
    UNION  
        SELECT 'capacity', CAST(@capacity AS nchar(20)), @capacityCmp, @capacityASC, @capacityOrder  
    UNION  
        SELECT 'price', STR(@price,20,2),  @priceCmp, @priceASC, @priceOrder
    UNION  
        SELECT 'angle_speed', CAST(@angleSpeed AS nchar(20)), @angleSpeedCmp ,@angleSpeedASC,@angleSpeedOrder
) AS AnyName
ORDER BY columnOrder 

/*---/a-----------------------------------------------------------------------------------*/

/*variables for above fields*/
DECLARE @columnName nchar(20)
DECLARE @comparator char
DECLARE @columnValue nchar(20)
DECLARE @isASC char

WHILE EXISTS(SELECT * FROM  @#SequenceTable)
BEGIN

    SELECT TOP 1 @columnName = columnName FROM  @#SequenceTable
    SELECT TOP 1 @comparator = comparator FROM  @#SequenceTable 
    SELECT TOP 1 @columnValue = columnValue FROM  @#SequenceTable 
    SELECT TOP 1 @isASC = isASC FROM  @#SequenceTable 

    IF @WHEREclause != 'WHERE '
        BEGIN
            SET @WHEREclause += ' AND '
        END
    PRINT 'before ' + @WHEREclause
    PRINT CONCAT(RTRIM(@columnName), @comparator, @columnValue)
    SET @WHEREclause += CONCAT(RTRIM(@columnName), @comparator, @columnValue)
    PRINT 'after ' +  @WHEREclause

    IF @ORDERBYclause != 'ORDER BY '
        BEGIN
            SET @ORDERBYclause += ','
        END
    IF @isASC = '1'
       SET @ORDERBYclause += CONCAT(RTRIM(@columnName),' ASC ')
    ELSE
       SET @ORDERBYclause += CONCAT(RTRIM(@columnName),' DESC ')

    Delete  @#SequenceTable Where columnName = @columnName

END

そして、これが印刷結果の一部です:

        before WHERE 
        angle_speed=7400 
        after WHERE 

+=期待どおりに動作しません。使用する場合

@SomeString = 'some value' 

その後、更新が表示されますが、代わりに使用する場合

@SomeString += 'some value' 

更新は表示されません

4

3 に答える 3

10

SQL Serverにバグはなく+=、期待どおりに機能することを保証できます。次のコードを試しました。

DECLARE @#SomeTable TABLE (somecolumn varchar(8000));

INSERT @#SomeTable VALUES('a'), ('bbb'), ('ccccc');

DECLARE @SomeString  varchar(8000) = 'init string',
        @somecolumn  varchar(8000);

WHILE EXISTS (SELECT * FROM  @#SomeTable)
BEGIN
    SELECT TOP 1 @somecolumn = somecolumn FROM @#SomeTable;

    SET @SomeString += @somecolumn;

    PRINT @SomeString; -- Works fine!!!

    DELETE  @#SomeTable Where somecolumn = @somecolumn;
END

そして、これが私の結果です:

init stringa
init stringabbb
init stringabbbccccc

コードで何をしているのかを正確に伝えることは不可能なので(最も重要な部分を難読化した)、そこから始めることができるでしょうか?確かに、テーブルにNULL値があるか、間違って割り当てているか、間違った変数に割り当てています。繰り返しになりますが、コードの重要な部分を非表示にしているため、わかりません。

また、順序を気にしないように見えるので、ループせずにこれを行うこともできます。

DECLARE @#SomeTable TABLE (somecolumn varchar(8000));

INSERT @#SomeTable VALUES('a'), ('bbb'), ('ccccc');

DECLARE @SomeString  varchar(8000) = 'init string',
        @somecolumn  varchar(8000);

SELECT @SomeString += somecolumn FROM @#SomeTable;

PRINT @SomeString;

結果:

init stringabbbccccc

順序が気になる場合でも、ループせずにこれを行うことができます。XMLトリックを使用してその順序で連結し、後でそれをinit文字列に追加します。

DECLARE @#SomeTable TABLE (somecolumn varchar(8000));

INSERT @#SomeTable VALUES('a'), ('bbb'), ('ccccc');

DECLARE @SomeString  varchar(8000) = 'init string',
        @somecolumn  varchar(8000) = '';

SELECT @somecolumn = (SELECT '' + somecolumn FROM @#SomeTable
ORDER BY somecolumn DESC
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'varchar(max)');

PRINT @SomeString + @somecolumn;

結果:

init stringcccccbbba

最新バージョン(SQL Server 2017以降)では、次のことができます。

DECLARE @#SomeTable TABLE (somecolumn varchar(8000));

INSERT @#SomeTable VALUES('a'), ('bbb'), ('ccccc');

DECLARE @SomeString  varchar(8000) = 'init string',
        @somecolumn  varchar(8000);

SELECT @somecolumn = STRING_AGG(somecolumn, '')
  WITHIN GROUP (ORDER BY somecolumn DESC)
  FROM @#SomeTable;

PRINT @SomeString + @somecolumn;
于 2013-01-13T03:14:30.300 に答える
8

SQL Serverで2つの値の間で計算をNULL行うと、関係する値の1つが。になりますNULL。あなたの場合、@SomeStringは初期化されていません。つまり、その値はNULLです。したがってSET @SomeString =+ 'somevalue'、結果はNULL

これを解決するには、最初に変数をに初期化します''

また、次のコード行があります。

SET @somecolumn += [some values from SomeTable]

しかし、あなたはおそらくこれを持っているつもりでした:

SET @SomeString += [some values from SomeTable]
于 2013-01-12T16:17:00.593 に答える
1

同じ問題がありますが、これは確かにT-SQLの既知の問題です。詳細については、https ://marc.durdin.net/2015/07/concatenating-strings-in-sql-server-or-undefined-behaviour-by-design/を参照してください。

要約すると:

SELECTステートメントを使用した変数の割り当ては、動作が未定義であるか、複数の行が生成される場合にプランに依存する独自の構文(T-SQLのみ)です。

ORDER BY句を使用したクエリで代入演算(この例では連結)を使用すると、未定義の動作が発生します。これは、リリースごとに、またはクエリプランの変更により、特定のサーバーバージョン内でさえも変更される可能性があります。回避策がある場合でも、この動作に依存することはできません。

保証される唯一のメカニズムは次のとおりです。1。カーソルを使用して特定の順序で行をループし、値を連結します2. ORDER BYを使用したxmlクエリに使用して、連結された値を生成します3. CLR集計を使用します(これはORDERでは機能しません) BY句)

于 2016-08-10T01:49:21.737 に答える