views:

688

answers:

4

I have a set of data, with columns x and y. This set contains rows where, for any 2 given values, A and B, there is a row with A and B in columns x and y respectivly and there will be a second row with B and A in columns x and y respectivly.

E.g

        **Column X**            **Column Y**
Row 1        A                       B
Row 2        B                       A
  • There are multiple pairs of data in this set that follow this rule.
  • For every row with A, B in Columns X and Y, there will always be a row with B, A in X and Y
  • Columns X and Y are of type int

I need a T-Sql query that given a set with the rules above will return me either Row 1 or Row 2, but not both.

Either the answer is very difficult, or its so easy that I can't see the forest for the trees, either way it's driving me up the wall.

A: 

To get the highest and lowest of each pair, you could use:

(X+Y+ABS(X-Y)) / 2 as High, (X+Y-ABS(X-Y)) / 2 as Low

So now use DISTINCT to get the pairs of them.

SELECT DISTINCT 
  (X+Y+ABS(X-Y)) / 2 as High, (X+Y-ABS(X-Y)) / 2 as Low 
FROM YourTable
Rob Farley
A: 
SELECT O.X, O.Y
FROM myTable O
WHERE EXISTS (SELECT X, Y FROM myTable I WHERE I.X = O.Y AND I.Y = O.X)

I have not tried this. But, this should work.

shahkalpesh
+7  A: 

Add to your query the predicate,

where X < Y

and you can never get row two, but will always get row one.

(This assumes that when you wrote "two given values" you meant two distinct given values; if the two values can be the same, add the predicate where X <= Y (to get rid of all "reversed" rows where X > Y) and then add a distinct to your select list (to collapse any two rows where X == Y into one row).)

In reply to comments:

That is, if currently your query is select foo, x, y from sometable where foo < 3; change it to select foo, x, y from sometable where foo < 3 and x < y;, or for the the second case (where X and Y are not distinct values) select distinct foo, x, y from sometable where foo < 3 and x <= y;.

tpdi
I just ran a test and this did not work.
ChaosPandion
Chaos, on what dataset did you run your test?
tpdi
Your solution does work. My apologies.
ChaosPandion
I created a test with a data set of 10,000 rows and your answer clocked in at 5 seconds while mine clocked in at 30. Clearly we have a winner. Hurray for over thinking.
ChaosPandion
I am in shock that mine is only 6 times slower though. SQL Server 2008 is quite the beast!
ChaosPandion
+1  A: 

This should work.

Declare @t Table (PK Int Primary Key Identity(1, 1), A int, B int);

Insert into @t values (1, 2);
Insert into @t values (2, 1);
Insert into @t values (3, 4);
Insert into @t values (4, 3);
Insert into @t values (5, 6);
Insert into @t values (6, 5);

Declare @Table Table (ID Int Primary Key Identity(1, 1), PK Int, A Int, B Int);
Declare @Current Int;
Declare @A Int;

Insert Into @Table 
Select PK, A, B 
From @t;

Set @Current = 1;    

While (@Current <= (Select Max(ID) From @Table) Begin    

    Select @A = A 
    From @Table 
    Where ID = @Current;        

    If (@A Is Not Null) Begin

        Delete From @Table Where B = @A;      
        If ((Select COUNT(*) From @Table Where A = @A) > 1) Begin
            Delete From @Table Where ID = @Current;
        End

    End

    Set @A = Null;  
    Set @Current = @Current + 1;

End

Select a.*
From @tAs a
    Inner Join @Table As b On a.PK = b.PK
ChaosPandion
Care to explain the down vote?
ChaosPandion
I didn't down vote you, but the top voted answer is significantly nicer than this, this seems like a procedural solution to a set based problem.
Sam Saffron
I didn't down vote you either, but using temp table, a while loop, and then a select and a delete for each iteration of that loop, is a lot of overkill. You don't need to cursor -- and if you did, why not just use a cursor, instead of rolling your own (less efficient) "cursor in a while loop"? As Sam says, you're approaching this in a procedurall way, not using the set tools that a database offers you.
tpdi
Nice try Chaos.
Jaimal Chohan