можно ли мне использовать MERGE

Manonia
Дата: 22.01.2013 18:27:16
Добрый день.
Хотелось бы узнать можно ли в моём случае и как, если можно, использовать операцию MERGE.

Есть табличка, в ней хранятся данные о настройках, например, телефонов. Есть хранимка, которую эту таблицу в случае надобности обновляет. Сейчас она работает так: на вход получает xml-ник с настройками, затем парсит его и ложит данные во временную табличку. Далее по id телефона удаляет из исходной таблицы все настройки данного телефона и потом вставляет новые из временной таблицы. Проблемы возникла потому что в исходной таблице есть поле, которое обнуляется при этой операции (так как его нет в приходящем xml. как я поняла, оно было добавлено позже написания хранимки). Конечно можно поправить хранимку, но это коснётся много чего и кажется муторным делом. Посоветовали сделать проще - использовать MERGE. Я посмотрела и не уверена, подойдёт ли он в моём случае. Написала тестовый скрипт, где подала, например, 5 настроек, в исходной же таблице их 10, скрипт не удалил мне лишние пять, а оставил всё как есть. Обновление вроде отрабатывает, но вот удаление - нет. Может я косячу где-то или всё-таки MERGE так не работает?
aleks2
Дата: 22.01.2013 18:30:55
Вы сильно облегчите участь здешних экстрасенсов, если процитируете свой MERGE.
Гость333
Дата: 22.01.2013 18:31:13
Manonia
Написала тестовый скрипт

... но я вам его не покажу.
Manonia
Дата: 22.01.2013 18:35:58
DECLARE @AccountID INT = 10153251
DECLARE @SourceTest TABLE (RoutingCode INT NOT NULL,
                           Priority INT NOT NULL,
                           OmniBusAccountID INT NULL)

INSERT INTO @SourceTest
SELECT 610, 14, 10118786

MERGE dbo.Accounts_Routes AS TARGET
USING (SELECT RoutingCode, Priority, OmniBusAccountID FROM @SourceTest) AS SOURCE (RoutingCode, Priority, OmniBusAccountID)
ON (TARGET.AccountID = @AccountID AND TARGET.RoutingCode = SOURCE.RoutingCode AND TARGET.Priority = SOURCE.Priority)
WHEN MATCHED AND SOURCE.OmniBusAccountID IS NULL THEN DELETE
WHEN MATCHED AND SOURCE.OmniBusAccountID IS NOT NULL THEN UPDATE SET OmniBusAccountID = SOURCE.OmniBusAccountID
WHEN NOT MATCHED AND SOURCE.OmniBusAccountID IS NOT NULL THEN INSERT (AccountID, RoutingCode, Priority, OmniBusAccountID) VALUES (@AccountID, SOURCE.RoutingCode, SOURCE.Priority, SOURCE.OmniBusAccountID);
Manonia
Дата: 22.01.2013 18:37:21
Правда я не совсем уверена нужна ли проверка SOURCE.OmniBusAccountID на NULL, поставила по подобию примера
Manonia
Дата: 22.01.2013 18:39:07
В данном случае, поменяться может только поле OmnibusAccountID(либо число, либо NULL). RoutingCode и priority поменяться не могут.
Кот Матроскин
Дата: 22.01.2013 18:59:28
Manonia,

не совсем понял - Вам же все равно придется править Вашу процедуру для внесения ?
Кот Матроскин
Дата: 22.01.2013 19:02:04
(пардон, сорвалось сообщение).

... для внесения MERGE? MERGE в Вашем случае никаких волшебных возможностей не даст, просто позволит записать код проверки и удаления/обновления чуть короче.
Владимир Затуливетер
Дата: 22.01.2013 19:10:37
Manonia,

Не знаю что на самом деле вам нужно точно, просто попытаюсь предположить.

В следущем скрипте в пределах акаунта все записи будут переписываться/добавляться и удаляться будут те которых нет в переданом наборе (source). Записи остальных акаунтов будут не тронуты.

Можно это все написать и через update/insert/delete если эти операторы более знакомы вам.

-- подготовим таблицу
create table dbo.Accounts_Routes (
      AccountID int
	, RoutingCode int not null
    , Priority int not null
    , OmniBusAccountID int null
)
go


-- тесты

-- что было в таблице
select * from dbo.Accounts_Routes

   
declare @AccountID int = 10153253

declare @SourceTest table (
      RoutingCode int not null
    , Priority int not null
    , OmniBusAccountID int null
    )

insert  into @SourceTest
values ( 620, 14, 10118786 )
     , ( 621, 10, 1011 )
     , ( 622, 11, 18786 )

;with cteTarget 
as (
	select *
	from dbo.Accounts_Routes
	where AccountID = @AccountID -- фильтр по акаунту, удалять данные в остальных акаунтах нельзя 
)
merge cteTarget as t
    using 
        ( select    RoutingCode
                  , Priority
                  , OmniBusAccountID
          from      @SourceTest
        ) as s ( RoutingCode, Priority, OmniBusAccountID )
    on  t.RoutingCode = s.RoutingCode
         and t.Priority = s.Priority
    when matched -- если совпало
        then update
          set OmniBusAccountID = s.OmniBusAccountID
    when not matched -- если не совпало
        then insert (
                      AccountID
                    , RoutingCode
                    , Priority
                    , OmniBusAccountID
                    )
          values    ( @AccountID
                    , s.RoutingCode
                    , s.Priority
                    , s.OmniBusAccountID
                    )
	when not matched by source -- удаляем лишнее в пределах акаунта
		then delete;
go

-- что получилось
select * from dbo.Accounts_Routes
iap
Дата: 22.01.2013 21:38:13
Manonia
ложит данные во временную табличку.
Дальше не читал