Here's a Script to Merge Duplicate Computers

Discussion in 'Notification Server' started by JeremyDallas, Sep 30, 2005.

  1. JeremyDallas SVS Scholar

    PLEASE SEE STEVE_OAKES UPDATED POST FURTHER DOWN

    Found this and thought it might be helpful...


    Question:
    How do I automatically Merge all Duplicate Computers found in the Merge Computers report-utility (located under the Configuration Tab, Server Settings, Notification Server Settings, Merge Computers, and Merge computers with duplicate names)?

    Answer:
    The following script will automatically merge all resouces found in the above mentioned report:


    DECLARE @MergeName nvarchar(400)
    DECLARE @MergeDomain nvarchar(400)
    DECLARE @FromGuid uniqueidentifier
    DECLARE @ToGuid uniqueidentifier

    DECLARE @PrimaryResource UNIQUEIDENTIFIER

    PRINT 'The following script will automatically merge resources as per the ''Merge computers with duplicate names'' report.'
    PRINT 'The merge automatically chooses the resource that has the most recent update.'

    DECLARE merge_cursor CURSOR FOR
    SELECT Ident.[Name], Ident.[Domain] FROM Inv_AeX_AC_Identification Ident
    INNER JOIN
    (
    SELECT Ident1.[_ResourceGuid], Ident1.[Name], Ident1.[Domain] FROM Inv_AeX_AC_Identification Ident1
    INNER JOIN Inv_AeX_AC_Identification Ident2 ON Ident2.[Name] = Ident1.[Name] AND Ident2.[Domain] = Ident1.[Domain] AND Ident2.[_id] != Ident1.[_id]
    INNER JOIN vComputerResource Ident3 ON Ident1.[_ResourceGuid] = Ident3.[Guid]
    GROUP BY Ident1.[_ResourceGuid], Ident1.[Name], Ident1.[Domain]
     ) udr ON Ident.[_ResourceGuid] = udr.[_ResourceGuid]
    GROUP BY Ident.[Name], Ident.[Domain]

    OPEN merge_cursor

    FETCH NEXT FROM merge_cursor
    INTO @MergeName, @MergeDomain

    WHILE @@FETCH_STATUS = 0
    BEGIN
    PRINT 'Merging ' + @MergeName + '.' + @MergeDomain

    CREATE TABLE #DUPLICATE_NAME (Resource UNIQUEIDENTIFIER, [Primary] BIT, [Name] NVARCHAR(64), [Domain] NVARCHAR(64), LastUpdated DATETIME, [OS Name] NVARCHAR(64))
    INSERT INTO #DUPLICATE_NAME
    SELECT ident1.[_ResourceGuid] AS [Resource], CAST (0 AS BIT) AS [Primary], ident1.[Name], ident1.[Domain], md.[ModifiedDate] AS [LastUpdated], ident1.[OS Name] FROM Inv_AeX_AC_Identification ident1
    JOIN
    (
    SELECT ident2.[Name], ident2.[Domain] FROM Inv_AeX_AC_Identification ident2
    GROUP BY ident2.[Name], ident2.[Domain]
    HAVING COUNT (ident2.[Name]) > 1 AND ident2.[Name] = @MergeName AND ident2.[Domain] = @MergeDomain
     ) dr ON ident1.[Name] = dr.[Name] and ident1.[Domain] = dr.[Domain]
    LEFT OUTER JOIN
    (
    SELECT DISTINCT rus.[ResourceGuid], rus.[ModifiedDate] FROM ResourceUpdateSummary rus
    JOIN
    (
    SELECT rus2.[ResourceGuid], MAX( rus2.[ModifiedDate] ) AS ModifiedDate FROM ResourceUpdateSummary rus2
    GROUP BY rus2.[ResourceGuid]
     ) td ON td.ResourceGuid = rus.ResourceGuid AND td.ModifiedDate = rus.ModifiedDate
     ) md ON ident1.[_ResourceGuid] = md.[ResourceGuid]
    SELECT TOP 1 @PrimaryResource = Resource FROM #DUPLICATE_NAME ORDER BY [LastUpdated] DESC
    UPDATE #DUPLICATE_NAME SET [Primary]=1 WHERE Resource = @PrimaryResource
    /*SELECT * FROM #DUPLICATE_NAME ORDER BY [LastUpdated] DESC*/
    SELECT TOP 1 @ToGuid = Resource FROM #DUPLICATE_NAME where Name = @MergeName and Domain = @MergeDomain and [Primary] = '1'
    SELECT TOP 1 @FromGuid = Resource FROM #DUPLICATE_NAME where Name = @MergeName and Domain = @MergeDomain and [Primary] = '0'
    DROP TABLE #DUPLICATE_NAME

    exec spResourceMerge @FromGuid,@ToGuid

    FETCH NEXT FROM merge_cursor
    INTO @MergeName, @MergeDomain

    END

    CLOSE merge_cursor
    DEALLOCATE merge_cursor



    Run the following script in SQL Query Analyzer pointing to the Altiris database
  2. JAustgen Site Administrator (Ex-SQL Savant)

    Thanks, Jeremy!
  3. JeremyDallas SVS Scholar

    You're welcome!
  4. JeremyDallas SVS Scholar

    Please let me know if you have any problems with this. Thanks!
  5. ddavis New Member

    Same name, different domain...

    Jeremy (or anyone else),

    We are seeing a number of machines on our network that for one reason or another get a domain change (training machines switching between domains, or domain field was blank in the db & then gets populated). When this happens, the NS gives it a new GUID and we end up with "Duplicates" of the same machine in the database, even though Altiris doesn't consider them to be duplicates, per se' because of the different domain.

    Do you have any magic way to detect records with duplicate names (and matching serial number or MAC, to make sure they are actually the same physical machine), and purging out only the "older" one - i.e. the one with the older last inventory date?

    Thanks,
    Darrell
  6. jperry784 New Member

    I have the same exact issue with some of my clients... I have been looking for an easy to do this. Unfortunately, right now it has been a manual merge for me. Hopefully someone has a better solution!
  7. mad.manu New Member

    Is it possible, when a new guid is affected to a machine, to keep this machine collections membership (not really a duplicate GUID but a duplicate name)?
    -> that will approximatively be to merge duplicate name with the older guid to keep the collections membership (that is based on the GUID and not the computer's name)
  8. eth123 New Member

    Jeremy - Does something similar to this exist for the DS db?
  9. Nick Altiris/AppSense Guru

    There's a DS Purge script that will purge machines from the DS database that haven't updated in a defined number of days.
  10. eth123 New Member

    Seems simple enough?

    Code:
     DELETE from computer 
    WHERE datediff(dd,last_inventory,getdate())>30  
  11. kschroeder Symantec Trusted Advisor

    Helps with AD Connector problems, but....

    I'm using this script to work around a known issue between Inventory 6.1 and Virtual machines; all of our VMs were showing up duplicated due to some bug in IS changing the machine type. My only concern is exactly how this "merge" works...I don't want to lose the existing events, etc for my VMs when they are merged with a non-existent GUID imported by the AD Connector. Unfortunately I dont' have any free time to extensively beta test the new AD Connector beta to see if it will clear this up. Looking at the code for the script, I'm trying to figure out how it "knows" which machine is the "newest". I would prefer that it went by the last Configuration Request, but as far as I can tell it just used "ModifiedDate" column from ResourceUpdateSummary table; but I don't know what that ModifiedDate really refers to.

    At this point I'm pretty much forced to run this script (I just set it as a scheduled report that runs ~30 minutes before my nightly AD update import) so that I can successfully import new machines from AD. If I don't do this, then the AD import fails due to some primary key conflict, which in turn causes my machines to not flow down into their OU-based collections, which causes my regional Admins to not see all of their actual machines in their collections, etc etc etc...ARGGGH!
  12. gsd New Member

    Is this Query still valid?

    Just wanted to know if this query would still work on NS 6.0.6074 SP3. Anyone know?
  13. dominique Sticky...

    Hello,

    For 5.5
    The table names have changed AeXInv_Aex_AC_Identification, AeXv_Computer but the structure remains.

    The selection should look like this:

    Duplication Name:

    SELECT Ident.[Name], Ident.[Domain] FROM AeXInv_Aex_AC_Identification Ident
    INNER JOIN
    (
    SELECT Ident1.[WrkstaId], Ident1.[Name], Ident1.[Domain] FROM AeXInv_Aex_AC_Identification Ident1
    INNER JOIN AeXInv_Aex_AC_Identification Ident2 ON Ident2.[Name] = Ident1.[Name] AND Ident2.[Domain] = Ident1.[Domain] AND Ident2.[_id] != Ident1.[_id]
    INNER JOIN AeXv_Computer Ident3 ON Ident1.[WrkstaId] = Ident3.[WrkstaId]
    GROUP BY Ident1.[WrkstaId], Ident1.[Name], Ident1.[Domain]
    ) udr ON Ident.[WrkstaID] = udr.[WrkstaID]
    GROUP BY Ident.[Name], Ident.[Domain]

    Duplication on Serial Number:

    SELECT Ident.[Serial Number], Ident.[Computer Model] FROM AeXInv_Aex_HW_Serial_Number Ident
    INNER JOIN
    (
    SELECT Ident1.[WrkstaId], Ident1.[Serial Number], Ident1.[Computer Model] FROM AeXInv_Aex_HW_Serial_Number Ident1
    INNER JOIN AeXInv_Aex_HW_Serial_Number Ident2 ON Ident2.[Serial Number] = Ident1.[Serial Number] AND Ident2.[Computer Model] = Ident1.[Computer Model] AND Ident2.[WrkstaId] != Ident1.[WrkstaID]
    INNER JOIN AeXv_Computer Ident3 ON Ident1.[WrkstaId] = Ident3.[WrkstaId]
    GROUP BY Ident1.[WrkstaId], Ident1.[Serial Number], Ident1.[Computer Model]
    ) udr ON Ident.[WrkstaID] = udr.[WrkstaID]
    GROUP BY Ident.[Serial Number], Ident.[Computer Model]

    Thanks,
    Dom
  14. shawn.m Member

    the problem with this merge script is it only lets you merge computers with that considered duplicates by name and domain. If you have a dual domain environment - ugh:mad: I have it- then you need to merge on the name only.

    I created the following variation after getting frustrated with Altiris support saying that merging will be better in the next version of asset management 6.2. The following merge query has a restriction based on computer name -'IS%' with IS being the beginning of all correctly name computers

    DECLARE @MergeName nvarchar(400)
    DECLARE @FromGuid uniqueidentifier
    DECLARE @ToGuid uniqueidentifier

    DECLARE @PrimaryResource UNIQUEIDENTIFIER

    PRINT 'The following script will automatically merge resources as per the ''Merge computers with duplicate names'' report.'
    PRINT 'The merge automatically chooses the resource that has the most recent update.'

    DECLARE merge_cursor CURSOR FOR
    SELECT Ident.[Name] FROM Inv_AeX_AC_Identification Ident
    INNER JOIN
    ( SELECT Ident1.[_ResourceGuid], Ident1.[Name]
    FROM Inv_AeX_AC_Identification Ident1
    INNER JOIN Inv_AeX_AC_Identification Ident2
    ON Ident2.[Name] = Ident1.[Name]
    AND Ident2.[_id] != Ident1.[_id]
    INNER JOIN vComputerResource Ident3 ON Ident1.[_ResourceGuid] = Ident3.[Guid]
    GROUP BY Ident1.[_ResourceGuid], Ident1.[Name] ) udr
    ON Ident.[_ResourceGuid] = udr.[_ResourceGuid]
    where ident.[name] like 'is%'
    GROUP BY Ident.[Name]

    OPEN merge_cursor

    FETCH NEXT FROM merge_cursor
    INTO @MergeName

    WHILE @@FETCH_STATUS = 0
    BEGIN
    PRINT 'Merging ' + @MergeName

    CREATE TABLE #DUPLICATE_NAME
    (Resource UNIQUEIDENTIFIER, [Primary] BIT,
    [Name] NVARCHAR(64), LastUpdated DATETIME, [OS Name] NVARCHAR(64))
    INSERT INTO #DUPLICATE_NAME
    SELECT ident1.[_ResourceGuid] AS [Resource], CAST (0 AS BIT) AS [Primary],
    ident1.[Name], md.[ModifiedDate] AS [LastUpdated], ident1.[OS Name]
    FROM Inv_AeX_AC_Identification ident1
    JOIN ( SELECT ident2.[Name] FROM Inv_AeX_AC_Identification ident2
    GROUP BY ident2.[Name]
    HAVING COUNT (ident2.[Name]) > 1 AND ident2.[Name] = @MergeName ) dr
    ON ident1.[Name] = dr.[Name]
    LEFT OUTER JOIN ( SELECT DISTINCT rus.[ResourceGuid], rus.[ModifiedDate]
    FROM ResourceUpdateSummary rus
    JOIN ( SELECT rus2.[ResourceGuid], MAX( rus2.[ModifiedDate] ) AS ModifiedDate
    FROM ResourceUpdateSummary rus2
    GROUP BY rus2.[ResourceGuid] ) td
    ON td.ResourceGuid = rus.ResourceGuid
    AND td.ModifiedDate = rus.ModifiedDate ) md
    ON ident1.[_ResourceGuid] = md.[ResourceGuid]
    SELECT TOP 1 @PrimaryResource = Resource
    FROM #DUPLICATE_NAME ORDER BY [LastUpdated] DESC
    UPDATE #DUPLICATE_NAME SET [Primary]=1 WHERE Resource = @PrimaryResource
    /*SELECT * FROM #DUPLICATE_NAME ORDER BY [LastUpdated] DESC*/
    SELECT TOP 1 @ToGuid = Resource FROM #DUPLICATE_NAME
    where Name = @MergeName and [Primary] = '1'
    SELECT TOP 1 @FromGuid = Resource FROM #DUPLICATE_NAME
    where Name = @MergeName and [Primary] = '0'
    DROP TABLE #DUPLICATE_NAME

    exec spResourceMerge @FromGuid,@ToGuid

    FETCH NEXT FROM merge_cursor
    INTO @MergeName

    END
  15. cnpalmer New Member

    NS console script/task?

    Does anyone or has anyone researched a way to automate this thru NS?

    Currently the only way to implement the script is to run it manually thru Query Analyzer or make the script a stored procedure and schedule it to run through SQL Ent. Manager.

    I'd like to see if anyone has any good suggestions about running this thru the NS console.
  16. shawn.m Member

    I have mine running as a stored procedure.
    the merge stored procedure does not like being run through a console window
  17. cnpalmer New Member

    Hey Shawn,

    I figured as much but the procedure works fine through the report when you right click on a workstation in the report list.

    Just would like to automate that right click function... call me lazy!
  18. Paco808 New Member

    I think I saw a XML file in the Manage fusion website for a script to merge the machines.

    /Quote
    "We are seeing a number of machines on our network that for one reason or another get a domain change (training machines switching between domains, or domain field was blank in the db & then gets populated). When this happens, the NS gives it a new GUID and we end up with "Duplicates" of the same machine in the database, even though Altiris doesn't consider them to be duplicates, per se' because of the different domain.
    /quote

    ^^^^
    IMO...
    This sounds to me like you have machine with duplicate GUID's!!! are your using Sidchange or Sysprep on the machine images? is the domain change in the machine or the NS database?

    Francisco
  19. dominique Sticky...

    Hello,

    Instead of merging duplicated serial number I am looking for a script which will delete the oldest one.

    Thanks,
    Dom
  20. bgreen New Member

    Dom,

    Reading through the query in the original post, it already grabs the two duplicate machines and determines which one has been updated more recently. That being the case, I would think you could just replace the line that executes the merge stored procedure with one that replaces the delete stored procedure (which I can't remember the name of right now):


    So instead of:
    exec spResourceMerge @FromGuid,@ToGuid

    You'd do:
    CAUTION - Just thinking out loud here, have nowhere to test it at the moment - CAUTION
    exec spResourceDelete @FromGuid (or whatever the delete stored proc is called)

Share This Page