MERGE (Transact-SQL)
Выполняет операции вставки, обновления или удаления для целевой таблицы на основе результатов соединения с исходной таблицей. Например, можно синхронизировать две таблицы путем вставки, обновления или удаления строк в одной таблице на основании отличий, найденных в другой таблице.
Синтаксис
[ WITH <common_table_expression> [,...n] ]
MERGE
[ TOP ( expression ) [ PERCENT ] ]
[ INTO ] <target_table> [ WITH ( <merge_hint> ) ] [ [ AS ] table_alias ]
USING <table_source>
ON <merge_search_condition>
[ WHEN MATCHED [ AND <clause_search_condition> ]
THEN <merge_matched> ] [...n ]
[ WHEN NOT MATCHED [ BY TARGET ] [ AND <clause_search_condition> ]
THEN <merge_not_matched> ]
[ WHEN NOT MATCHED BY SOURCE [ AND <clause_search_condition> ]
THEN <merge_matched> ] [...n ]
[ <output_clause> ]
[ OPTION ( <query_hint> [ ,...n ] ) ]
;
<target_table> ::=
{
[ database_name . schema_name . | schema_name . ]
target_table
}
<merge_hint>::=
{
{ [ <table_hint_limited> [ ,...n ] ]
[ [ , ] INDEX ( index_val [ ,...n ] ) ] }
}
<table_source> ::=
{
table_or_view_name [ [ AS ] table_alias ] [ <tablesample_clause> ]
[ WITH ( table_hint [ [ , ]...n ] ) ]
| rowset_function [ [ AS ] table_alias ]
[ ( bulk_column_alias [ ,...n ] ) ]
| user_defined_function [ [ AS ] table_alias ]
| OPENXML <openxml_clause>
| derived_table [ AS ] table_alias [ ( column_alias [ ,...n ] ) ]
| <joined_table>
| <pivoted_table>
| <unpivoted_table>
}
<merge_search_condition> ::=
<search_condition>
<merge_matched>::=
{ UPDATE SET <set_clause> | DELETE }
<set_clause>::=
SET
{ column_name = { expression | DEFAULT | NULL }
| { udt_column_name.{ { property_name = expression
| field_name = expression }
| method_name ( argument [ ,...n ] ) }
}
| column_name { .WRITE ( expression , @Offset , @Length ) }
| @variable = expression
| @variable = column = expression
| column_name { += | -= | *= | /= | %= | &= | ^= | |= } expression
| @variable { += | -= | *= | /= | %= | &= | ^= | |= } expression
| @variable = column { += | -= | *= | /= | %= | &= | ^= | |= } expression
} [ ,...n ]
<merge_not_matched>::=
{
INSERT [ ( column_list ) ]
{ VALUES ( values_list )
| DEFAULT VALUES }
}
<clause_search_condition> ::=
<search_condition>
<search condition> ::=
{ [ NOT ] <predicate> | ( <search_condition> ) }
[ { AND | OR } [ NOT ] { <predicate> | ( <search_condition> ) } ]
[ ,...n ]
<predicate> ::=
{ expression { = | < > | ! = | > | > = | ! > | < | < = | ! < } expression
| string_expression [ NOT ] LIKE string_expression
[ ESCAPE 'escape_character' ]
| expression [ NOT ] BETWEEN expression AND expression
| expression IS [ NOT ] NULL
| CONTAINS
( { column | * } , '< contains_search_condition >' )
| FREETEXT ( { column | * } , 'freetext_string' )
| expression [ NOT ] IN ( subquery | expression [ ,...n ] )
| expression { = | < > | ! = | > | > = | ! > | < | < = | ! < }
{ ALL | SOME | ANY} ( subquery )
| EXISTS ( subquery ) }
<output_clause>::=
{
[ OUTPUT <dml_select_list> INTO { @table_variable | output_table }
[ (column_list) ] ]
[ OUTPUT <dml_select_list> ]
}
<dml_select_list>::=
{ <column_name> | scalar_expression }
[ [AS] column_alias_identifier ] [ ,...n ]
<column_name> ::=
{ DELETED | INSERTED | from_table_name } . { * | column_name }
| $action
Аргументы
WITH <common_table_expression>
Указывает определенные в области инструкции MERGE временные именованные результирующие набор или представление, которые называются обобщенным табличным выражением. Результирующий набор, на который ссылается инструкция MERGE, является производным простого запроса. Дополнительные сведения см. в разделе WITH обобщенное_табличное_выражение (Transact-SQL).TOP ( expression ) [ PERCENT ]
Указывает количество или процент строк, которые подпадают под эту операцию. expression может быть либо числом, либо процентной долей строк. Строки, на которые ссылается выражение TOP, не расположены в определенном порядке. Дополнительные сведения см. в разделе TOP (Transact-SQL).Предложение TOP применяется после соединения всей исходной таблицы и всей целевой таблицы и удаления соединенных строк, которые не рассматриваются как предназначенные для выполнения операций вставки, обновления или удаления. Предложение TOP дополнительно сокращает количество соединенных строк до указанного значения, а затем к оставшимся соединенным строкам применяются операции вставки, обновления или удаления без учета порядка. Иными словами, порядок, в котором строки подвергаются операциям, определенным в предложениях WHEN, не задан. Например, указание значения TOP (10) затрагивает 10 строк. Из них 7 могут быть обновлены и 3 вставлены или одна строка может быть удалена, 5 обновлено, 4 вставлено и т. д.
Инструкция MERGE выполняет полный просмотр исходной и целевой таблиц, поэтому при использовании предложения TOP для изменения большой таблицы путем создания нескольких пакетов производительность ввода-вывода может снизиться. В этом случае необходимо обеспечить, чтобы во всех подряд идущих пакетах осуществлялась обработка новых строк. Дополнительные сведения см. в разделе Оптимизация производительности инструкции MERGE.
database_name
Имя базы данных, в которой расположена таблица target_table.schema_name
Имя схемы, которой принадлежит таблица target_table.target_table
Таблица или представление, с которыми выполняется сопоставление строк данных из таблицы <table_source> по условию <clause_search_condition>. Параметр target_table является целевым объектом любых операций вставки, обновления или удаления, указанных предложениями WHEN инструкции MERGE.Если аргумент target_table является представлением, то все действия, выполняемые с ним, должны удовлетворять условиям для обновления представлений. Дополнительные сведения см. в разделе Изменение данных через представление.
Таблица target_table не может быть удаленной, и для нее не могут быть определены правила.
[AS] table_alias
Альтернативное имя, используемое для указания ссылок на эту таблицу.USING <table_source>
Указывается источник данных, который сопоставляется со строками данных в таблице target_table на основе условия <merge_search condition>. Результат этого совпадения обуславливает действия, которые выполняются предложениями WHEN инструкции MERGE. Аргумент <table_source> может быть удаленной таблицей или производной таблицей, которая обращается к удаленным таблицам.Аргументом <table_source> может быть производная таблица, использующая конструктор табличных значенийTransact-SQL для построения таблицы путем указания нескольких строк.
Дополнительные сведения о синтаксисе и аргументах этого предложения см. в разделе Предложение FROM (Transact-SQL).
ON <merge_search_condition>
Указываются условия, при которых таблица <table_source> соединяется с таблицей target_table для сопоставления.Внимание! Важно указать только те столбцы из целевой таблицы, которые используются для поиска совпадений. Иными словами, необходимо указать столбцы целевой таблицы, которые сравниваются с соответствующим столбцом исходной таблицы. Не рекомендуется повышать производительность запроса за счет фильтрации строк в целевой таблице в предложении ON, как например, при указании AND NOT target_table.column_x = value. Это может привести к получению непредвиденных и неверных результатов.
WHEN MATCHED THEN <merge_matched>
Указывается, что все строки target_table, которые соответствуют строкам, возвращенным <table_source> ON <merge_search_condition>, и удовлетворяют дополнительным условиям поиска, обновляются или удаляются в соответствии с предложением <merge_matched>.Инструкция MERGE может иметь не больше двух предложений WHEN MATCHED. Если указаны два предложения, первое предложение должно сопровождаться предложением AND <search_condition>. Для любой строки второе предложение WHEN MATCHED применяется только в тех случаях, когда не применяется первое. Если имеются два предложения WHEN MATCHED, одно должно указывать действие UPDATE, а другое — действие DELETE. Если действие UPDATE указано в предложении <merge_matched> и более одной строки в <table_source>соответствует строке в target_table на основе <merge_search_condition>, то SQL Server возвращает ошибку. Инструкцию MERGE нельзя использовать для обновления одной строки более одного раза, а также использовать для обновления и удаления одной и той же строки.
WHEN NOT MATCHED [ BY TARGET ] THEN <merge_not_matched>
Указывает, что строка вставлена в таблицу target_table для каждой строки, возвращенной выражением <table_source> ON <merge_search_condition>, которые не соответствуют строке в таблице target_table, но удовлетворяют дополнительному условию поиска (при наличии такового). Значения для вставки указываются с помощью предложения <merge_not_matched>. Инструкция MERGE может иметь только одно предложение WHEN MATCHED.WHEN NOT MATCHED BY SOURCE THEN <merge_matched>
Указывается, что все строки target_table, которые не соответствуют строкам, возвращенным <table_source> ON <merge_search_condition>, и удовлетворяют дополнительным условиям поиска, обновляются или удаляются в соответствии с предложением <merge_matched>.Инструкция MERGE может иметь не более двух предложений WHEN NOT MATCHED BY SOURCE. Если указаны два предложения, то первое предложение должно сопровождаться предложением AND <clause_search_condition>. Для любой выбранной строки второе предложение WHEN NOT MATCHED BY SOURCE применяется только в тех случаях, если не применяется первое. Если имеется два предложения WHEN NOT MATCHED BY SOURCE, то одно должно указывать действие UPDATE, а другое — действие DELETE. В условии <clause_search_condition> можно ссылаться только на столбцы целевой таблицы.
Если строки не возвращаются таблицей <table_source>, к столбцам в исходной таблице не может быть предоставлен доступ. Если операции обновления или удаления указываются в <merge_matched>, предложение ссылается на исходную таблицу и возвращается ошибка 207 (недопустимое имя столбца). Например, предложение WHEN NOT MATCHED BY SOURCE THEN UPDATE SET TargetTable.Col1 = SourceTable.Col1 может стать причиной ошибки инструкции из-за недоступности столбца Col1 в исходной таблице.
AND <clause_search_condition>
Указывается любое действительное условие поиска. Дополнительные сведения см. в разделе Условие поиска (Transact-SQL).<table_hint_limited>
Указывается одна или несколько табличных подсказок, которые применяются в целевой таблице для каждой операции вставки, обновления или удаления, которые выполняются инструкцией MERGE. Необходимо использовать ключевое слово WITH и круглые скобки.Использование аргументов NOLOCK и READUNCOMMITTED запрещено. Дополнительные сведения о табличных подсказках см. в разделе Табличные подсказки (Transact-SQL).
Указание подсказки TABLOCK для целевой таблицы инструкции INSERT приведет к тем же последствиям, что и указание подсказки TABLOCKX. К таблице будет применена монопольная блокировка. Если указано FORCESEEK, то оно применяется к явному экземпляру целевой таблицы, соединенной с исходной таблицей.
Внимание! Указание READPAST с предложением WHEN NOT MATCHED [ BY TARGET ] THEN INSERT может привести к выполнению операций INSERT, которые нарушают ограничения UNIQUE.
INDEX ( index_val [ ,...n ] )
Указывается имя или идентификатор одного или нескольких индексов целевой таблицы для выполнения явного соединения с исходной таблицей. Дополнительные сведения см. в разделе Табличные подсказки (Transact-SQL).<output_clause>
Возвращает строку для каждой строки в target_table, которая обновляется, вставляется или удаляется без определенного порядка. Дополнительные сведения об аргументах этого предложения см. в разделе Предложение OUTPUT (Transact-SQL).OPTION ( <query_hint> [ ,...n ] )
Указывает, что для настройки способа, которым компонент Database Engine обрабатывает инструкцию, используются подсказки оптимизатора. Дополнительные сведения см. в разделе Подсказки в запросах (Transact-SQL).<merge_matched>
Указывает действие обновления или удаления, которое применяется ко всем строкам target_table, не соответствующим строкам, которые возвращаются <table_source> ON <merge_search_condition> и удовлетворяют каким-либо дополнительным условиям поиска.UPDATE SET <set_clause>
Указывается список имен столбцов или переменных, которые необходимо обновить в целевой таблице, и значений, которые необходимо использовать для их обновления.Дополнительные сведения об аргументах этого предложения см. в разделе UPDATE (Transact-SQL). Присваивание переменной того же значения, что и столбцу, не разрешается.
DELETE
Указывается, что строки, совпадающие со строками в target_table, удаляются.
<merge_not_matched>
Указываются значения для вставки в целевую таблицу.( column_list )
Список, состоящий из одного или нескольких столбцов целевой таблицы, в которые вставляются данные. Столбцы необходимо указывать в виде однокомпонентного имени, так как в противном случае инструкция MERGE возвращает ошибку. Аргумент column_list должен быть заключен в скобки и разделен запятыми.VALUES ( values_list )
Список с разделителями-запятыми ограничений, переменных или выражений, которые возвращают значения для вставки в целевую таблицу. Выражения не могут содержать инструкцию EXECUTE.DEFAULT VALUES
Заполняет вставленную строку значениями по умолчанию, определенными для каждого столбца.
Дополнительные сведения об этом предложении см. в разделе Инструкция INSERT (Transact-SQL).
<search condition>
Указываются условия поиска, используемые для указания <merge_search_condition> или <clause_search_condition>. Дополнительные сведения об аргументах этого предложения см. в разделе Условие поиска (Transact-SQL).
Замечания
Должно быть указано по крайней мере одно из трех предложений MATCHED, но они могут быть указаны в любом порядке. В одном предложении MATCHED переменная не может быть обновлена больше одного раза.
На все операции удаления, вставки или обновления, указанные применительно к целевой таблице инструкции MERGE, распространяются все ограничения, определенные для этой таблицы, включая все каскадные ограничения ссылочной целостности. Если IGNORE_DUP_KEY имеет значение ON для всех уникальных индексов в целевой таблице, то в инструкции MERGE этот параметр не учитывается.
Чтобы использовать инструкцию MERGE, необходима точка с запятой (;) как признак конца инструкции. Возникает ошибка 10713, если инструкция MERGE выполняется без признака конца конструкции.
Если функция @@ROWCOUNT (Transact-SQL) используется после инструкции MERGE, она возвращает общее количество вставленных, обновленных и удаленных строк из клиента.
Ключевое слово MERGE полностью резервируется, если уровень совместимости базы данных установлен в значение 100. Инструкция MERGE доступна при уровне совместимости базы данных 90 и 100, однако это ключевое слово не полностью зарезервировано при уровне совместимости 90.
Реализация триггера
Для каждой операции вставки, обновления или удаления, указанной в инструкции MERGE, SQL Server запускает все соответствующие триггеры AFTER, определенные для целевой таблицы, но не гарантирует определенного порядка запуска триггеров. Триггеры, которые определены для одного и того же действия, реализуются в порядке, указанном пользователем. Дополнительные сведения о настройке порядка выполнения триггеров см. в разделе Указание первого и последнего триггеров.
Если в целевой таблице включен триггер INSTEAD OF для операций вставки, обновления или удаления, выполняемых инструкцией MERGE, то должен быть включен триггер INSTEAD OF для всех операций, указанных в инструкции MERGE.
Если в таблице target_table определены триггеры INSTEAD OF UPDATE или INSTEAD OF DELETE, то операции обновления или удаления не выполняются. Вместо этого запускаются триггеры, а таблицы inserted и deleted заполняются соответствующим образом.
Если в таблице target_table определены триггеры INSTEAD OF INSERT, то операции вставки не выполняются. Вместо этого запускаются триггеры, и таблица inserted заполняется соответствующим образом.
Разрешения
Необходимо разрешение SELECT для исходной таблицы и разрешения INSERT, UPDATE или DELETE для целевой таблицы. Дополнительные сведения см. в подразделах SELECT, INSERT, UPDATE и DELETE раздела «Разрешения».
Примеры
А. Использование инструкции MERGE для выполнения операций INSERT и UPDATE в таблице в одной инструкции
Чаще всего производится обновление одного или нескольких столбцов в таблице, если существуют совпадающие строки, либо вставка данных в новую строку, если совпадающих строк не существует. Это обычно выполняется путем передачи параметров хранимой процедуре, содержащей подходящие инструкции UPDATE и INSERT. Используя инструкцию MERGE, обе эти задачи можно реализовать в одной инструкции. В следующем примере показывается хранимая процедура, содержащая инструкции INSERT и UPDATE. Затем процедура изменяется для выполнения эквивалентных операций с использованием единственной инструкции MERGE.
USE AdventureWorks;
GO
CREATE PROCEDURE dbo.InsertUnitMeasure
@UnitMeasureCode nchar(3),
@Name nvarchar(25)
AS
BEGIN
SET NOCOUNT ON;
-- Update the row if it exists.
UPDATE Production.UnitMeasure
SET Name = @Name
WHERE UnitMeasureCode = @UnitMeasureCode
-- Insert the row if the UPDATE statement failed.
IF (@@ROWCOUNT = 0 )
BEGIN
INSERT INTO Production.UnitMeasure (UnitMeasureCode, Name)
VALUES (@UnitMeasureCode, @Name)
END
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'Test Value';
SELECT UnitMeasureCode, Name FROM Production.UnitMeasure
WHERE UnitMeasureCode = 'ABC';
GO
-- Rewrite the procedure to perform the same operations using the MERGE statement.
-- Create a temporary table to hold the updated or inserted values from the OUTPUT clause.
CREATE TABLE #MyTempTable
(ExistingCode nchar(3),
ExistingName nvarchar(50),
ExistingDate datetime,
ActionTaken nvarchar(10),
NewCode nchar(3),
NewName nvarchar(50),
NewDate datetime
);
GO
ALTER PROCEDURE dbo.InsertUnitMeasure
@UnitMeasureCode nchar(3),
@Name nvarchar(25)
AS
BEGIN
SET NOCOUNT ON;
MERGE Production.UnitMeasure AS target
USING (SELECT @UnitMeasureCode, @Name) AS source (UnitMeasureCode, Name)
ON (target.UnitMeasureCode = source.UnitMeasureCode)
WHEN MATCHED THEN
UPDATE SET Name = source.Name
WHEN NOT MATCHED THEN
INSERT (UnitMeasureCode, Name)
VALUES (source.UnitMeasureCode, source.Name)
OUTPUT deleted.*, $action, inserted.* INTO #MyTempTable;
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'New Test Value';
EXEC InsertUnitMeasure @UnitMeasureCode = 'XYZ', @Name = 'Test Value';
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'Another Test Value';
SELECT * FROM #MyTempTable;
-- Cleanup
DELETE FROM Production.UnitMeasure WHERE UnitMeasureCode IN ('ABC','XYZ');
DROP TABLE #MyTempTable;
Go
Б. Использование инструкции MERGE для выполнения операций UPDATE и DELETE в таблице в одной инструкции
В следующем примере инструкция MERGE используется для ежедневного обновления таблицы ProductInventory в образце базы данных AdventureWorks на основе заказов, обработанных в таблице SalesOrderDetail. Столбец Quantity таблицы ProductInventory обновляется путем вычитания количества заказов на каждый продукт, которые размещаются в течение дня в таблице SalesOrderDetail. Если количество заказов на продукт таково, что уровень запасов продукта опускается до нуля или становится еще ниже, то строка этого продукта удаляется из таблицы ProductInventory.
USE AdventureWorks;
GO
IF OBJECT_ID (N'Production.usp_UpdateInventory', N'P') IS NOT NULL DROP PROCEDURE Production.usp_UpdateInventory;
GO
CREATE PROCEDURE Production.usp_UpdateInventory
@OrderDate datetime
AS
MERGE Production.ProductInventory AS target
USING (SELECT ProductID, SUM(OrderQty) FROM Sales.SalesOrderDetail AS sod
JOIN Sales.SalesOrderHeader AS soh
ON sod.SalesOrderID = soh.SalesOrderID
AND soh.OrderDate = @OrderDate
GROUP BY ProductID) AS source (ProductID, OrderQty)
ON (target.ProductID = source.ProductID)
WHEN MATCHED AND target.Quantity - source.OrderQty <= 0
THEN DELETE
WHEN MATCHED
THEN UPDATE SET target.Quantity = target.Quantity - source.OrderQty,
target.ModifiedDate = GETDATE()
OUTPUT $action, Inserted.ProductID, Inserted.Quantity, Inserted.ModifiedDate, Deleted.ProductID,
Deleted.Quantity, Deleted.ModifiedDate;
GO
EXECUTE Production.usp_UpdateInventory '20030501'
В. Использование инструкции MERGE для выполнения операций UPDATE и INSERT применительно к целевой таблице с помощью производной исходной таблицы
В следующем примере инструкция MERGE используется для изменения таблицы SalesReason путем обновления или вставки строк. Если значение NewName в исходной таблице соответствует значению в столбце Name целевой таблицы (SalesReason), то в целевой таблице обновляется столбец ReasonType. Если значение NewName не совпадает со значением в целевой таблице, исходная строка вставляется в целевую таблицу. Исходная таблица является производной таблицей, в которой используется конструктор табличных значений Transact-SQL для указания нескольких строк исходной таблицы. Дополнительные сведения об использовании конструктора табличных значений в производной таблице см. в разделе конструктор табличных значений. В примере также показано, как сохранить результаты предложения OUTPUT в табличной переменной, а затем составить сводку результатов инструкцию MERGE, выполняя простую операцию выборки, которая возвращает количество вставленных и обновленных строк.
USE AdventureWorks;
GO
-- Create a temporary table variable to hold the output actions.
DECLARE @SummaryOfChanges TABLE(Change VARCHAR(20));
MERGE INTO Sales.SalesReason AS Target
USING (VALUES ('Recommendation','Other'), ('Review', 'Marketing'), ('Internet', 'Promotion'))
AS Source (NewName, NewReasonType)
ON Target.Name = Source.NewName
WHEN MATCHED THEN
UPDATE SET ReasonType = Source.NewReasonType
WHEN NOT MATCHED BY TARGET THEN
INSERT (Name, ReasonType) VALUES (NewName, NewReasonType)
OUTPUT $action INTO @SummaryOfChanges;
-- Query the results of the table variable.
SELECT Change, COUNT(*) AS CountPerChange
FROM @SummaryOfChanges
GROUP BY Change;
Г. Вставка результатов инструкции MERGE в другую таблицу
В следующем примере производится отслеживание данных, возвращаемых предложением OUTPUT инструкции MERGE, а затем осуществляется вставка этих данных в другую таблицу. Инструкция MERGE обновляет столбец Quantity таблицы ProductInventory с учетом заказов, обработанных в таблице SalesOrderDetail. В этом примере производится отслеживание обновляемых строк и их вставка в другую таблицу, которая используется для отслеживания изменений запасов.
USE AdventureWorks;
GO
CREATE TABLE Production.UpdatedInventory
(ProductID INT NOT NULL, LocationID int, NewQty int, PreviousQty int,
CONSTRAINT PK_Inventory PRIMARY KEY CLUSTERED (ProductID, LocationID));
GO
INSERT INTO Production.UpdatedInventory
SELECT ProductID, LocationID, NewQty, PreviousQty
FROM
( MERGE Production.ProductInventory AS pi
USING (SELECT ProductID, SUM(OrderQty)
FROM Sales.SalesOrderDetail AS sod
JOIN Sales.SalesOrderHeader AS soh
ON sod.SalesOrderID = soh.SalesOrderID
AND soh.OrderDate BETWEEN '20030701' AND '20030731'
GROUP BY ProductID) AS src (ProductID, OrderQty)
ON pi.ProductID = src.ProductID
WHEN MATCHED AND pi.Quantity - src.OrderQty >= 0
THEN UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty
WHEN MATCHED AND pi.Quantity - src.OrderQty <= 0
THEN DELETE
OUTPUT $action, Inserted.ProductID, Inserted.LocationID, Inserted.Quantity AS NewQty, Deleted.Quantity AS PreviousQty)
AS Changes (Action, ProductID, LocationID, NewQty, PreviousQty) WHERE Action = 'UPDATE';
GO
Дополнительные примеры см. в разделах Добавление, обновление и удаление данных с помощью инструкции MERGE и Оптимизация производительности инструкции MERGE.
См. также