Search code examples
sql-serverjoinrowpartial

Convert a row with multiple quantity to multiple rows of single quantity while joining another table with partial data


I have 2 tables, one with the ID and its count and the other with the names belonging to the respective IDs. They need to be joined so that the end result is a table with count of 1 in each row and the respective names next to them. Note that the number of names in table 2 is less than the count in table 1 for the same ID in some cases.

Table 1

ID  | Count
-----------
100 | 3
101 | 2
102 | 4

Table 2

ID  | Name
----------
100 | abc       
100 | def       
101 | ghi       
101 | jkl       
102 | mno       
102 | pqr       
102 | stu       

Result

ID  | Count | Name
------------------
100 | 1     | abc
100 | 1     | def
100 | 1     |
101 | 1     | ghi
101 | 1     | jkl
102 | 1     | mno
102 | 1     | pqr
102 | 1     | stu
102 | 1     |

I'm using TSQL for this and my current query converts table 1 into multiple rows in the result table; then it inserts individual names from table 2 into the result table through a loop. I'm hoping there must be a simpler or more efficient way to do this as the current method takes considerable amount of time. If there is, please let me know.


Solution

  • The first thing that comes to mind for me involves using a Number table, which you could create (as a one-time task) like this:

    CREATE TABLE numbers (
        ID INT
    )
    
    DECLARE @CurrentNumber INT, @MaxNumber INT
    SET @MaxNumber = 100 -- Choose a value here which you feel will always be greater than MAX(table1.Count)
    SET @CurrentNumber = 1
    
    WHILE @CurrentNumber <= @MaxNumber
    BEGIN
        INSERT INTO numbers VALUES (@CurrentNumber)
        SET @CurrentNumber = @CurrentNumber + 1
    END
    

    Once you have a numbers table, you can solve this problem like this:

    SELECT  one.ID,
            1 AS [Count],
            ISNULL(two.Name,'') AS Name
    FROM table1 one
    JOIN numbers n ON n.ID <= CASE WHEN one.[Count] >= (SELECT COUNT(1) FROM table2 two WHERE one.ID = two.ID)
                                THEN one.[Count]
                                ELSE (SELECT COUNT(1) FROM table2 two WHERE one.ID = two.ID)
                              END
    LEFT JOIN (SELECT   ID, 
                        Name,
                        ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS RecordNo
                FROM table2) two ON one.ID = two.ID
                                AND two.RecordNo = n.ID