1

我正在尝试编写一个将用户从 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 子句是否可行?有没有更好的方法来解决我缺少的这个问题?

4

1 回答 1

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

[来源] 将被具体化为一个表,您可以像加入任何其他表一样加入它。您的 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;   
于 2013-04-11T23:21:25.350 回答