Почему / как работает этот неоднозначный оператор UPDATE? - PullRequest
3 голосов
/ 06 февраля 2009

Допустим, вы выполняете инструкцию UPDATE для таблицы, но информация, которую вы помещаете в эту базовую таблицу, взята из некоторой другой вспомогательной таблицы. Обычно вы присоединяетесь к данным и не ожидаете, что строки в предложении FROM оператора UPDATE будут умножаться, сохраняя при этом, что одна новая строка отображается на одну старую строку в базовой таблице.

Но мне было интересно, что произойдет, если ваша таблица JOIN будет как-то неоднозначной, как если бы вы не могли учесть сопоставление каждой базовой сущности только с одной присоединенной сущностью. Или если вы сделали что-то бессмысленное, например, присоединение к базовой таблице к таблице ее дочерних элементов и обновление базовой таблицы с использованием этой информации. Как бы это выбрать? Теперь в одной строке базовой таблицы несколько строк.

Я запустил подобное утверждение в SQL Server 2005, и мне показалось, что он выбирает первую строку в каждом наборе. Но это просто кажется мне неправильным. Разве это не должно вызвать ошибку? Почему это желаемое поведение?

Пример кода

-- normal
-- categories are one-to-many bundles

update bundles_denormalized set category = c.description

from bundles_denormalized b
left join categories c
on b.category_id = c.id

-- ambiguous
-- bundles are one-to-many products

update bundles_denormalized set category = p.description

from bundles_denormalized b
left join products p
on b.id = p.bundle_id

Ответы [ 3 ]

5 голосов
/ 06 февраля 2009

На самом деле, если я правильно понимаю вопрос, это обновляет поле несколько раз, это просто потому, что есть только одна запись, поэтому она заканчивается только одним значением. Почему это не ошибка? Потому что синтаксис правильный, и у базы данных нет способа узнать, каково было ваше намерение. Хотели бы вы сделать это? Обычно это не то, почему вы должны сделать выбор своего обновления, прежде чем запускать его, чтобы убедиться, что корректные записи получают правильные значения.

Обычно я пишу обновление с помощью соединения следующим образом:

update b    
set category = p.description
--select b.category, p.description
from bundles_denormalized b
left join products p on b.id = p.bundle_id

Я бы также опасался использовать левое соединение в обновлении, так как вы можете изменить значения на нули. Это нормально, если это то, что вы хотели, но не если это не так.

4 голосов
/ 06 февраля 2009

С BOL на ОБНОВЛЕНИЕ

Использование UPDATE с предложением FROM

Результаты оператора UPDATE: не определено, если заявление включает в себя Предложение FROM, которое не указано в таким образом, что только одно значение доступно для каждого вхождения столбца что обновляется, то есть если ОБНОВЛЕНИЕ утверждение не является детерминированным. Например, в операторе UPDATE в следующем сценарии обе строки в Таблица 1 соответствует квалификации Предложение FROM в выражении UPDATE; но не определено, какая строка из Table1 используется для обновления строки в Table2.

USE AdventureWorks;
GO
IF OBJECT_ID ('dbo.Table1', 'U') IS NOT NULL
    DROP TABLE dbo.Table1;
GO
IF OBJECT_ID ('dbo.Table2', 'U') IS NOT NULL
    DROP TABLE dbo.Table2;
GO
CREATE TABLE dbo.Table1 
    (ColA int NOT NULL, ColB decimal(10,3) NOT NULL);
GO
CREATE TABLE dbo.Table2 
    (ColA int PRIMARY KEY NOT NULL, ColB decimal(10,3) NOT NULL);
GO
INSERT INTO dbo.Table1 VALUES(1, 10.0);
INSERT INTO dbo.Table1 VALUES(1, 20.0);
INSERT INTO dbo.Table2 VALUES(1, 0.0);
GO
UPDATE dbo.Table2 
SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
FROM dbo.Table2 
    INNER JOIN dbo.Table1 
    ON (dbo.Table2.ColA = dbo.Table1.ColA);
GO
SELECT ColA, ColB 
FROM dbo.Table2;

Другими словами, это допустимый синтаксис, и он не собирается выдавать ошибку или исключение.

Но в то же время вы не можете быть уверены, что значение обновления будет первой или последней записью в предложении FROM, поскольку оно не определено.

0 голосов
/ 05 октября 2009

Я на самом деле только что заметил, что это делает что-то глупо в моем случае. Моим намерением было создать выборку из одной строки, но, видимо, были дубликаты, которых я не ожидал. В этом случае он фактически ввел смесь данных в целевую строку, выбрав несколько столбцов из первой строки источника, а некоторые - из второй строки источника.

Я почти уверен, что Firebird выдает исключение, если делается попытка сделать что-то столь же неоднозначное, как это. Но тогда Firebird вообще не поддерживает (нестандартное?) Обновление sntax X из X join Y ...

...