Объединение оператора INSERT и UPDATE (хранимая процедура SQL2005) - PullRequest
2 голосов
/ 10 августа 2009

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

Мой текущий код SP:

SELECT TBL_ADDRESSBOOKADDRESSES.* FROM TBL_ADDRESSBOOKADDRESSES 
INNER JOIN TBL_CAMPAIGNS
ON TBL_ADDRESSBOOKADDRESSES.adds_ABMId = TBL_CAMPAIGNS.campaign_AddressBook
WHERE TBL_CAMPAIGNS.campaign_Status = 1

Теперь, когда все вышеперечисленное выполнено, мне нужно вставить эти данные во вторую таблицу с именем TBL_RECIPIENTS. Предположим, что столбцы просто называются col_1, col_2, col_3 .... col_5 в TBL_ADDRESSBOOKADDRESSES и что это то же самое в TBL_RECIPIENTS.

После выполнения этого действия мне нужно обновить TBL_CAMPAIGNS.campaign_Status = 2 В идеале это должно быть только для тех записей, которые действительно были обновлены (в случае, если сценарий останавливается на полпути из-за сбоя сервера и т. Д.)

Пожалуйста, дайте мне знать, если вам нужно что-то прояснить.

Большое спасибо!


Я принял совет, любезно предоставленный ниже, и предложил рабочий код ниже. Я читал учебник, в котором предлагалось добавить try / catch, чтобы обеспечить откат в случае возникновения ошибок. Является ли мой код ниже адекватным в этом отношении ??

Любое предложение будет с благодарностью принято.

Спасибо.

CREATE PROCEDURE web.SERVER_create_email_recipients
AS
BEGIN TRY
  --sets (n) campaigns ready for transfer of emails to mailing list
      UPDATE TOP(1) TBL_CAMPAIGNS
  SET TBL_CAMPAIGNS.campaign_Status = 1
  WHERE TBL_CAMPAIGNS.campaign_Status = 0

  --finds above marked campaigns, retreives addresses then copies them to     TBL_CAMPAIGNRECIPIENTS ready for auto mailout
  INSERT TBL_CAMPAIGNRECIPIENTS (recip_CampaignId, recip_Email, recip_Forename, recip_Surname, recip_adds_Key)
  SELECT C.Campaign_AddressBook, ABA.adds_Email, ABA.adds_RecipientForename,     ABA.adds_RecipientSurname, ABA.adds_Key
  FROM TBL_ADDRESSBOOKADDRESSES ABA
  JOIN TBL_CAMPAIGNS C ON ABA.adds_ABMId = C.campaign_AddressBook
  WHERE C.campaign_Status = 1

  --checks that above emails have been copied across and then sets the campaigns status accordingly
  UPDATE C
  SET C.campaign_Status = 2
  From TBL_CAMPAIGNS C
  JOIN TBL_ADDRESSBOOKADDRESSES  aba
  ON aba.adds_ABMId = C.campaign_AddressBook
  JOIN TBL_CAMPAIGNRECIPIENTS r on aba.adds_Key = r.recip_adds_Key
  WHERE C.campaign_Status = 1

END TRY
BEGIN CATCH
  -- Whoops, there was an error
  IF @@TRANCOUNT > 0
     ROLLBACK

  -- Raise an error with the details of the exception
  DECLARE @ErrMsg nvarchar(4000), @ErrSeverity int
  SELECT @ErrMsg = ERROR_MESSAGE(),
         @ErrSeverity = ERROR_SEVERITY()

  --throws out error to logs?
  RAISERROR(@ErrMsg, @ErrSeverity, 1)
END CATCH

Ответы [ 4 ]

4 голосов
/ 10 августа 2009

Рассматривали ли вы поместить все это в транзакцию?

Пример: ОБЪЯВИТЬ @ErrorCode INT

BEGIN TRAN
  UPDATE Authors
  SET Phone = '911'
  WHERE au_id = 2

  SELECT @ErrorCode = @@ERROR
  IF (@intErrorCode <> 0) GOTO ErrExit

  DELETE Authors WHERE Phone = '911' au_id <> 2 

  SELECT @ErrorCode = @@ERROR
  IF @ErrorCode <> 0) GOTO ErrExit
COMMIT TRAN

ErrExit;
IF (@intErrorCode <> 0) 

ROLLBACK TRAN

Если в результате обновления или удаления возникнет ошибка, транзакция будет отменена. Если система аварийно завершает работу перед тем, как сервер sql выполнит откат, потому что есть незафиксированная транзакция

2 голосов
/ 10 августа 2009

Я сделал некоторые предположения о вашей структуре, соединения могут быть неправильными

INSERT TBL_RECIPIENTS (Col1, Col2, COl3)
SELECT ABA.Col1, ABA.Col2,ABA.Col3
FROM TBL_ADDRESSBOOKADDRESSES ABA
INNER JOIN TBL_CAMPAIGNS C ON ABA.adds_ABMId = C.campaign_AddressBook
WHERE TBL_CAMPAIGNS.campaign_Status = 1

UPDATE C
SET C.campaign_Status = 2
From TBL_CAMPAIGNS C
JOIN TBL_ADDRESSBOOKADDRESSES  aba
 on aba.adds_ABMId = C.campaign_AddressBook
JOIN TBL_RECIPIENTS r on aba.id = r.sameid
WHERE TBL_CAMPAIGNS.campaign_Status = 1

Примечание. Я не использовал select *, который никогда не должен использоваться в рабочем коде. Также я использовал псевдонимы для облегчения чтения кода.

1 голос
/ 10 августа 2009

Одним из способов является использование предложения OUTPUT; в этом случае вы можете выбрать все идентификаторы адресной книги, которые вы вставили, и использовать их для обновления кампаний. Тем не менее, вам действительно нужно использовать транзакции, если вы хотите получить эту надежность, с блокировкой, которая предотвратит как обновления, так и вставки .

DECLARE @addressBookIds TABLE(AddressBookId INT NOT NULL)

INSERT INTO TBL_RECIPIENTS
OUTPUT INSERTED.adds_ABMId INTO @addressBookIds
SELECT TBL_ADDRESSBOOKADDRESSES.* 
FROM   TBL_ADDRESSBOOKADDRESSES
INNER JOIN TBL_CAMPAIGNS
  ON   TBL_ADDRESSBOOKADDRESSES.adds_ABMId = TBL_CAMPAIGNS.campaign_AddressBook
WHERE  TBL_CAMPAIGNS.campaign_Status = 1

UPDATE TBL_CAMPAIGNS
SET [campaign_Status] = 2
FROM TBL_CAMPAIGNS
INNER JOIN @addressBookIds AS T
ON TBL_CAMPAIGNS.campaign_AddressBook = T.AddressBookId
AND TBL_CAMPAIGNS.campaign_Status = 1

Если бы идентификатор кампании был частью таблицы TBL_RECIPIENTS, вы были бы на 100% уверены, что получили правильные идентификаторы кампании с помощью условия OUTPUT, и блокировка транзакций на самом деле не потребуется. Насколько я могу судить, SQL Server не позволяет ссылаться на столбцы из соединенных таблиц в предложении OUTPUT (в отличие от DELETE ... OUTPUT и UPDATE ... OUTPUT), поэтому идентификатор кампании должен быть частью из пункта INSERT * SELECT.

Поскольку идентификатор кампании не является частью выходных данных, это решение в значительной степени является сложной версией ответа HLGEM, но может пригодиться вам в будущем.

0 голосов
/ 10 августа 2009

Вы должны сохранить обновленный идентификатор адреса в табличной переменной, заполненной с помощью предложения OUTPUT в INSERT:

create table TBL_ADDRESSBOOKADDRESSES (
    adds_ABMID int identity(1,1) not null,
    col_1 varchar(100),
    col_2 varchar(100));
go

create table TBL_RECIPIENTS (
    adds_ABMID int not null,    
    col_1 varchar(100),
    col_2 varchar(100));
go

create table TBL_CAMPAIGNS (
    campaign_AddressBook int,
    campaign_Status int);
go

insert into TBL_ADDRESSBOOKADDRESSES (col_1, col_2)
    values ('spam', 'is evil');
insert into TBL_ADDRESSBOOKADDRESSES (col_1, col_2)
    values ('all mass mail', 'is spam');


insert into TBL_CAMPAIGNS (campaign_AddressBook, campaign_Status)
    values (1,1);
insert into TBL_CAMPAIGNS (campaign_AddressBook, campaign_Status)
    values (2,1);
go  

set nocount on;
declare @newRecipients table (adds_ABMID int);
begin transaction
insert into TBL_RECIPIENTS (adds_ABMID, col_1, col_2)
    output inserted.adds_ABMID into @newRecipients
    SELECT a.adds_ABMID
        , a.col_1
        , a.col_2
    FROM TBL_ADDRESSBOOKADDRESSES a 
    INNER JOIN TBL_CAMPAIGNS ON a.adds_ABMId = TBL_CAMPAIGNS.campaign_AddressBook
    WHERE TBL_CAMPAIGNS.campaign_Status = 1
update TBL_CAMPAIGNS 
    set campaign_Status = 2
    from TBL_CAMPAIGNS as c
    join @newRecipients as new on c.campaign_AddressBook = new.adds_ABMID;
commit; 
go

select * from TBL_RECIPIENTS;
select * from TBL_CAMPAIGNS;
go
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...