web-dev-qa-db-ja.com

複数の行を更新して結合する場合、どの行の値が使用されますか?

次のステートメントがあり、内部結合の結果はa.Id = b.Idの3つの行になりますが、3つの行のそれぞれに異なるb.Valueがあるとします。 tableAの1行のみが更新されているので、3つの値のどれが更新で使用されますか?

UPDATE a
SET a.Value = b.Value
FROM tableA AS a
INNER JOIN tableB as b 
ON a.Id = b.Id
26
sooprise

この場合のルールはないと思いますし、特定の結果に依存することはできません。

特定の行の後、たとえば最新の行の場合は、applyを使用できます。

UPDATE  a
SET     a.Value = b.Value
FROM    tableA AS a
CROSS APPLY
        (
        select  top 1 *
        from    tableB as b
        where   b.id = a.id
        order by
                DateColumn desc
        ) as b
18
Andomar

通常、このシナリオで最終的に発生するのは、テーブルの物理インデックスの順序で表示される最初の行です。実際には、これを非決定論的なものとして扱い、結果を1行に限定するものを含める必要があります。

7
JamieSee

私の場合、複数のレコードを更新するための最良のオプションは、マージクエリ(SQL Server 2008からサポート)を使用することです。このクエリでは、更新対象を完全に制御できます。また、出力クエリを使用して、さらに処理を行うこともできます。

例:出力節なし(更新のみ)

;WITH cteB AS
( SELECT Id, Col1, Col2, Col3  
  FROM B WHERE Id > 10  ---- Select Multiple records
)
MERGE A
USING cteB
ON(A.Id = cteB.Id) -- Update condition
WHEN MATCHED THEN UPDATE
SET  
A.Col1 = cteB.Col1,  --Note: Update condition i.e; A.Id = cteB.Id cant appear here   again.
A.Col2 = cteB.Col2,
A.Col3 = cteB.Col3;

例:OputPut句を使用する

CREATE TABLE #TempOutPutTable
  {
  PkId INT NOT NULL,
  Col1 VARCHAR(50),
  Col2 VARCHAR(50)
  }

;WITH cteB AS
( SELECT Id, Col1, Col2, Col3
FROM B WHERE Id > 10
)
MERGE A
USING cteB
ON(A.Id = cteB.Id)
WHEN MATCHED THEN UPDATE
SET  
A.Col1 = cteB.Col1, 
A.Col2 = cteB.Col2,
A.Col3 = cteB.Col3
OUTPUT 
 INSERTED.Id, cteB.Col1, A.Col2 INTO #TempOutPutTable;

--Do what ever you want with the data in temporary table
SELECT * FROM #TempOutPutTable; -- you can check here which records are updated.
2
sudhAnsu63

ここに私がSQL Server 2008を使用して思いついたものがあります

--drop table #b
--drop table #a
select 1 as id, 2 as value
into #a

select 1 as id, 5 as value
into #b

insert into #b
select 1, 3

insert into #b
select 1, 6

select * from #a
select * from #b

UPDATE #a 
SET #a.Value = #b.Value
FROM #a
INNER JOIN #b 
ON #a.Id = #b.Id

毎回、基本的な選択の最上位値を使用しているようです(select * from #bの行1)。したがって、それはおそらく索引付けに依存します。ただし、SQLによって設定された実装は、変更される可能性があるため、依存しません。代わりに、Andomarが提供するソリューションを使用して、選択する値を確実に把握することをお勧めします。

つまり、デフォルトの実装を信頼せず、独自の実装を作成してください。しかし、これは興味深い学術的な質問でした:)

2
Justin Pihony

はい、ジャスティンピホニーと同様の実験を思いつきました。

    IF OBJECT_ID('tempdb..#test') IS NOT NULL DROP TABLE #test ;
SELECT 
1 AS Name, 0 AS value 
INTO #test

IF OBJECT_ID('tempdb..#compare') IS NOT NULL DROP TABLE #compare ;
SELECT 1 AS name, 1 AS value
INTO #compare
INSERT INTO #compare
SELECT 1 AS name, 0 AS value;

SELECT * FROM #test
SELECT * FROM #compare

UPDATE t
SET t.value = c.value
FROM #test t
INNER JOIN #compare c
    ON t.Name = c.name

比較の右側のテーブルの一番上の行を取ります。 #compare.valueの値を0と1に逆にすると、逆になります。私は上記のポスターに同意します...この操作がエラーメッセージをスローしないので、非常に奇妙です完全に非表示この操作は2次値を無視する

0
E.Gannon