How to model n-to-n relationship correctly? Women also have friendship with men and not only men have friendship with women

Asked

Viewed 807 times

2

Hello, I want to do the following: Create in Mysql the n-to-n relationship where men have friendships with women and women also have friendships with men.

Example:

John has friendship with Fernanda, but, Fernanda has no friendship with John rather with Carlos, but John continues to have friendship with Fernanda.

I created a n-to-n model where men befriend women, but couldn’t make women befriend men and vice versa.

See my SQL script below:

create database pessoas;
use pessoas;

create table homens (id int auto_increment, nome varchar(10), primary key (id));

create table mulheres (id int auto_increment, nome varchar(10), primary key (id));

create table amizades (id int auto_increment, homem_id int, mulher_id int,
primary key(id),
foreign key(homem_id) references homens(id),
foreign key(mulher_id) references mulheres(id));

insert into homens values 
(default, 'Joao'),
(default, 'Flavio'),
(default, 'Carlos');

insert into mulheres values 
(default,'Ana'),
(default,'Fernanda'),
(default,'Julia');

insert into amizades values 
(default, 1, 2),
(default, 2, 1),
(default, 3, 3);

/* join com 3 tabelas, aparecer o id e nome do homem e depois o id e nome da mulher de quem o homem é amigo */
select homens.id, homens.nome, mulheres.id, mulheres.nome from homens join amizades on homens.id = amizades.homem_id
join mulheres on amizades.mulher_id = mulheres.id order by homens.id;

I want an explicit relationship in the database so I know who has friendship with whom and not just simulate that relationship by changing the order of the columns in the SELECT, understood? What better way to do this?

  • Make a table only PERSON (ID, NAME, SEX) other FRIENDSHIP (ID_DE,ID_PARA) and by TRIGGER test if the sexes are different

  • More I want to know regarding n-to-n something explicit and not testing the sexes.

  • just did not see the need for two tables, the relationship would be maintained.

  • @Motta Valeu ai

2 answers

1


I would create two relationship tables: amizades_hm and amizades_mh. Both have only two fields: homem_id and mulher_id, both being part of the primary key in both tables:

Here is the script to create the database:

CREATE DATABASE pessoas;
USE pessoas;

CREATE table homens (
    id INT AUTO_INCREMENT,
    nome VARCHAR(10),
    PRIMARY KEY (id)
);

CREATE table mulheres (
    id INT AUTO_INCREMENT,
    nome VARCHAR(10),
    PRIMARY KEY (id)
);

CREATE table amizades_hm (
    homem_id INT,
    mulher_id INT,
    PRIMARY KEY (homem_id, mulher_id),
    FOREIGN KEY (homem_id) REFERENCES homens(id),
    FOREIGN KEY (mulher_id) REFERENCES mulheres(id)
);

CREATE table amizades_mh (
    homem_id INT,
    mulher_id INT,
    PRIMARY KEY (homem_id, mulher_id),
    FOREIGN KEY (homem_id) REFERENCES homens(id),
    FOREIGN KEY (mulher_id) REFERENCES mulheres(id)
);

To insert data into it:

INSERT INTO homens VALUES (DEFAULT, 'Joao'), (DEFAULT, 'Flavio'), (DEFAULT, 'Carlos');
INSERT INTO mulheres VALUES (DEFAULT, 'Ana'), (DEFAULT, 'Fernanda'), (DEFAULT, 'Julia');
INSERT INTO amizades_hm VALUES
    (1 /*Joao*/, 2 /*Fernanda*/),
    (2 /*Flavio*/, 1 /*Ana*/),
    (3 /*Carlos*/, 3 /*Julia*/);
INSERT INTO amizades_mh VALUES
    (2 /*Flavio*/, 2 /*Fernanda*/),
    (2 /*Flavio*/, 3 /*Julia*/),
    (3 /*Carlos*/, 3 /*Julia*/);

To read each man’s friends:

SELECT homens.id, homens.nome, mulheres.id, mulheres.nome
FROM homens
INNER JOIN amizades_hm ON homens.id = amizades_hm.homem_id
INNER JOIN mulheres ON amizades_hm.mulher_id = mulheres.id
ORDER BY homens.id;

Exit:

id | nome   | id | nome
---+--------+----+--------
1  | Joao   | 2  | Fenanda
2  | Flavio | 2  | Ana
3  | Carlos | 3  | Julia

To read each woman’s friends:

SELECT mulheres.id, mulheres.nome, homens.id, homens.nome
FROM mulheres
INNER JOIN amizades_mh ON mulheres.id = amizades_mh.mulher_id
INNER JOIN homens ON amizades_mh.homem_id = homens.id
ORDER BY mulheres.id;

Exit:

id | nome     | id | nome
---+----------+----+-------
1  | Fernanda | 2  | Flavio
3  | Julia    | 2  | Flavio
3  | Julia    | 3  | Carlos

To read all the friendships:

SELECT a.origem_tipo, a.origem_id, a.origem_nome, a.destino_tipo, a.destino_id, a.destino_nome
FROM (
    SELECT
        'Homem' AS origem_tipo,
        h1.id AS origem_id,
        h1.nome AS origem_nome,
        'Mulher' AS destino_tipo,
        m1.id AS destino_id,
        m1.nome AS destino_nome
    FROM homens h1
    INNER JOIN amizades_hm hm ON h1.id = hm.homem_id
    INNER JOIN mulheres m1 ON hm.mulher_id = m1.id
    UNION ALL
    (
        SELECT
            'Mulher' AS origem_tipo,
            m2.id AS origem_id,
            m2.nome AS origem_nome,
            'Homem' AS destino_tipo,
            h2.id AS destino_id,
            h2.nome AS destino_nome
        FROM mulheres m2
        INNER JOIN amizades_mh mh ON m2.id = mh.mulher_id
        INNER JOIN homens h2 ON mh.homem_id = h2.id
    )
) a
ORDER BY a.origem_tipo, a.origem_id;

Exit:

origem_tipo | origem_id | origem_nome | destino_tipo | destino_id | destino_nome
------------+-----------+-------------+--------------+------------+--------------
Homem       | 1         | Joao        | Mulher       | 2          | Fernanda
Homem       | 2         | Flavio      | Mulher       | 1          | Ana
Homem       | 3         | Carlos      | Mulher       | 3          | Julia
Mulher      | 2         | Fernanda    | Homem        | 2          | Flavio
Mulher      | 3         | Julia       | Homem        | 3          | Carlos
Mulher      | 3         | Julia       | Homem        | 2          | Flavio
  • In this way he had thought, but it came to doubt whether according to my script is possible relation h-with-m and m-with-h using 3 tables, the third being of relationship. Is this model of applying 2 field as a primary key correct? I know that only 1 field can be a primary key.

  • @Bruno It is not true that only a field can be a primary key. This form is the standard way of applying N-to-N relatedros: an intermediate table that contains all the primary key fields of each of the related tables as a foreign key and all of them are primary keys and nothing more than that. See more in this question.

  • @Bruno Daria to put the two in a single table by adding an extra field, but I decided to keep it that way because this is the standard form of N-to-N relationship and because many modeling tools identify it as such by having this exact format.

  • Thank you then! However my script has error, has 3 fields in the intermediate table, 1 id field as primary key, 1 homem_id field as foreign key and 1 field mulher_id also as foreign key

  • @Bruno In this case you modeled another entity that has a relationship with a man and a woman. The problem with your modeling is that I can do INSERT INTO amizades (DEFAULT, 1, 2), (DEFAULT, 1, 2); and then we have that João and Fernanda are friends twice - but it doesn’t make much sense for someone to be someone’s friend more than once.

  • @Bruno Ah, I edited my answer to add the output of SELECTs and put a SELECT more complex to list all friendships.

  • Thanks, Bs: I put in the friendship table that the key Primary is the foreign key of man and woman and the record did not repeat itself, of course id as key Primary the record repeats? Because I learned that the field id as Primary key the record does not repeat.

  • @Bruno Yes. The set of fields of the Primary key is always unique, the database does not allow you to insert two records with the same Primary key. Since you have a composite key, it means that it will not be possible to repeat the same values in all fields. That is, you will be able to register (2, 3) only once, but to have the (2, 3) registered does not prevent you from registering (2, 2) or (1, 3) because in that case there will be collision in only some of the primary key fields, and not at all.

  • Take me a question, I made a table and I made 2 fields, an id for key and another as name. So being able to register my name 2 times more the id was different, how do I actually have a tuple as unique in the database without having to use UNIQUE? Someone in the answers said that my model is wrong, because it would be allowed to repeat the tuple. I’m new to this SQL, but I realized that a field like Primary key is not working as I thought before I started working with SQL.

  • @Bruno The answer would be exactly to use UNIQUE. Another possibility would be to make the field nome be the primary key. However, if the nome was the primary key, when someone changed their name (e.g., correction of typo or woman who married and adopted her husband’s surname), you would break your face to change all foreign keys, and because of this it is not a good idea to make the name a primary key. So the alternative would be to make the name UNIQUE. However, there are many namesake people out there (is there only one "João da Silva"?) - And therefore, the ideal is [continue...]

  • [continuation] that you accept the fact that the name can repeat. Many real systems put a constraint UNIQUE as a whole "name + mother’s name + date of birth" to ensure oneness, in order to believe that there should not be two persons of the same name, son(a)s of mothers of the same name and born(a)s on the same day. Despite this, I once read a news report of someone who had a problem with the government because that’s exactly what happened.

  • I get it, but it’s complicated, I thought the primary key was for the record not to repeat. added in my table the Unique index for name and for the new surname field, so the id, name and surname did not repeat, but I do not know which is the correct way to solve this.

  • @Bruno The primary key serves to identify a record in a unique and unequivocal way, but nothing says about the fields that are not in the primary key. The values of the primary key fields cannot be repeated, but this has nothing to say about the other fields. In your case the id will not repeat itself and the set "name + surname" not either. If you don’t consider the case of homonyms, then fine.

  • thanks, now I understand about primary key! =)

Show 9 more comments

0

An n-to-n relation is always modeled with an auxiliary table whose primary key is the set of the two foreign keys or a single key. Technically, your solution is correct.

First, the model in question does not prevent the relationship: "(...) where men have friendship with women, (...) women have friendship with men and vice versa." , since the combinations h-h, m-m, h-m, m-h are possible; although the codes coincide.

According to,

inserir a descrição da imagem aqui

To get the intersection you can:

1)

select 
    homens.id, homens.nome, mulheres.id, mulheres.nome 
from amizades join homens on homens.id = amizades.homem_id
              join mulheres on mulheres.id = amizades.mulher_id;

2) Or,

select a.nome, b.nome
from
(select 
    homens.id, homens.nome
from amizades join homens on homens.id = amizades.homem_id) a
union
(select 
   mulheres.id, mulheres.nome 
from mulheres on mulheres.id = amizades.mulher_id) b

In none of the above cases you will need to simulate any relationship. I hope I have understood your doubt.

  • That two fields being the primary key of the auxiliary table, is that correct? In my script, only the ID field is a primary key, and it has 2 fields that are a foreign key. I did not understand that 2 foreign key is the primary key of the auxiliary table

  • I explain: you can create a primary key defined by the union of two primary keys that come from another table. Thus: ID-friendship = (ID-man + ID-woman)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.