-- ====================================================================================================================================================
-- Name:        Merge_Duplicate_Nodes_v0.9.3.sql
-- Author:      Intel
-- Author date: 04NOV24
-- Description:	SQL script to merge duplicate endpoints in Intel EMA.  Endpoints are identified by the Hwid in newer versions or by name in older
--              versions of Intel EMA, collected and merged if duplicates are found.
-- ====================================================================================================================================================
-- Variable descriptions:
--     PriEpIdKey: sets the primary value to use for duplicate record identification
--     SecEpIdKey: sets the secondary value to use for duplicate record identification
--     PriSqlQuery: SQL query string for the primary endpoint ID value
--     SecSqlQuery: SQL query string for the secondary endpoint ID value
--     EmaVer: Current version of the Intel EMA agents in the database
--     EmaMin: Minimum version of Intel EMA required to execute the secondary method
--     ForceDate: Past date to force endpoint configuration to the top of the manageability server queue and restore CIRA connectivity more quickly
-- ====================================================================================================================================================
DECLARE @PriEpIdKey NVARCHAR(max),
        @SecEpIdKey NVARCHAR(max),
        @PriSqlQuery NVARCHAR(max),
        @SecSqlQuery NVARCHAR(max),
        @EmaVer INT,
        @EmaMin INT,
        @ForceDate AS DATE = (DATEADD(YEAR, -1, GETDATE()))
-- ====================================================================================================================================================
-- Check the Intel EMA version based on the version of the endpoint agent in the database and conditionally set the endpoint record fields to query to
-- facilitate script schema evaluation at the time of execution
--
-- Only the first method will execute on legacy versions of Intel EMA but the value for the field to query is set redundantly to mitigate schema
-- evaluation errors
-- ====================================================================================================================================================
SET @EmaMin = 11400
SELECT TOP (1) @EmaVer = [VERSION] FROM [dbo].[MeshAgents]
IF @EmaVer >= @EmaMin
  BEGIN
    SET @PriEpIdKey = N'Hwid'
    SET @SecEpIdKey = N'ComputerName'
  END
ELSE
  BEGIN
    SET @PriEpIdKey = N'ComputerName'
    SET @SecEpIdKey = N'ComputerName'
  END
-- ====================================================================================================================================================
-- Execute using the endpoint record value set above for all Intel EMA versions
-- Intel EMA v1.14 and newer will use the HwId value as the unique identifier for endpoints but will execute a second time using the ComputerName value
-- Intel EMA versions prior to v1.14 will use the ComputerName as the unique identifier and only execute one time
-- ====================================================================================================================================================
-- NOTE: Duplicate records for endpoints with hostnames that have changed will not be identified unless Intel EMA is v1.14 or newer and had been online
-- after the server had been upgraded
-- ====================================================================================================================================================
IF OBJECT_ID(N'tempdb..#Temp_UniqueEndpointsPrimary') IS NOT NULL
  BEGIN
    DROP TABLE #Temp_UniqueEndpointsPrimary
  END

IF OBJECT_ID(N'tempdb..#Temp_DuplicateEndpointsPrimary') IS NOT NULL
   BEGIN
     DROP TABLE #Temp_DuplicateEndpointsPrimary
   END

SELECT @PriSqlQuery = N'SELECT '+QUOTENAME(@PriEpIdKey)+', NodeIdentity, NodeId, UpdateTime
  INTO #Temp_UniqueEndpointsPrimary
  FROM Nodes,
    (SELECT MAX(NodeIdentity) AS EndpointID
    FROM Nodes
    GROUP BY '+QUOTENAME(@PriEpIdKey)+') TopNodes, 
      (SELECT '+QUOTENAME(@PriEpIdKey)+' AS EndpointComputer, COUNT('+QUOTENAME(@PriEpIdKey)+') AS Duplicate
      FROM Nodes
      GROUP BY '+QUOTENAME(@PriEpIdKey)+'
        HAVING COUNT('+QUOTENAME(@PriEpIdKey)+') > 1) EndpointDups
  WHERE Nodes.NodeIdentity = TopNodes.EndpointID
    AND Nodes.'+QUOTENAME(@PriEpIdKey)+' = EndpointDups.EndpointComputer;

SELECT Nodes.'+QUOTENAME(@PriEpIdKey)+', Nodes.NodeIdentity, Nodes.NodeId, Nodes.UpdateTime, UniqueEP.NodeId AS UniqueId, 
  DupCount = ROW_NUMBER() OVER (PARTITION BY Nodes.'+QUOTENAME(@PriEpIdKey)+' ORDER BY Nodes.LastUpdate)
  INTO #Temp_DuplicateEndpointsPrimary
  FROM Nodes, #Temp_UniqueEndpointsPrimary AS UniqueEP
  WHERE Nodes.'+QUOTENAME(@PriEpIdKey)+' = UniqueEP.'+QUOTENAME(@PriEpIdKey)+'
    AND Nodes.NodeIdentity NOT IN (SELECT NodeIdentity FROM #Temp_UniqueEndpointsPrimary)
  ORDER BY Nodes.NodeId;

DELETE FROM [Manageability].[Amt8021XSetup_IntelAmtSetup]
  WHERE NodeID in (SELECT NodeID FROM #Temp_DuplicateEndpointsPrimary);

DELETE FROM [dbo].[UsbrSession]
  WHERE EndpointId in (SELECT NodeID FROM #Temp_DuplicateEndpointsPrimary);

UPDATE EndpointConnectInfo
  SET EndpointConnectInfo.NodeID = Duplicates.UniqueId
  FROM #Temp_DuplicateEndpointsPrimary AS Duplicates
  WHERE EndpointConnectInfo.NodeID = Duplicates.NodeID
    AND Duplicates.DupCount = 1
    AND Duplicates.UniqueId NOT IN (SELECT NodeId FROM EndpointConnectInfo);

UPDATE AmtCertMap
  SET AmtCertMap.NodeID = Duplicates.UniqueId
  FROM #Temp_DuplicateEndpointsPrimary AS Duplicates
  WHERE AmtCertMap.NodeID = Duplicates.NodeID
    AND Duplicates.DupCount = 1
    AND Duplicates.UniqueId NOT IN (SELECT NodeId FROM AmtCertMap);

UPDATE IntelAmtSetup
  SET IntelAmtSetup.NodeID = Duplicates.UniqueId, IntelAmtSetup.LastSetup = Duplicates.UpdateTime, IntelAmtSetup.LastAttempt = @ForceDate, IntelAmtSetup.AdoptionState = 1, IntelAmtSetup.State = 2
  FROM #Temp_DuplicateEndpointsPrimary AS Duplicates
  WHERE IntelAmtSetup.NodeID = Duplicates.NodeID
    AND Duplicates.DupCount = 1
    AND Duplicates.UniqueId NOT IN (SELECT NodeId FROM IntelAmtSetup);

DELETE FROM IntelAmt
  WHERE IntelAmt.NodeID IN (SELECT NodeId  FROM #Temp_DuplicateEndpointsPrimary);

DELETE FROM Nodes
  WHERE Nodes.NodeID IN (SELECT NodeId FROM #Temp_DuplicateEndpointsPrimary);

SELECT '+QUOTENAME(@PriEpIdKey)+' FROM #Temp_DuplicateEndpointsPrimary'

EXECUTE sp_executesql @PriSqlQuery, N'@ForceDate AS DATE', @ForceDate

IF OBJECT_ID(N'tempdb..#Temp_UniqueEndpointsPrimary') IS NOT NULL
  BEGIN
    DROP TABLE #Temp_UniqueEndpointsPrimary
  END

IF OBJECT_ID(N'tempdb..#Temp_DuplicateEndpointsPrimary') IS NOT NULL
   BEGIN
     DROP TABLE #Temp_DuplicateEndpointsPrimary
   END

-- ====================================================================================================================================================
-- Second execution using the original method of ComputerName to catch legacy endpoint records that do not have a HW ID but the hostname is the same as
-- the original endpoint record
-- This secondary method will only execute if the Intel EMA version is v1.14 or newer as identified with the agent version evaluation above
-- ====================================================================================================================================================
IF @EmaVer >= @EmaMin
  BEGIN
  IF OBJECT_ID(N'tempdb..#Temp_UniqueEndpointsSecondary') IS NOT NULL
    BEGIN
      DROP TABLE #Temp_UniqueEndpointsSecondary
    END

  IF OBJECT_ID(N'tempdb..#Temp_DuplicateEndpointsSecondary') IS NOT NULL
     BEGIN
       DROP TABLE #Temp_DuplicateEndpointsSecondary
     END

  SELECT @SecSqlQuery = N'SELECT '+QUOTENAME(@SecEpIdKey)+', NodeIdentity, NodeId, UpdateTime
    INTO #Temp_UniqueEndpointsSecondary
    FROM Nodes,
      (SELECT MAX(NodeIdentity) AS EndpointID
      FROM Nodes
      GROUP BY '+QUOTENAME(@SecEpIdKey)+') TopNodes, 
        (SELECT '+QUOTENAME(@SecEpIdKey)+' AS EndpointComputer, COUNT('+QUOTENAME(@SecEpIdKey)+') AS Duplicate
        FROM Nodes
        GROUP BY '+QUOTENAME(@SecEpIdKey)+'
          HAVING COUNT('+QUOTENAME(@SecEpIdKey)+') > 1) EndpointDups
    WHERE Nodes.NodeIdentity = TopNodes.EndpointID
      AND Nodes.'+QUOTENAME(@SecEpIdKey)+' = EndpointDups.EndpointComputer;

  SELECT Nodes.'+QUOTENAME(@SecEpIdKey)+', Nodes.NodeIdentity, Nodes.NodeId, Nodes.UpdateTime, UniqueEP.NodeId AS UniqueId, 
    DupCount = ROW_NUMBER() OVER (PARTITION BY Nodes.'+QUOTENAME(@SecEpIdKey)+' ORDER BY Nodes.LastUpdate)
    INTO #Temp_DuplicateEndpointsSecondary
    FROM Nodes, #Temp_UniqueEndpointsSecondary AS UniqueEP
    WHERE Nodes.'+QUOTENAME(@SecEpIdKey)+' = UniqueEP.'+QUOTENAME(@SecEpIdKey)+'
      AND Nodes.NodeIdentity NOT IN (SELECT NodeIdentity FROM #Temp_UniqueEndpointsSecondary)
    ORDER BY Nodes.NodeId;
  DELETE FROM [Manageability].[Amt8021XSetup_IntelAmtSetup]
    WHERE NodeID in (SELECT NodeID FROM #Temp_DuplicateEndpointsSecondary);

  DELETE FROM [dbo].[UsbrSession]
    WHERE EndpointId in (SELECT NodeID FROM #Temp_DuplicateEndpointsSecondary);

  UPDATE EndpointConnectInfo
    SET EndpointConnectInfo.NodeID = Duplicates.UniqueId
    FROM #Temp_DuplicateEndpointsSecondary AS Duplicates
    WHERE EndpointConnectInfo.NodeID = Duplicates.NodeID
      AND Duplicates.DupCount = 1
      AND Duplicates.UniqueId NOT IN (SELECT NodeId FROM EndpointConnectInfo);

  UPDATE AmtCertMap
    SET AmtCertMap.NodeID = Duplicates.UniqueId
    FROM #Temp_DuplicateEndpointsSecondary AS Duplicates
    WHERE AmtCertMap.NodeID = Duplicates.NodeID
      AND Duplicates.DupCount = 1
      AND Duplicates.UniqueId NOT IN (SELECT NodeId FROM AmtCertMap);

  UPDATE IntelAmtSetup
    SET IntelAmtSetup.NodeID = Duplicates.UniqueId, IntelAmtSetup.LastSetup = Duplicates.UpdateTime, IntelAmtSetup.LastAttempt = @ForceDate, IntelAmtSetup.AdoptionState = 1, IntelAmtSetup.State = 2
    FROM #Temp_DuplicateEndpointsSecondary AS Duplicates
    WHERE IntelAmtSetup.NodeID = Duplicates.NodeID
      AND Duplicates.DupCount = 1
      AND Duplicates.UniqueId NOT IN (SELECT NodeId FROM IntelAmtSetup);

  DELETE FROM IntelAmt
    WHERE IntelAmt.NodeID IN (SELECT NodeId  FROM #Temp_DuplicateEndpointsSecondary);

  DELETE FROM Nodes
    WHERE Nodes.NodeID IN (SELECT NodeId FROM #Temp_DuplicateEndpointsSecondary);

SELECT '+QUOTENAME(@SecEpIdKey)+' FROM #Temp_DuplicateEndpointsSecondary'

  EXECUTE sp_executesql @SecSqlQuery, N'@ForceDate AS DATE', @ForceDate
  PRINT 'Used: '+@SecEpIdKey

  IF OBJECT_ID(N'tempdb..#Temp_UniqueEndpointsSecondary') IS NOT NULL
    BEGIN
      DROP TABLE #Temp_UniqueEndpointsSecondary
    END

  IF OBJECT_ID(N'tempdb..#Temp_DuplicateEndpointsSecondary') IS NOT NULL
     BEGIN
       DROP TABLE #Temp_DuplicateEndpointsSecondary
     END
  END
