SQL Server Updating and join on multiple rows, which row's value is used?

ve7v8dk2  于 2023-03-22  发布在  其他
关注(0)|答案(5)|浏览(152)

Let's say I have the following statement and the inner join results in 3 rows where a.Id = b.Id, but each of the 3 rows have different b.Value's. Since only one row from tableA is being updated, which of the 3 values is used in the update?

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

k4emjkb11#

I don't think there are rules for this case and you cannot depend on a particular outcome.

If you're after a specific row, say the latest one, you can use apply , like:

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
uttx8gqw

uttx8gqw2#

Usually what you end up with in this scenario is the first row that appears in the order of the physical index on the table. In actual practice, you should treat this as non-deterministic and include something that narrows your result to one row.

u2nhd7ah

u2nhd7ah3#

Here is what I came up with using 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

It appears that it uses the top value of a basic select each time (row 1 of select * from #b). So, it possibly depends on indexing. However, I would not rely on the implementation set by SQL, as that has the possibility of changing. Instead, I would suggest using the solution presented by Andomar to make sure you know what value you are going to choose.

In short, do not trust the default implementation, create your own. But, this was an interesting academic question :)

kgsdhlau

kgsdhlau4#

Best option in my case for updating multiple records is to use merge Query(Supported from SQL Server 2008), in this query you have complete control of what you are updating. Also you can use output query to do further processing.

Example: Without Output clause(only update)

;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;

Example: With OputPut clause

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.
8ehkhllq

8ehkhllq5#

Yes, I came up with a similar experiment to 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

Takes the topmost row in the comparison, right-side table. You can reverse the #compare.value values to 0 and 1 and you'll get the reverse. I agree with the posters above...its very strange that this operation does not throw an error message as it is completely hidden that this operation IGNORES secondary values

相关问题