简体   繁体   中英

MySQL convert many-to-many table into one-to-one

I have a table which is created by joining 2 tables and now the two columns looks like having a many-to-many relationship. I want to convert this to a one-to-one relationship meaning, values in both the columns has to be unique . Here is a sample setting;

create table Test(id1 integer, id2 integer);
insert into Test(id1, id2) values(1, 10);
insert into Test(id1, id2) values(1, 20);
insert into Test(id1, id2) values(2, 10);
insert into Test(id1, id2) values(2, 20);
select * from Test;
id1 id2
1 10
1 20
2 10
2 20

I want to convert the above table into something as below;

tbl1

id1 id2
1 10
2 20

OR

tbl2

id1 id2
1 20
2 10

How to write a SQL query to convert test table to tbl1 or tbl2 ?

This will take out unique values as mentioned in the comments on my other answer.

SELECT a.id1, b.id2
FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY id1) AS RowNum FROM (SELECT DISTINCT id1 FROM test) x
) AS a,
(
    SELECT *, ROW_NUMBER() OVER (ORDER BY id2) AS RowNum FROM (SELECT DISTINCT id2 FROM test) x
) AS b
WHERE a.RowNum = b.RowNum

Let me know if there is something I misunderstood.

Ok. To force the table to have unique columns you can just add a unique index on the table. (Feel free to change to better name on the index).

CREATE UNIQUE INDEX my_index_name_1 ON test(id1);
CREATE UNIQUE INDEX my_index_name_2 ON test(id2);

Then the indexes will not allow any duplicates. (There can't be any duplicates when you create the indexes above.)

But....if you have full control over the database it seems a bit strange to have this type of table if there are only 1 to 1 relations. Then you could just add a fields on the two main tables instead.

If the ids are coming from the tables 'Table1' and 'Table2' you can have a field in Table1 which are called table2_id and pointing to the row in table2. And in the Table2 you could have a field called table1_id and that would point to table1. Then you could just skip this table and all searches would be faster. But of course there might be a reason this is not possible. Just wanted to mention it anyways...

I misunderstood the question at first, so this is my new answer. As I understand it you want to remove rows so the only rows left are unique and it does not matter which rows are deleted? If so, this solution will work:

DELETE test
FROM test
JOIN (
    SELECT * FROM (
        SELECT t.id1, t.id2, (SELECT MAX(id2) FROM test WHERE id1 = t.id1) AS maxId2
            from test t
        ) AS r
    WHERE r.id2 != maxId2
) d
 ON d.id1 = test.id1 AND d.id2 = test.id2;

This query will delete all rows except the ones with the highest id2. Please test it and make a backup before running it, I have of course tested it on my side but still.

Relational databases do not recommend/allow many to many relationships by design. However, for this specific requirement, following code should get closer to what you want. Code is on a dbfiddle

create table tb11(id1 integer, id2 integer);
create table tb12(id1 integer, id2 integer);

INSERT INTO tb11  
SELECT id1, id2 FROM  
(SELECT
    id1,
    id2,
    RANK() OVER (PARTITION BY id2 ORDER BY id1 ASC) as rnk
FROM
    Test) t1 where t1.rnk = 1;

INSERT INTO tb12  
SELECT id1, id2 FROM  
(SELECT
    id1,
    id2,
    RANK() OVER (PARTITION BY id2 ORDER BY id1 ASC) as rnk
FROM
    Test) t2  where t2.rnk = 2;

SELECT * FROM tb11;
SELECT * FROM tb12;

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