MERGE без указания имен столбцов в SQL Server 2008 - PullRequest
13 голосов
/ 07 сентября 2011

Я смотрел на команду MERGE, которая кажется классной, но все же требует указания столбцов. Я ищу что-то вроде:

MERGE INTO target AS t
USING source AS s          
WHEN MATCHED THEN
    UPDATE SET
    [all t.fields = s.fields]
WHEN NOT MATCHED THEN 
      INSERT ([all fields])
      VALUES ([all s.fields])

Возможно ли это?

Ответы [ 4 ]

13 голосов
/ 07 октября 2013

Я ленивый ... это дешевый процесс, который я написал, который будет выдавать общую команду MERGE для таблицы.Он запрашивает information_schema.columns для имен столбцов.Я вырвал имя моей исходной базы данных - так что вы должны обновить proc для работы с вашей базой данных (ищите @SourceDB ... Я сказал, что это дешево.) В любом случае, я знаю, что другие могли бы написать это намного лучше - это помогло мнецель хорошо.(Он делает пару предположений, которые вы могли бы использовать для обработки логики, а именно - отключение IDENTITY_INSERT - даже когда таблица не имеет столбцов идентификаторов.) Она обновляет таблицу в вашем текущем контексте.Он был написан для SQL Server 2008 для синхронизации некоторых таблиц.Используйте на свой страх и риск, конечно.

    CREATE PROCEDURE [dbo].[GenerateMergeSQL]
    @TableName varchar(100)
AS
BEGIN
    SET NOCOUNT ON 

    declare @sql varchar(5000),@SourceInsertColumns varchar(5000),@DestInsertColumns varchar(5000),@UpdateClause varchar(5000)
    declare @ColumnName varchar(100), @identityColName varchar(100)
    declare @IsIdentity int,@IsComputed int, @Data_Type varchar(50)

    declare @SourceDB as varchar(200)


    -- source/dest i.e. 'instance.catalog.owner.' - table names will be appended to this
    -- the destination is your current db context
    set @SourceDB = '[mylinkedserver].catalog.myDBOwner.'

    set @sql = ''
    set @SourceInsertColumns  = ''
    set @DestInsertColumns  = ''
    set @UpdateClause  = ''
    set @ColumnName  = ''
    set @isIdentity = 0
    set @IsComputed = 0
    set @identityColName  = ''
    set @Data_Type  = ''


DECLARE @ColNames CURSOR
SET @ColNames = CURSOR FOR 
    select column_name, COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') as IsIdentity ,
        COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsComputed') as IsComputed , DATA_TYPE
    from information_schema.columns where table_name = @TableName order by ordinal_position

OPEN @ColNames
FETCH NEXT FROM @ColNames INTO @ColumnName, @isIdentity, @IsComputed, @DATA_TYPE

WHILE @@FETCH_STATUS = 0
    BEGIN
        if @IsComputed = 0 and @DATA_TYPE <> 'timestamp'
        BEGIN
            set @SourceInsertColumns = @SourceInsertColumns + 
                case when @SourceInsertColumns = '' THEN '' ELSE ',' end +
                'S.' + @ColumnName

            set @DestInsertColumns = @DestInsertColumns + 
                case when @DestInsertColumns = '' THEN '' ELSE ',' end +
                @ColumnName

            if @isIdentity = 0
            BEGIN
                set @UpdateClause = @UpdateClause + 
                case when @UpdateClause = '' THEN '' ELSE ',' end
                 + @ColumnName + ' = ' + 'S.' + @ColumnName + char(10)
            END

            if @isIdentity = 1 set @identityColName = @ColumnName
        END

        FETCH NEXT FROM @ColNames INTO @ColumnName, @isIdentity, @IsComputed, @DATA_TYPE
    END

CLOSE @ColNames
DEALLOCATE @ColNames

    SET @sql = 'SET IDENTITY_INSERT ' + @TableName + ' ON;
            MERGE ' + @TableName + ' AS D
                USING ' + @SourceDB + @TableName + ' AS S
                ON (D.' + @identityColName + ' = S.' + @identityColName + ')
            WHEN NOT MATCHED BY TARGET
                THEN INSERT(' + @DestInsertColumns + ') 
                VALUES(' + @SourceInsertColumns + ')
            WHEN MATCHED 
                THEN UPDATE SET 
                    ' + @UpdateClause + '
            WHEN NOT MATCHED BY SOURCE
                THEN DELETE
            OUTPUT $action, Inserted.*, Deleted.*;
            SET IDENTITY_INSERT ' + @TableName + ' OFF'

    Print @SQL

END
7 голосов
/ 07 сентября 2011

Не все, что вы хотели, но частично:

WHEN NOT MATCHED THEN
INSERT([all fields])
VALUES (field1, field2, ...)

(Список значений должен быть полным и соответствовать порядку полей в определении вашей таблицы.)

1 голос
/ 27 ноября 2014

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

--Two statement run as transaction batch
DELETE
    C
FROM
    productschina C
    JOIN 
    (select * from productschina c except select * from productsus) z
    on c.productid=z.productid

INSERT into productschina select * from productsus except select * from productschina

Вот код для настройки таблиц для проверки выше:

--Create a target table
--drop table ProductsUS
CREATE TABLE ProductsUS
(
ProductID INT PRIMARY KEY,
ProductName VARCHAR(100),
Rate MONEY
) 
GO
--Insert records into target table
INSERT INTO ProductsUS
VALUES
(1, 'Tea', 10.00),
(2, 'Coffee', 20.00),
(3, 'Muffin', 30.00),
(4, 'Biscuit', 40.00)
GO
--Create source table
--drop table productschina
CREATE TABLE ProductsChina
(
ProductID INT PRIMARY KEY,
ProductName VARCHAR(100),
Rate MONEY
) 
GO
--Insert records into source table
INSERT INTO ProductsChina
VALUES
(1, 'Tea', 10.00),
(2, 'Coffee', 25.00),
(3, 'Muffin', 35.00),
(5, 'Pizza', 60.00)
GO
SELECT * FROM ProductsUS
SELECT * FROM ProductsChina
GO
0 голосов
/ 20 февраля 2016

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

Я подозреваю, что вы можете сэкономить немного времени, просто соединив первичный ключ и дату последнего изменения (если таковой существует).

DELETE
    C
FROM
    productschina C
    JOIN 
    (select primary_key, last_mod_date from productschina c except select primary_key, last_mod_date from productsus) z
    on c.productid=z.productid

INSERT into productschina select * from productsus except select * from productschina
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...