Могу ли я обновить много таблиц на основе другой таблицы, содержащей имена таблиц в столбце? - PullRequest
0 голосов
/ 25 июня 2019

Текущая работа над проектом, в котором мне нужно обновить данные 85 таблиц, заменяя текущую пустую строку значением NULL. это простой SQL-запрос для этого, но, поскольку это чувствительная среда, если что-то пойдет не так, нам нужно это исправить.

Основная идея состояла в том, чтобы создать таблицу для сохранения данных для отката. но я пытаюсь избежать создания 85 таблиц.

Я приведу меньший пример:

есть 4 таблицы


   ------------------------------------
   |            airplane              |
   ------------------------------------
   | air_ID | color | tail_number     |
   ------------------------------------
   |  1     | red   |                 |
   |  2     | green |                 |
   |  3     | black |  21AF           |
   ------------------------------------
   ------------------------------------
   |            bus                   |
   ------------------------------------
   | bus_ID | color | tag_number      |
   ------------------------------------
   |  1     | red   |  AAY-464        |
   |  2     | green |                 |
   |  3     | black |                 |
   ------------------------------------
   ------------------------------------
   |            train                 |
   ------------------------------------
   | tr_ID  | color | designated_name |
   ------------------------------------
   |  1     | red   |  99212          |
   |  2     | green |                 |
   |  3     | black |                 |
   ------------------------------------
   ------------------------------------
   |         Cruise_Ship              |
   ------------------------------------
   | sea_ID | color | hull_number     |
   ------------------------------------
   |  1     | red   |                 |
   |  2     | green |  MAGDA          |
   |  3     | black |                 |
   ------------------------------------

Итак, я создал временную таблицу с данными


    -------------------------------------------------
    |         update_table                          |
    -------------------------------------------------
    | table_name | ID_colname | ID  |  col_name     |
    -------------------------------------------------
    |   airplane |   air_ID   |  1  | tail_number   |
    |   airplane |   air_ID   |  2  | tail_number   |
    |    bus     |   bus_ID   | 2   | tag_number    |
    |    bus     |   bus_ID   | 3   | tag_number    |
    |    train   |   tr_ID    |  2  |designated_name|
    |    train   |   tr_ID    |  3  |designated_name|
    |Cruise_Ship |   sea_ID   |  1  |  hull_number  |
    |Cruise_Ship |   sea_ID   |  3  |  hull_number  |
    -------------------------------------------------

С помощью этой таблицы я пытался сгенерировать динамический SQL, чтобы обновить все таблицы одним вызовом


    SET @SQLString = N'UPDATE @table 
        SET  @value = '+ @empty +'
        where @key = @id';

    SET @ParmDefinition = N'@table nvarchar(max),
        @value nvarchar(max) ,
        @key nvarchar(max) ,
        @id int';

    DECLARE @table nvarchar(255)
    DECLARE @value nvarchar(255)
    DECLARE @key nvarchar(255)
    DECLARE @id int

    select @table = table_name, @id = ID, @key = ID_colname , @value = col_name from update_table

    EXECUTE sp_executesql
        @SQLString
        ,@ParmDefinition
        ,@table
        ,@value
        ,@key
        ,@id
        ;

Но это не работает, у кого-нибудь есть идеи, как улучшить этот запрос?

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

Ответы [ 2 ]

3 голосов
/ 26 июня 2019

Согласно документации по SQL Server :

Если инструкция SELECT возвращает более одной строки и переменная ссылается на нескалярное выражение, для переменной устанавливается значение, возвращаемое дляВыражение в последней строке набора результатов.

Это означает, что вашим переменным были присвоены значения только из последней строки.Поэтому только [Cruise_Ship]. [Hull_number] будет передан sp_executesql, и это единственный столбец, который обновляется вашим скриптом.

Для хранения значений нескольких переменных должна использоваться табличная переменная.

sp_executesql принимает параметры и используется для построения динамических запросов.

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

добавлено: проверьте this , как передать табличную переменную в sp_executesql.

Здесь ваш код работает неправильно.

select @table = table_name, @id = ID, @key = ID_colname , @value = col_name from update_table

Я знаю, что это не элегантно, но следующий код должен сделать эту работу.И я бы посоветовал обернуть его в СДЕЛКУ независимо от того, делали вы резервную копию или нет.

DECLARE @empty NVARCHAR(10) 
SET @empty = 'NULL'

SELECT
    'UPDATE ' + s.name + '.' + t.name + ' 
    SET [' + c.name + '] =  ' + @empty + ' 
    WHERE LTRIM([' + c.name + ']) = ''''
    END;'
FROM sys.tables t
INNER JOIN sys.schemas s
    ON t.schema_id = s.schema_id
INNER JOIN sys.columns c
    ON t.object_id = c.object_id
ORDER BY
    s.name
   ,t.name
   ,c.column_id
0 голосов
/ 26 июня 2019

Я согласен с Адамом Ямом, я только расширил ответ, включив в него необходимые данные из таблицы update_tables.

Таким образом, выполнение SQL будет происходить только для этих 8 записей в таблице.

результатом является корректное обновление 4 таблиц из примера.

DECLARE @currentId INT 
SELECT @currentId = MIN(tabl.ID) from udpate_table tabl

DECLARE @sql NVARCHAR(MAX)

WHILE (1 = 1)
BEGIN  
   --- execute for the current pk
    BEGIN
        SELECT @sql =
        'UPDATE ' + s.name + '.' + t.name + ' 
        SET [' + c.name + '] = '''' 
           where  ' + s.name + '.' + t.name + '.' + tab.ID_colname + ' = ' + convert (varchar(20), tab.ID) + ' '
        FROM sys.tables t
        INNER JOIN sys.schemas s
            ON t.schema_id = s.schema_id
        INNER JOIN sys.columns c
            ON t.object_id = c.object_id
        JOIN update_table tab
            on t.name = tab.table_name
            and c.name = tab.col_name
            and tab.ID = @currentId
        ORDER BY
            tab.ID
           ,s.name
           ,t.name
           ,c.column_id
    END

    exec sp_executesql @sql

    -- select the next id to handle    
  SELECT TOP 1 @currentId = tabl.ID
  FROM update_table tabl
  WHERE tabl.ID > @currentId 
  ORDER BY tabl.ID

  IF @@ROWCOUNT = 0 BREAK;

END
...