• Try this code, please note how I took the data you provided and included it in a way that can be used by others as well:

    [font="Courier New"]-- test table to hold test data

    DECLARE @products TABLE(flag bit, product_id INT, product_id_group INT, id INT)

    -- insert test data

    INSERT INTO @products

       SELECT      

           0,             123456,                NULL,        456

       UNION ALL

       SELECT

           0,             789012,                NULL,        456

       UNION ALL

       SELECT

           1,             789012,                NULL,        456

       UNION ALL

       SELECT

           0,               1,                       456789,    132    

       UNION ALL

       SELECT

           0,               1,                       156894,    132

       UNION ALL

       SELECT

           1,               1,                       456789,    132

    -- proposed solution

    ;WITH cteCounts AS

       (

       SELECT

           COUNT(DISTINCT flag) AS flag_count,

           ISNULL(product_id_group, product_id) AS product_id,

           id

       FROM

           @products

       GROUP BY

           ISNULL(product_id_group, product_id) ,

           id

       )

       SELECT

           A.id,

           A.product_id AS new_product,

           B.product_id AS old_product

       FROM

           cteCounts A JOIN

           cteCounts B ON

               A.id = B.id AND

               A.product_id <> B.product_id

       WHERE

           A.flag_count = 1

           [/font]

    This works for the data you provided, but I can't guarantee it won't need to be changed to work for the real data.