Swapping column values in MySQL - mysql

I have a MySQL table with coordinates, the column names are X and Y. Now I want to swap the column values in this table, so that X becomes Y and Y becomes X. The most apparent solution would be renaming the columns, but I don't want to make structure changes since I don't necessarily have permissions to do that.
Is this possible to do with UPDATE in some way? UPDATE table SET X=Y, Y=X obviously won't do what I want.
Edit: Please note that my restriction on permissions, mentioned above, effectively prevents the use of ALTER TABLE or other commands that change the table/database structure. Renaming columns or adding new ones are unfortunately not options.

I just had to deal with the same and I'll summarize my findings.
The UPDATE table SET X=Y, Y=X approach obviously doesn't work, as it'll just set both values to Y.
Here's a method that uses a temporary variable. Thanks to Antony from the comments of http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/ for the "IS NOT NULL" tweak. Without it, the query works unpredictably. See the table schema at the end of the post. This method doesn't swap the values if one of them is NULL. Use method #3 that doesn't have this limitation.
UPDATE swap_test SET x=y, y=#temp WHERE (#temp:=x) IS NOT NULL;
This method was offered by Dipin in, yet again, the comments of http://beerpla.net/2009/02/17/swapping-column-values-in-mysql/. I think it’s the most elegant and clean solution. It works with both NULL and non-NULL values.
UPDATE swap_test SET x=(#temp:=x), x = y, y = #temp;
Another approach I came up with that seems to work:
UPDATE swap_test s1, swap_test s2 SET s1.x=s1.y, s1.y=s2.x WHERE s1.id=s2.id;
Essentially, the 1st table is the one getting updated and the 2nd one is used to pull the old data from.
Note that this approach requires a primary key to be present.
This is my test schema:
CREATE TABLE `swap_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`x` varchar(255) DEFAULT NULL,
`y` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `swap_test` VALUES ('1', 'a', '10');
INSERT INTO `swap_test` VALUES ('2', NULL, '20');
INSERT INTO `swap_test` VALUES ('3', 'c', NULL);

You could take the sum and subtract the opposing value using X and Y
UPDATE swaptest SET X=X+Y,Y=X-Y,X=X-Y;
Here is a sample test (and it works with negative numbers)
mysql> use test
Database changed
mysql> drop table if exists swaptest;
Query OK, 0 rows affected (0.03 sec)
mysql> create table swaptest (X int,Y int);
Query OK, 0 rows affected (0.12 sec)
mysql> INSERT INTO swaptest VALUES (1,2),(3,4),(-5,-8),(-13,27);
Query OK, 4 rows affected (0.08 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM swaptest;
+------+------+
| X | Y |
+------+------+
| 1 | 2 |
| 3 | 4 |
| -5 | -8 |
| -13 | 27 |
+------+------+
4 rows in set (0.00 sec)
mysql>
Here is the swap being performed
mysql> UPDATE swaptest SET X=X+Y,Y=X-Y,X=X-Y;
Query OK, 4 rows affected (0.07 sec)
Rows matched: 4 Changed: 4 Warnings: 0
mysql> SELECT * FROM swaptest;
+------+------+
| X | Y |
+------+------+
| 2 | 1 |
| 4 | 3 |
| -8 | -5 |
| 27 | -13 |
+------+------+
4 rows in set (0.00 sec)
mysql>
Give it a Try !!!

The following code works for all scenarios in my quick testing:
UPDATE swap_test
SET x=(#temp:=x), x = y, y = #temp

UPDATE table SET X=Y, Y=X will do precisely what you want (edit: in PostgreSQL, not MySQL, see below). The values are taken from the old row and assigned to a new copy of the same row, then the old row is replaced. You do not have to resort to using a temporary table, a temporary column, or other swap tricks.
#D4V360: I see. That is shocking and unexpected. I use PostgreSQL and my answer works correctly there (I tried it). See the PostgreSQL UPDATE docs (under Parameters, expression), where it mentions that expressions on the right hand side of SET clauses explicitly use the old values of columns. I see that the corresponding MySQL UPDATE docs contain the statement "Single-table UPDATE assignments are generally evaluated from left to right" which implies the behaviour you describe.
Good to know.

Ok, so just for fun, you could do this! (assuming you're swapping string values)
mysql> select * from swapper;
+------+------+
| foo | bar |
+------+------+
| 6 | 1 |
| 5 | 2 |
| 4 | 3 |
+------+------+
3 rows in set (0.00 sec)
mysql> update swapper set
-> foo = concat(foo, "###", bar),
-> bar = replace(foo, concat("###", bar), ""),
-> foo = replace(foo, concat(bar, "###"), "");
Query OK, 3 rows affected (0.00 sec)
Rows matched: 3 Changed: 3 Warnings: 0
mysql> select * from swapper;
+------+------+
| foo | bar |
+------+------+
| 1 | 6 |
| 2 | 5 |
| 3 | 4 |
+------+------+
3 rows in set (0.00 sec)
A nice bit of fun abusing the left-to-right evaluation process in MySQL.
Alternatively, just use XOR if they're numbers. You mentioned coordinates, so do you have lovely integer values, or complex strings?
Edit: The XOR stuff works like this by the way:
update swapper set foo = foo ^ bar, bar = foo ^ bar, foo = foo ^ bar;

I believe have a intermediate exchange variable is the best practice in such way:
update z set c1 = #c := c1, c1 = c2, c2 = #c
First, it works always; second, it works regardless of data type.
Despite of Both
update z set c1 = c1 ^ c2, c2 = c1 ^ c2, c1 = c1 ^ c2
and
update z set c1 = c1 + c2, c2 = c1 - c2, c1 = c1 - c2
are working usually, only for number data type by the way, and it is your responsibility to prevent overflow, you can not use XOR between signed and unsigned, you also can not use sum for overflowing possibility.
And
update z set c1 = c2, c2 = #c where #c := c1
is not working
if c1 is 0 or NULL or zero length string or just spaces.
We need change it to
update z set c1 = c2, c2 = #c where if((#c := c1), true, true)
Here is the scripts:
mysql> create table z (c1 int, c2 int)
-> ;
Query OK, 0 rows affected (0.02 sec)
mysql> insert into z values(0, 1), (-1, 1), (pow(2, 31) - 1, pow(2, 31) - 2)
-> ;
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 0 | 1 |
| -1 | 1 |
| 2147483647 | 2147483646 |
+------------+------------+
3 rows in set (0.02 sec)
mysql> update z set c1 = c1 ^ c2, c2 = c1 ^ c2, c1 = c1 ^ c2;
ERROR 1264 (22003): Out of range value for column 'c1' at row 2
mysql> update z set c1 = c1 + c2, c2 = c1 - c2, c1 = c1 - c2;
ERROR 1264 (22003): Out of range value for column 'c1' at row 3
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 0 | 1 |
| 1 | -1 |
| 2147483646 | 2147483647 |
+------------+------------+
3 rows in set (0.02 sec)
mysql> update z set c1 = c2, c2 = #c where #c := c1;
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 0 | 1 |
| -1 | 1 |
| 2147483647 | 2147483646 |
+------------+------------+
3 rows in set (0.00 sec)
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 1 | 0 |
| 1 | -1 |
| 2147483646 | 2147483647 |
+------------+------------+
3 rows in set (0.00 sec)
mysql> update z set c1 = #c := c1, c1 = c2, c2 = #c;
Query OK, 3 rows affected (0.02 sec)
Rows matched: 3 Changed: 3 Warnings: 0
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 0 | 1 |
| -1 | 1 |
| 2147483647 | 2147483646 |
+------------+------------+
3 rows in set (0.00 sec)
mysql>update z set c1 = c2, c2 = #c where if((#c := c1), true, true);
Query OK, 3 rows affected (0.02 sec)
Rows matched: 3 Changed: 3 Warnings: 0
mysql> select * from z;
+------------+------------+
| c1 | c2 |
+------------+------------+
| 1 | 0 |
| 1 | -1 |
| 2147483646 | 2147483647 |
+------------+------------+
3 rows in set (0.00 sec)

ALTER TABLE table ADD COLUMN tmp;
UPDATE table SET tmp = X;
UPDATE table SET X = Y;
UPDATE table SET Y = tmp;
ALTER TABLE table DROP COLUMN tmp;
Something like this?
Edit: About Greg's comment:
No, this doesn't work:
mysql> select * from test;
+------+------+
| x | y |
+------+------+
| 1 | 2 |
| 3 | 4 |
+------+------+
2 rows in set (0.00 sec)
mysql> update test set x=y, y=x;
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from test;
+------+------+
| x | y |
+------+------+
| 2 | 2 |
| 4 | 4 |
+------+------+
2 rows in set (0.00 sec)

Two alternatives
1. Use a temporary table
2. Investigate
the XOR algorithm

As other answers point out, a simple swap won't work with MySQL because it caches the value of column 1 immediately before processing column 2, resulting in both columns being set to the value of column 2.
Given that the order of operations is not guaranteed in MySQL, using a temporary variable is also not reliable.
The only safe way to swap two columns without modifying the table structure is with an inner join, which requires a primary key (id in this case).
UPDATE mytable t1, mytable t2
SET t1.column1 = t1.column2,
t1.column2 = t2.column1
WHERE t1.id = t2.id;
This will work without any issues.

I've not tried it but
UPDATE tbl SET #temp=X, X=Y, Y=#temp
Might do it.
Mark

This surely works! I've just needed it to swap Euro and SKK price columns. :)
UPDATE tbl SET X=Y, Y=#temp where #temp:=X;
The above will not work (ERROR 1064 (42000): You have an error in your SQL syntax)

In SQL Server, you can use this query:
update swaptable
set col1 = t2.col2,
col2 = t2.col1
from swaptable t2
where id = t2.id

Assuming you have signed integers in your columns, you may need to use CAST(a ^ b AS SIGNED), since the result of the ^ operator is an unsigned 64-bit integer in MySQL.
In case it helps anyone, here's the method I used to swap the same column between two given rows:
SELECT BIT_XOR(foo) FROM table WHERE key = $1 OR key = $2
UPDATE table SET foo = CAST(foo ^ $3 AS SIGNED) WHERE key = $1 OR key = $2
where $1 and $2 are the keys of two rows and $3 is the result of the first query.

You could change column names, but this is more of a hack. But be cautious of any indexes that may be on these columns

Table name is customer.
fields are a and b, swap a value to b;.
UPDATE customer SET a=(#temp:=a), a = b, b = #temp
I checked this is working fine.

You can apply below query, It worked perfect for me.
Table name: studentname
only single column available: name
update studentnames
set names = case names
when "Tanu" then "dipan"
when "dipan" then "Tanu"
end;
or
update studentnames
set names = case names
when "Tanu" then "dipan"
else "Tanu"
end;

Swapping of column values using single query
UPDATE my_table SET a=#tmp:=a, a=b, b=#tmp;
cheers...!

I had to just move value from one column to the other (like archiving) and reset the value of the original column.
The below (reference of #3 from accepted answer above) worked for me.
Update MyTable set X= (#temp:= X), X = 0, Y = #temp WHERE ID= 999;

CREATE TABLE Names
(
F_NAME VARCHAR(22),
L_NAME VARCHAR(22)
);
INSERT INTO Names VALUES('Ashutosh', 'Singh'),('Anshuman','Singh'),('Manu', 'Singh');
UPDATE Names N1 , Names N2 SET N1.F_NAME = N2.L_NAME , N1.L_NAME = N2.F_NAME
WHERE N1.F_NAME = N2.F_NAME;
SELECT * FROM Names;

This example swaps start_date and end_date for records where the dates are the wrong way round (when performing ETL into a major rewrite, I found some start dates later than their end dates. Down, bad programmers!).
In situ, I'm using MEDIUMINTs for performance reasons (like Julian days, but having a 0 root of 1900-01-01), so I was OK doing a condition of WHERE mdu.start_date > mdu.end_date.
The PKs were on all 3 columns individually (for operational / indexing reasons).
UPDATE monitor_date mdu
INNER JOIN monitor_date mdc
ON mdu.register_id = mdc.register_id
AND mdu.start_date = mdc.start_date
AND mdu.end_date = mdc.end_date
SET mdu.start_date = mdu.end_date, mdu.end_date = mdc.start_date
WHERE mdu.start_date > mdu.end_date;

Let's say you want to swap the value of first and last name in tb_user.
The safest would be:
Copy tb_user. So you will have 2 tables: tb_user and tb_user_copy
Use UPDATE INNER JOIN query
UPDATE tb_user a
INNER JOIN tb_user_copy b
ON a.id = b.id
SET a.first_name = b.last_name, a.last_name = b.first_name

if you want to swap all the columns where x is to y and y to x; use this query.
UPDATE table_name SET column_name = CASE column_name WHERE 'value of col is x' THEN 'swap it to y' ELSE 'swap it to x' END;

Let's imagine this table and let's try to swap the m and f from the 'sex' table:
id
name
sex
salary
1
A
m
2500
2
B
f
1500
3
C
m
5500
4
D
f
500
UPDATE sex
SET sex = CASE sex
WHEN 'm' THEN 'f'
ELSE 'm'
END;
So the updated table becomes:
id
name
sex
salary
1
A
f
2500
2
B
m
1500
3
C
f
5500
4
D
m
500

Related

Why does MySQL add 1 to ENUM values?

I discovered something strange today when I was trying to select sum from an ENUM field. It was adding 1 to the values! A sample table I used is shown below:
x y
_______
3 | 2
0 | 1
Both x and y are ENUM ('0','1','2','3')
My query is as follows:
select sum(x), sum(y), sum(x+y) from myfield
And the result is:
5 5 10
This is very strange. Does it mean that is this behaviour consistent? Can I use something like:
select sum(x - 1), sum(y - 1), sum((x-1)+(y-1)) from myfield
which will produce the correct results or is this behavior only peculiar to my db server in particular?
Thanks.
EDIT:
For those wondering why I'm using ENUM, it's because the actual field I'm using contains 'AR' which can't fit in into tinyint.
As an ENUM field is really just an INT UNSIGNED, it will not work as you expect, if you use integer values for the ENUMs. For example, your ENUM of '0', is stored internally as a numeric 1, and your '1', is stored as a numeric 2. Surprisingly, the empty string '' is stored internally as a 0. Here's an example of this not working as expected:
mysql> CREATE TABLE enumtest (
-> id int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> a ENUM ('0','1','2','3') NOT NULL DEFAULT '0',
-> i int unsigned NOT NULL DEFAULT 0
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO enumtest SET a = 0, i=0;
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> SHOW WARNINGS;
+---------+------+----------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------+
| Warning | 1265 | Data truncated for column 'a' at row 1 |
+---------+------+----------------------------------------+
1 row in set (0.00 sec)
mysql> INSERT INTO enumtest SET a = '0', i=0;
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO enumtest SET a = 1, i=1;
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO enumtest SET a = '1', i=1;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT a,0+a,i FROM enumtest;
+---+-----+---+
| a | 0+a | i |
+---+-----+---+
| | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 1 | 1 |
| 1 | 2 | 1 |
+---+-----+---+
4 rows in set (0.00 sec)
mysql> SELECT SUM(a),SUM(0+a), SUM(i) FROM enumtest;
+--------+----------+--------+
| SUM(a) | SUM(0+a) | SUM(i) |
+--------+----------+--------+
| 4 | 4 | 2 |
+--------+----------+--------+
1 row in set (0.00 sec)
The clause 0+a coerces the ENUM to its underlying UNSIGNED value. This is equivalent to CAST(a AS UNSIGNED).
This is because doing SUM() on an enum column doesn't do what you think it does. The data stored for an enum column type is the index into the enum, that index starts at 1, and it's this index that mysql will SUM()
Your table looks like this when displayed:
x y
_______
3 | 2
0 | 1
This is showing your enum values - you just happened to define your enum values to be numbers too. You could have defined them to be e.g. ENUM ('blue','red','green','yellow')
And it would have looked like:
x y
_______________
yellow | green
blue | red
However, this is for display only. What is actually stored in the rows for this table is
the index in the enum.
The elements listed in the column specification are assigned index
numbers, beginning with 1.
So those indexes starts at 1. it's these underlying data that SUM() and other aggregate functions works at for an ENUM column. There's no implicit conversion to the enum values, which you defined as numbers too.
i.e. the data stored is these indexes:
x y
_______
4 | 3
1 | 2
And while it doesn't really make sense to SUM enums, it is those indexes that mysql would aggregate when using SUM()
The docs is a must read.

Enforce order on composite primary key

Say I have the following table
item_a_id, item_b_id, value
Where item_a_id and item_b_id are a composite primary key. In my example a,b and b,a are equivalent. Therefore I want to ensure that item_a_id < item_b_id. Obviously the application logic will enforce this but is there a way to ensure the database does too?
In a reasonably current version of MySql you can use triggers to emulate a check constraint that produces the desired behavior.
Well, in your case, you could use trigger to check values before insert/update and swap it to ensure item_a_id will always less than item_b_id.
Assuming the table name is item_links, you could try this:
DELIMITER |
CREATE TRIGGER ensure_a_b_before_insert BEFORE INSERT ON item_links
FOR EACH ROW
BEGIN
IF NEW.item_a_id > NEW.item_b_id THEN
SET #tmp = NEW.item_b_id;
SET NEW.item_b_id = NEW.item_a_id;
SET NEW.item_a_id = #tmp;
END IF;
END;
|
CREATE TRIGGER ensure_a_b_before_update BEFORE UPDATE ON item_links
FOR EACH ROW
BEGIN
IF NEW.item_a_id > NEW.item_b_id THEN
SET #tmp = NEW.item_b_id;
SET NEW.item_b_id = NEW.item_a_id;
SET NEW.item_a_id = #tmp;
END IF;
END;
|
DELIMITER ;
Here's what I got when I test inserting:
mysql> INSERT INTO `item_links` (`item_a_id`, `item_b_id`, `value`)
-> VALUES ('1', '2', 'a')
-> , ('3', '2', 'b')
-> , ('4', '1', 'c');
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM `item_links`;
+-----------+-----------+-------+
| item_a_id | item_b_id | value |
+-----------+-----------+-------+
| 1 | 2 | a |
| 2 | 3 | b |
| 1 | 4 | c |
+-----------+-----------+-------+
3 rows in set (0.00 sec)
Update works, too:
mysql> UPDATE `item_links`
-> SET `item_a_id` = 100, `item_b_id` = 20
-> WHERE `item_a_id` = 1 AND `item_b_id` = 2;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT * FROM `item_links`;
+-----------+-----------+-------+
| item_a_id | item_b_id | value |
+-----------+-----------+-------+
| 20 | 100 | a |
| 2 | 3 | b |
| 1 | 4 | c |
+-----------+-----------+-------+
3 rows in set (0.00 sec)

MySQL aggregate function problem

In the following example, why does the min() query return results, but the max() query does not?
mysql> create table t(id int, a int);
Query OK, 0 rows affected (0.10 sec)
mysql> insert into t(id, a) values(1, 1);
Query OK, 1 row affected (0.03 sec)
mysql> insert into t(id, a) values(1, 2);
Query OK, 1 row affected (0.02 sec)
mysql> select * from t
-> ;
+------+------+
| id | a |
+------+------+
| 1 | 1 |
| 1 | 2 |
+------+------+
2 rows in set (0.00 sec)
mysql> select * from t where a < 4;
+------+------+
| id | a |
+------+------+
| 1 | 1 |
| 1 | 2 |
+------+------+
2 rows in set (0.00 sec)
mysql> select * from t where a < 4 having a = max(a);
Empty set (0.00 sec)
mysql> select * from t where a < 4 having a = min(a);
+------+------+
| id | a |
+------+------+
| 1 | 1 |
+------+------+
1 row in set (0.00 sec)
The HAVING clause is used to filter groups of rows. You reference min(a) and max(a) which (in the absence of any GROUP BY clause) aggregate over all a values in the table but then use a comparison against a single a value.
So which a value is MySQL supposed to use? All other RDBMSs that I know of would throw an error at this point however MySQL does allow this. From the docs
Standard SQL does not permit the HAVING clause to name any column
not found in the GROUP BY clause unless it is enclosed in an aggregate
function. MySQL permits the use of such columns to simplify
calculations. This extension assumes that the nongrouped columns will
have the same group-wise values. Otherwise, the result is
indeterminate.
So in your case from the results you are getting it appears that it ended up using 1 as the scalar value for a but this behaviour is not guaranteed and it could equally well have used 2 or any other existing a value.

Set iterative values in rows of a table

I have the following table
id name address empid
1 AA aa 0
2 BB bb 0
3 CC cc 0
I need to write a query to set empid starting from 1. How to write it please. Do i have to use a stored procedure to that or can do it with a normal query?
Thank You.
Here is a way to do it that utilizes a pretty obscure assignment operator in MySQL. This solution won't skip numbers in the case of gaps in the primary key sequence like some of the other solutions.
set #count = 0;
update test set empid = #count := #count+1;
Here is the proof:
mysql> create table test (
-> id int unsigned primary key auto_increment,
-> name varchar(32) not null,
-> address varchar(32) not null,
-> empid int unsigned not null default 0
-> ) engine=innodb;
Query OK, 0 rows affected (0.02 sec)
mysql> insert into test (name, address)
-> values ('AA', 'aa'), ('BB', 'bb'), ('CC', 'cc');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from test;
+----+------+---------+-------+
| id | name | address | empid |
+----+------+---------+-------+
| 1 | AA | aa | 0 |
| 2 | BB | bb | 0 |
| 3 | CC | cc | 0 |
+----+------+---------+-------+
3 rows in set (0.00 sec)
mysql> set #count=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test set empid = #count := #count+1;
Query OK, 3 rows affected (0.00 sec)
Rows matched: 3 Changed: 3 Warnings: 0
mysql> select * from test;
+----+------+---------+-------+
| id | name | address | empid |
+----+------+---------+-------+
| 1 | AA | aa | 1 |
| 2 | BB | bb | 2 |
| 3 | CC | cc | 3 |
+----+------+---------+-------+
3 rows in set (0.00 sec)
If you are looking to put 1 in empid for the first row, 2 for the second, etc. the easiest way would be to use your id field that is already doing this like so:
UPDATE table
SET empid = id
The only thing you need to worry about is missing numbers in the id column. If that would be an issue and you are missing id numbers, you will have to use a different method. To do that, you would need to do something like this:
DECLARE #counter int
SET #counter = 1
UPDATE table
SET #counter = empid = #counter + 1
As #BiggsTRC suggested you can use id to set empid. If not, you can create stored procedure or some PHP code to do that.
If your ID is not "AutoIncrement" field, you can consider a new column as autoincrement field and assign that value to emp with update query and later delete that new column. (These are some alternates, you need to choose the best one)
UPDATE `test` SET `empid`=`id`
But why would you want to do it? It's pretty much definition of redundancy.

Removing duplicate entries from a Mysql database

I've got a table with three columns, obj1, obj2 (both varchars) and the distance between the objects. Unfortunately the way the data was constructed, I've double the number of entries, for example,
obj1 obj2 distance
c1 c2 10.5
c2 c1 10.5
Want I want is to be able to delete one of the entries listed. I've thought and tried to use the Exists clause, but had no luck. I'm wondering if this requires a stored procedure?
Any help would be gratefully received!
Jim
mysql> create table doubles(a int,b int,c int);
Query OK, 0 rows affected (0.11 sec)
mysql> insert into doubles values (1,2,10),(2,1,10),(1,3,12),(3,1,12),(2,3,13);
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from doubles;
+------+------+------+
| a | b | c |
+------+------+------+
| 1 | 2 | 10 |
| 2 | 1 | 10 |
| 1 | 3 | 12 |
| 3 | 1 | 12 |
| 2 | 3 | 13 |
+------+------+------+
5 rows in set (0.00 sec)
mysql> DELETE a FROM doubles a JOIN doubles b ON a.a = b.b AND a.b = b.a AND a.a > b.a;
Query OK, 2 rows affected (0.03 sec)
mysql> select * from doubles;
+------+------+------+
| a | b | c |
+------+------+------+
| 1 | 2 | 10 |
| 1 | 3 | 12 |
| 2 | 3 | 13 |
+------+------+------+
3 rows in set (0.00 sec)
The last clause (a.a > b.a) could equally be a.a < b.a, we just have to decide which one of the doubles should go.
If you can guarantee that every row has a "duplicate" with obj1 and obj2 values reversed, then you can remove one such row for each duplicate by doing
DELETE FROM dist WHERE obj1 > obj2
where dist is the name of your table.
If your table has rows where obj1 equals obj2, then you could make a unique index on (obj1,obj2):
ALTER IGNORE TABLE dist ADD UNIQUE INDEX dist_index (obj1,obj2)
The above command will drop rows from the table whenever the unique index constraint is not satisfied. (The first row where obj1 equals obj2, the row will be kept because the unique index constraint is still satisfied. The second row where obj1 equals obj2 will be dropped, because the second row contradicts the uniqueness constraint.)
You can choose to keep the unique index, or, if you wish to drop it, the command would be:
ALTER TABLE dist DROP INDEX dist_index