简体   繁体   中英

SQL Server 2008 R2: Avoid vice versa records

I have following table with two columns:

Table : vv

CREATE TABLE vv
(
    cola INT,
    colb INT
);

Insertion :

INSERT INTO vv VALUES (1,2),(2,1),(3,4),(4,3);

Records I have :

SELECT * FROM vv;

Cola    Colb
------------
1   2
2   1
3   4
4   3

Note : Now I want to remove vice versa records from the table. Like in my table I have record 1,2 and 2,1 , I just want to keep the only one value whichever appear first in the table.

Expected Result should be :

If I order by 1 in the SELECT statement:

SELECT * FROM vv
ORDER BY CASE WHEN Cola = '1' THEN 1 ELSE 2 END;

The result should be:

Cola    Colb
-------------   
1       2
3       4

If I order by 4 in the SELECT statement:

SELECT * FROM vv
ORDER BY CASE WHEN Cola = '4' THEN 1 ELSE 2 END;

The result should be:

Cola    Colb
-------------
4       3
1       2

If I order by 3 in the SELECT statement:

SELECT * FROM vv
ORDER BY CASE WHEN Cola = '3' THEN 1 ELSE 2 END;

The result should be:

Cola    Colb
-------------   
3       4 
1       2

You can try rearranging them in new columns then do a ROW_NUMBER and PARTITION BY the new columns:

WITH Cte AS(
    SELECT *,
        firstCol = CASE WHEN cola >= colb  THEN colb ELSE cola END,
        secondCol = CASE WHEN cola < colb  THEN colb ELSE cola END
    FROM vv
),
CteFinal AS(
    SELECT *,
        rn = ROW_NUMBER() OVER(
                PARTITION BY firstCol, secondCol
                ORDER BY CASE WHEN Cola = '4' THEN 1 ELSE 2 END
             )
    FROM Cte
)
SELECT cola, colb
FROM CteFinal
WHERE rn = 1
ORDER BY CASE WHEN Cola = '4' THEN 1 ELSE 2 END

You have to replace the ORDER BY clause to achieve the desired order.

Possible solution:

;WITH cte1 AS(SELECT * FROM @vv WHERE cola = 3),
      cte2 AS(SELECT v.* FROM @vv v
              CROSS JOIN cte1 c 
              WHERE NOT ((v.cola = c.colb AND v.colb = c.cola) OR (v.cola = c.cola AND v.colb = c.colb)))
SELECT * FROM cte1
UNION ALL
SELECT c1.* FROM cte2 c1
join cte2 c2 ON c1.cola = c2.colb AND c1.colb = c2.cola AND c1.cola < c2.cola

If it were me, I'd take the following approach:

1) Create the table with a slight twist:

CREATE TABLE vv
(
    cola INT,
    colb INT
);

alter table vv add constraint [CK_a_less_than_b] check (cola < colb);
alter table vv add constraint [UQ_a_b] unique (cola, colb);

2) Inserts happen through the ff procedure:

create procedure dbo.insertVV (@a int, @b int) as begin

   if (@a > @b)
      insert into dbo.vv
         (cola, colb)
      values
         (@b, @a);
   else
      insert into dbo.vv
         (cola, colb)
      values
         (@a, @b);
end

Alternatively, if you don't/can't trust that people with use the sproc to do their inserts, you could put the above in an instead of insert trigger.

You could also get fancy with step 1 and create a computed column that is defined as a value that provides a guaranteed order and then put the unique index on that and then step 2 is unnecessary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM