【问题标题】:Combining INSERT and UPDATE statement (SQL2005 Stored Procedure)结合 INSERT 和 UPDATE 语句(SQL2005 存储过程)
【发布时间】:2009-08-10 16:36:02
【问题描述】:

我需要从一个表中提取记录,将数据复制到第二个表中,然后更新第一个表中的记录以表明它们已成功复制。

我目前的 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 的第二个表中。假设这些列在 TBL_ADDRESSBOOKADDRESSES 中被简单地命名为 col_1、col_2、col_3 .... col_5,并且在 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

【问题讨论】:

  • 您使用的是哪个数据库系统?一些 DBMS 支持非常好的非标准方式。
  • tbl_campaigns 与 tbl_recipients 有何关系?我需要通过 Tbl_AddressBookAddresses 加入吗?
  • SQL Server 2008 将具有专为这种情况设计的 MERGE 语句 - 可以让您简化很多事情
  • 有人可以评论一下这个更新吗?

标签: sql sql-server sql-server-2005 tsql stored-procedures


【解决方案1】:

您是否考虑过将其全部放入事务中?

示例: 声明@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 server 会回滚,因为有一个未提交的事务

【讨论】:

  • 我从来没有处理过事务,也不知道它们是什么或如何去做。请澄清您的评论。
  • 我已经用 Try/Catch 块添加了它。这是一样的吗?尽管尝试不包括提交,但捕获中有回滚。这样可以吗?
  • 不,不一样。开始事务启动一个新事务,直到它被提交,您所做的更改对其他数据库连接不可见。当你开始一个事务时,你必须要么提交它要么回滚它,这是非常重要的要记住。
  • 那么我应该删除try catch吗?或者将 BEGIN TRAN .....COMMIT TRAN 放在 TRY/CATCH 的 TRY 元素中?
  • 请参阅我对上述问题的编辑。它将我的代码显示为带有回滚的 try/catch 的一部分。我阅读的教程建议这比每次操作后的@errorCode 更好,因为它能够记录所有错误并使用更简单的代码。请发表评论。
【解决方案2】:

我对你的结构做了一些猜测,连接可能不正确

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 *,它不应该在生产代码中使用。我还使用别名使代码更易于阅读。

【讨论】:

  • CRuello 是正确的,所有这些都应该在一个事务中,如果它的任何部分失败,它就会回滚。
  • 您可以使用 try catch 块来处理回滚过程,但您仍然必须从 begin tran 语句开始,然后确保代码中有路径来提交 tran 或回滚 tran .
【解决方案3】:

一种方法是使用OUTPUT 子句;在这种情况下,您可以选择您插入的所有地址簿 ID,并使用它来更新广告系列。不过,如果您想获得这种可靠性,您确实需要使用事务,并使用可以防止更新和插入的锁。

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

如果活动 ID 是 TBL_RECIPIENTS 表的一部分,您将 100% 确定您通过 OUTPUT 子句获得了正确的活动 ID,并且实际上不需要锁定事务。据我所知,SQL Server 不允许您在 OUTPUT 子句中引用连接表中的列(与 DELETE ... OUTPUTUPDATE ... OUTPUT 不同),因此活动 ID 必须INSERTSELECT 子句的一部分。

由于广告系列 ID 不是输出的一部分,因此此解决方案几乎是 HLGEM 答案的复杂版本,但将来可能对您有用。

【讨论】:

  • 我已经尝试了上面的代码,但由于以下错误,我的 SQL 管理器不会编译它:“插入错误:列名或提供的值的数量与表定义不匹配。”实际上,表之间的列名实际上是不同的。所以我希望它只是将列从一个表映射到下一个表的情况。请你能告诉我如何更新 INSERT 块来满足这个要求。为方便起见,假设表 A 上的 col1 是表 B 上的 colA。非常感谢!!!
  • 这正是您通常会做的: INSERT INTO TBL_RECIPIENTS([colA], ...) OUTPUT INSERTED.adds_ABMId INTO @addressBookIds SELECT TBL_ADDRESSBOOKADDRESSES.col1, ... 您只需将 OUTPUT 子句夹在中间在常规 INSERT INTO 和 SELECT 之间
【解决方案4】:

您应该将更新的地址 id 存储到使用 INSERT 的 OUTPUT 子句填充的表变量中:

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

【讨论】:

  • 感谢您抽出宝贵的时间,但如果不对正在发生的事情进行某种解释,这对我来说没有任何意义。
猜你喜欢
  • 2021-03-27
  • 1970-01-01
  • 2012-08-08
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多