#sql-server #tsql
#sql-сервер #tsql
Вопрос:
Допустим, у меня есть таблица FavoriteFruits
, которая имеет NAME
, FRUIT
, и GUID
для столбцов. Таблица уже заполнена именами и плодами. Итак, допустим:
NAME FRUIT GUID
John Apple NULL
John Orange NULL
John Grapes NULL
Peter Canteloupe NULL
Peter Grapefruit NULL
Хорошо, теперь я хочу обновить GUID
столбец новым GUID
(используя NEWID()
), но я хочу иметь одинаковое GUID
имя для каждого отдельного имени. Итак, я хочу, чтобы все John Smiths
они были одинаковыми GUID
, и я хочу, чтобы оба Peters
они были одинаковыми GUID
, но GUID
отличались от того, который использовался для Johns. Итак, теперь это будет выглядеть примерно так:
NAME FRUIT GUID
John Apple f6172268-78b7-4c2b-8cd7-7a5ca20f6a01
John Orange f6172268-78b7-4c2b-8cd7-7a5ca20f6a01
John Grapes f6172268-78b7-4c2b-8cd7-7a5ca20f6a01
Peter Canteloupe e3b1851c-1927-491a-803e-6b3bce9bf223
Peter Grapefruit e3b1851c-1927-491a-803e-6b3bce9bf223
Могу ли я сделать это в инструкции update без использования курсора? Если да, можете ли вы привести пример?
Спасибо, ребята…
Ответ №1:
Обновление CTE не будет работать, потому что оно будет вычисляться для каждой строки. Табличная переменная будет работать:
Вы должны иметь возможность использовать табличную переменную в качестве источника для обновления данных. Это непроверено, но будет выглядеть примерно так:
DECLARE @n TABLE (Name varchar(10), Guid uniqueidentifier);
INSERT @n
SELECT Name, newid() AS Guid
FROM FavoriteFruits
GROUP BY Name;
UPDATE f
SET f.Guid = n.Guid
FROM @n n
JOIN FavoriteFruits f ON f.Name = n.Name
Таким образом, это заполняет переменную идентификатором GUID для каждого имени, затем присоединяет ее обратно к исходной таблице и соответствующим образом обновляет.
Комментарии:
1. «CTE не будет работать, потому что он будет вычисляться для каждой строки» — то же самое относится к другим табличным выражениям, например, к
USING
предложению вMERGE
инструкции.MERGE
Конечно, использование с табличной переменной вместо табличного выражения будет работать.2. Спасибо за добавление этой ссылки. Я слышал об этой проблеме, но не понимал, что они решили закрыть это как «не исправить». Семантика кажется мне неправильной (конечно, не то, что я ожидал).
Ответ №2:
Для уточнения комментариев используйте табличное выражение в USING
предложении MERGE
инструкции.
Следующее не будет работать, потому что оно будет вычисляться для каждой строки:
MERGE INTO FavoriteFruits
USING (
SELECT NAME, NEWID() AS GUID
FROM FavoriteFruits
GROUP
BY NAME
) AS source
ON source.NAME = FavoriteFruits.NAME
WHEN MATCHED THEN
UPDATE
SET GUID = source.GUID;
Но будет работать следующее, используя табличную переменную:
DECLARE @n TABLE
(
NAME VARCHAR(10) NOT NULL UNIQUE,
GUID UNIQUEIDENTIFIER NOT NULL UNIQUE
);
INSERT INTO @n (NAME, GUID)
SELECT NAME, NEWID()
FROM FavoriteFruits
GROUP
BY NAME;
MERGE INTO FavoriteFruits
USING @n AS source
ON source.NAME = FavoriteFruits.NAME
WHEN MATCHED THEN
UPDATE
SET GUID = source.GUID;
Ответ №3:
Также существует решение с одним оператором, которое, однако, имеет некоторые ограничения. Идея состоит в том, чтобы использовать OPENQUERY()
, как это:
UPDATE FavoriteFruits
SET GUID = n.GUID
FROM (
SELECT NAME, GUID
FROM OPENQUERY(
linkedserver,
'SELECT NAME, NEWID() AS GUID FROM database.schema.FavoriteFruits GROUP BY NAME'
)
) n
WHERE FavoriteFruits.NAME = n.NAME
Это решение подразумевает, что вам необходимо создать самонаводящийся связанный сервер. Другая особенность заключается в том, что вы не можете использовать этот метод для табличных переменных или локальных временных таблиц (глобальные подойдут так же, как и «обычные» таблицы).