Combaine rows into one row with partial information match

Debilon 431 Reputation points
2022-04-26T09:17:40.717+00:00

I have a table with phone numbers and emails
few of the rows partially match : Same first & Last Name , Same Address different phone numbers and sometimes different email

 -- DDL and sample data population, start  

 DECLARE @PhoneTable Table (ID INT IDENTITY PRIMARY KEY, Email VARCHAR(30),FirstName VARCHAR(30), LastName Varchar(30), Address varchar(50), City Varchar(30), State Varchar(10), Zip Varchar(10),  
 PhoneNumber VARCHAR(30));  



 INSERT INTO @PhoneTable (Email, FirstName, LastName,Address,City,State,Zip,PhoneNumber) VALUES  
 (Null,' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-514-7725'),  
 ('D.Shmooz'@gmail.com',' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-514-7725'),  
 (Null,' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-914-8825')  


  ;WITH MyCTE AS (  
   Select Distinct  
  Email, FirstName, LastName,Address,City,State,Zip,PhoneNumber  
   FROM @PhoneTable   
   )  
   Select * from MyCTE  

Current Result
196520-image.png

I need to combine the rows into a one single row with all available information from the table

  1. I don't know how many emails / phones exist in the table.

Desired Output
<code>196467-image.png</code>

I'm Using Microsoft SQL Server 2019 (RTM-CU11) (KB5003249) - 15.0.4138.2 (X64) May 27 2021 17:34:14 Copyright (C) 2019 Microsoft Corporation Standard Edition (64-bit) on Windows Server 2019 Standard 10.0 <X64> (Build 17763: )

Developer technologies | Transact-SQL
{count} votes

Accepted answer
  1. LiHong-MSFT 10,056 Reputation points
    2022-04-27T02:32:17.723+00:00

    Hi @Debilon
    For STRING_AGG function, you can use Stuff instead.
    And for ‘No Group By in an update’ issue,you may try put the 'Group By' query inside a subquery or another CTE.
    Check this :

    CREATE Table #PhoneTable  
    (ID INT IDENTITY PRIMARY KEY, Email VARCHAR(30),FirstName VARCHAR(30),LastName Varchar(30),   
     Address varchar(50), City Varchar(30), State Varchar(10), Zip Varchar(10), PhoneNumber VARCHAR(30));  
                      
    INSERT INTO #PhoneTable (Email, FirstName, LastName,Address,City,State,Zip,PhoneNumber) VALUES  
      (Null,' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-514-7725'),  
      ('******@gmail.com',' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-514-7725'),  
      (Null,' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-914-8825'),  
      ('******@gmail.com',' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-914-9925'),  
      (Null,' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-614-6625')  
      
    SELECT * FROM #PhoneTable  
      
    ;WITH CTE AS  
    (  
     SELECT *,DENSE_RANK()OVER(PARTITION BY FirstName, LastName,Address,City,State,Zip ORDER BY ISNULL(email,'')DESC) AS RNum_Email  
             ,DENSE_RANK()OVER(PARTITION BY FirstName, LastName,Address,City,State,Zip ORDER BY ISNULL(PhoneNumber,'')DESC) AS RNum_PhoneNumber  
     FROM #PhoneTable  
    ),CTE2 AS   
    (   
     SELECT FirstName,LastName,Address,City,State,Zip  
           ,MAX(CASE WHEN RNum_Email=1 THEN Email END) AS Email_1  
    	   ,MAX(CASE WHEN RNum_Email=2 THEN Email END) AS Email_2  
           ,STUFF((SELECT DISTINCT',' + Email FROM CTE WHERE RNum_Email > 2 FOR XML PATH('')),1,1,'') AS Email_3  
           ,MAX(CASE WHEN RNum_PhoneNumber=1 THEN PhoneNumber END) AS PhoneNumber_1  
    	   ,MAX(CASE WHEN RNum_PhoneNumber=2 THEN PhoneNumber END) AS PhoneNumber_2  
           ,STUFF((SELECT DISTINCT',' + PhoneNumber FROM CTE WHERE RNum_PhoneNumber > 2 FOR XML PATH('')),1,1,'') AS PhoneNumber_3  
     FROM CTE  
     GROUP BY FirstName,LastName,Address,City,State,Zip  
    )  
    SELECT * FROM CTE2 --Check IF Correct Before Update  
    --UPDATE CTE2 SET Column...=Column...  
    

    Best regards,
    LiHong


1 additional answer

Sort by: Most helpful
  1. Naomi Nosonovsky 8,431 Reputation points
    2022-04-26T18:04:32.843+00:00

    This seems to work, but I have to order by email in both cases, otherwise I got an error:

    DECLARE @PhoneTable Table (ID INT IDENTITY PRIMARY KEY, Email VARCHAR(30),FirstName VARCHAR(30), LastName Varchar(30), Address varchar(50), City Varchar(30), State Varchar(10), Zip Varchar(10),
      PhoneNumber VARCHAR(30));
    
    
    
      INSERT INTO @PhoneTable (Email, FirstName, LastName,Address,City,State,Zip,PhoneNumber) VALUES
      (Null,' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-514-7725'),
      ('******@gmail.com',' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-514-7725'),
      (Null,' Darla', 'Shmooz', '81 Grass Valley DR', 'New York', 'NY', '10021', '829-914-8825')
    
      ;WITH cte AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY FirstName, LastName,Address,City,State,Zip
      ORDER BY CASE WHEN email IS NULL THEN 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZ' ELSE email END) AS RnEmail,
      ROW_NUMBER() OVER (PARTITION BY FirstName, LastName,Address,City,State,Zip
      ORDER BY CASE WHEN phoneNumber IS NULL THEN 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZ' ELSE phoneNumber END) AS RnPhone
      FROM @PhoneTable)
    
      SELECT FirstName, LastName,Address,City,State,Zip, 
      MAX(CASE WHEN RnEmail = 1 THEN Email END) AS Email1,
      MAX(CASE WHEN RnEmail = 2 THEN Email END) AS Email2,
      STRING_AGG(CASE WHEN RnEmail>2 THEN Email END, ',' ) WITHIN GROUP (ORDER BY RnEmail) AS Email3,
      MAX(CASE WHEN RnPhone = 1 THEN PhoneNumber END) AS Phone1,
      MAX(CASE WHEN RnPhone = 2 THEN PhoneNumber END) AS Phone2,
      STRING_AGG(CASE WHEN RnPhone>2 THEN PhoneNumber END, ',' ) WITHIN GROUP (ORDER BY RnEmail) AS Phone3
      FROM cte
      GROUP BY FirstName, LastName,Address,City,State,Zip
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.