【问题标题】:Using T-SQL XML input element values for an IN Clause为 IN 子句使用 T-SQL XML 输入元素值
【发布时间】:2013-03-22 15:32:24
【问题描述】:

我正在尝试编写一个将用户从 Active Directory 同步到我的本地应用程序数据库的过程。从我的代码中,我将以下格式的 XML 传递给存储过程:

<AdUsers>
  <AdUser AccountSid="S-1-5-21-111111111-111111111-111111111-1111" DisplayName="Test User" EmailAddress="tuser@mail.local" ExchangeServerFk="4" ExchangeServer="https://mail.local" Department="" StatusFK="1" UserName="TUSER">
    <AccountSids>
      <Sid>S-1-5-21-111111111-111111111-111111111-1111</Sid>
    </AccountSids>
  </AdUser>
</AdUsers>

我想使用以下存储过程在 XML 和我的 tb_Mailboxes 表中的行之间进行同步:

@adUsers XML, @lastSyncBy VARCHAR (50), @lastSyncOn DATETIME, @defaultProfileId INT, @adDomainId INT
AS

begin try
    BEGIN TRANSACTION
        --First delete all the mailboxes exist in the database but not in the xml.
        delete tb_Mailboxes
        where AccountSid not in (
            select 
                 rtrim(element.value('text()[1]', 'varchar(100)')) as AccountSid
            from 
                @adUsers.nodes('/AdUsers/AdUser/AccountSids/Sid') t(element)


        ) AND @adDomainId = AdDomainFk

        --Then insert or update existing accounts
        MERGE tb_Mailboxes as [target]
        USING 
        (
            select 
                 rtrim(element.value('data(@AccountSid)', 'varchar(100)')) as AccountSid
                ,rtrim(element.value('data(@DisplayName)', 'varchar(100)')) as DisplayName
                ,rtrim(element.value('data(@EmailAddress)', 'varchar(500)')) as EmailAddress
                ,rtrim(element.value('data(@ExchangeServerFk)', 'varchar(100)')) as ExchangeServerFk
                ,rtrim(element.value('data(@ExchangeServer)', 'varchar(150)')) as ExchangeServer
                ,rtrim(element.value('data(@Department)', 'varchar(100)')) as Department
                ,rtrim(element.value('data(@StatusFK)', 'varchar(100)')) as StatusFK
                ,rtrim(element.value('data(@UserName)', 'varchar(100)')) as UserName
                ,element.query('AccountSids') as SidList
            from 
                @adUsers.nodes('/AdUsers/AdUser') t(element)
        ) as [source] 
             on [target].AccountSid IN
             (
                SELECT rtrim(A.value('text()[1]', 'varchar(100)')) as CurSid 
                FROM [source].SidList.nodes('Sid') AS FN(A)
             )
        WHEN MATCHED THEN UPDATE SET
            DisplayName = [source].DisplayName
            ,EmailAddress = [source].EmailAddress
            ,ExchangeServerFk = [source].ExchangeServerFk
            ,ExchangeServer = [source].ExchangeServer
            ,Department = [source].Department
            ,UserName = [source].UserName   
            /*,StatusFK = [source].StatusFK*/
            ,LastSyncOn = @lastSyncOn
            ,LastSyncBy = @lastSyncBy
        WHEN NOT MATCHED THEN INSERT 
            (
                AdDomainFk, 
                UserName, 
                DisplayName, 
                Department, 
                EmailAddress, 
                ExchangeServerFk, 
                ExchangeServer, 
                AccountSid, 
                IsAutoDeleteEnabled, 
                ProfileFk, 
                Settings, 
                QueueLastPickedUp, 
                QueueLastProcessed, 
                QueueLastFinished, 
                LastSyncOn, 
                LastSyncBy,
                StatusFK
            )
            VALUES
            (
            @adDomainId
            ,[source].UserName
            ,[source].DisplayName
            ,[source].Department
            ,[source].EmailAddress
            ,[source].ExchangeServerFk
            ,[source].ExchangeServer
            ,[source].AccountSid
            ,0
            ,@defaultProfileId
            ,NULL
            ,NULL
            ,NULL
            ,NULL
            ,@lastSyncOn
            ,@lastSyncBy
            ,[source].StatusFK
            );

    COMMIT TRANSACTION
END TRY
BEGIN CATCH

    ROLLBACK TRANSACTION
END CATCH

但是,删除部分中的“NOT IN”和匹配部分中的“IN”似乎不起作用。这种在 XML 中使用多个值的 IN 子句是否可行?有没有更好的方法来解决我缺少的这个问题?

【问题讨论】:

  • 抱歉编辑问题。这篇文章现在应该可以阅读了。
  • 在执行事务时(对于插入/更新/删除或合并(更新插入)/删除),我喜欢将 xml 分解为 @ 变量或 # 临时表,然后成为 TRAN。这样,粉碎就在 TRAN 之外进行。

标签: sql-server tsql sql-server-2008-r2


【解决方案1】:

您的 MERGE 查询的问题是 [source] 和 [target] 表之间的连接。而不是使用

连接目标表和源表
     ON [target].AccountSid IN
     (
        SELECT rtrim(A.value('text()[1]', 'varchar(100)')) as CurSid 
        FROM [source].SidList.nodes('Sid') AS FN(A)
     )

改用这个:

    ON [target].AccountSid = [source].AccountSid

[source] 将被具体化为一个表,您可以像加入任何其他表一样加入它。您的 IN 语句没有多大意义,因为它是一个完全不同的实体,因此相当于一种笛卡尔联接(FULL OUTER)。

我要提出的另一条评论是,为什么要使用单独的 DELETE 语句来删除 XML 中不再存在的邮箱?为什么不简单地使用以下语句将 DELETE 放在 MERGE 语句中?

WHEN NOT MATCHED BY SOURCE 
               THEN DELETE

应用所有这些,您的 MERGE 语句变为:

MERGE tb_Mailboxes AS [target]
    USING 
        (SELECT RTRIM(element.value('data(@AccountSid)', 'varchar(100)')) AS AccountSid
              , RTRIM(element.value('data(@DisplayName)', 'varchar(100)')) AS DisplayName
              , RTRIM(element.value('data(@EmailAddress)', 'varchar(500)')) AS EmailAddress
              , RTRIM(element.value('data(@ExchangeServerFk)', 'varchar(100)')) AS ExchangeServerFk
              , RTRIM(element.value('data(@ExchangeServer)', 'varchar(150)')) AS ExchangeServer
              , RTRIM(element.value('data(@Department)', 'varchar(100)')) AS Department
              , RTRIM(element.value('data(@StatusFK)', 'varchar(100)')) AS StatusFK
              , RTRIM(element.value('data(@UserName)', 'varchar(100)')) AS UserName
         FROM   @adUsers.nodes('/AdUsers/AdUser') t (element)) AS [source]
    ON [target].AccountSid = [source].AccountSid
    WHEN MATCHED 
        THEN UPDATE
           SET      DisplayName = [source].DisplayName
                  , EmailAddress = [source].EmailAddress
                  , ExchangeServerFk = [source].ExchangeServerFk
                  , ExchangeServer = [source].ExchangeServer
                  , Department = [source].Department
                  , UserName = [source].UserName   
        /*,StatusFK = [source].StatusFK*/
                  , LastSyncOn = @lastSyncOn
                  , LastSyncBy = @lastSyncBy
    WHEN NOT MATCHED BY TARGET
        THEN INSERT (AdDomainFk
                   , UserName
                   , DisplayName
                   , Department
                   , EmailAddress
                   , ExchangeServerFk
                   , ExchangeServer
                   , AccountSid
                   , IsAutoDeleteEnabled
                   , ProfileFk
                   , Settings
                   , QueueLastPickedUp
                   , QueueLastProcessed
                   , QueueLastFinished
                   , LastSyncOn
                   , LastSyncBy
                   , StatusFK)
           VALUES   (@adDomainId
                   , [source].UserName
                   , [source].DisplayName
                   , [source].Department
                   , [source].EmailAddress
                   , [source].ExchangeServerFk
                   , [source].ExchangeServer
                   , [source].AccountSid
                   , 0
                   , @defaultProfileId
                   , NULL
                   , NULL
                   , NULL
                   , NULL
                   , @lastSyncOn
                   , @lastSyncBy
                   , [source].StatusFK)
     WHEN NOT MATCHED BY SOURCE 
         THEN DELETE;   

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    • 2013-10-03
    • 2023-03-19
    • 2010-12-01
    • 1970-01-01
    相关资源
    最近更新 更多