T-SQL для обновления строк с одинаковым значением в столбце

#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
 

Это решение подразумевает, что вам необходимо создать самонаводящийся связанный сервер. Другая особенность заключается в том, что вы не можете использовать этот метод для табличных переменных или локальных временных таблиц (глобальные подойдут так же, как и «обычные» таблицы).