How to determine programmatically MySQL relationship type (1:1, 1:n, n:m) between tables?

穿精又带淫゛_ 提交于 2021-02-08 03:57:14

问题


I'm trying to query a MySQL server to determine information about a database in order to scaffold some code.

I've been quite successful using Java JDBC and INFORMATION_SCHEMA table for this but the problem is that I need to determine if a table relation is OneToOne, OneToMany or ManyToMany. I can't find a good way to achieve this and I would love if someone could help me a bit and if its possible with a solution that is not MySQL specific and solid so it could help others.

I found this question in stackoverflow but it wont solve the problem: how-to-determine-cardinality-of-foreign-key-using-mysql

EDIT (More) To further explain my problem I will add a bit more information. Currently I'm using MySQL with InnoDB and MySQL Workbench to create the EER Diagram and generate the SQL to create the database.

I'm trying to reverse engineer in my Java application the relation between two existing tables to determine if a table is OneToOne, OneToMany or ManyToMany. The problem is that when I design the model in MySQL Workbench and I create a relationship between two tables I don't see any difference between Non-Identifying 1:1 and Non-Identifying 1:N even their SQL output are the same.

Non-Identifying 1:1

CREATE TABLE IF NOT EXISTS `TestDB`.`table1` (
  `var1` BIT(1) NOT NULL,
  `var2` BIT(8) NOT NULL,
  `var3` VARCHAR(45) NULL DEFAULT NULL,
  `var4` INT(11) NOT NULL,
  `table2_var1` INT(11) NOT NULL,
  PRIMARY KEY (`var1`, `var2`),
  INDEX `fk_table1_table2_idx` (`table2_var1` ASC),
  CONSTRAINT `fk_table1_table2`
    FOREIGN KEY (`table2_var1`)
    REFERENCES `TestDB`.`table2` (`var1`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci

Non-Identifying 1:n

CREATE TABLE IF NOT EXISTS `TestDB`.`table1` (
  `var1` BIT(1) NOT NULL,
  `var2` BIT(8) NOT NULL,
  `var3` VARCHAR(45) NULL DEFAULT NULL,
  `var4` INT(11) NOT NULL,
  `table2_var1` INT(11) NOT NULL,
  PRIMARY KEY (`var1`, `var2`),
  INDEX `fk_table1_table2_idx` (`table2_var1` ASC),
  CONSTRAINT `fk_table1_table2`
    FOREIGN KEY (`table2_var1`)
    REFERENCES `TestDB`.`table2` (`var1`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci

The amazing part comes when I do reverse the database using MySQL Workbench to see if it can guess if it was a 1:1 or a 1:n, it's actually able to guess it, the diagram has the correct relationship arrows!!! Perhaps it stores the reference as unique somewhere or InnoDB has this on its own vendor specific INFORMATION_SCHEMA but I would like to replicate this behaviour on my application.

Any ideas of how could I achieve this?


回答1:


After further research I found that although somehow MySQL Workbench is able to reverse engineer the 1:1 and 1:n relation even when is a non-identifying relationship where the attribute that references the foreign key isn't PK or UQ, this might be do to vendor specific (InnoDB) properties.

ALL other SQL reverse engineer tools tested showed non-identifying relationships as OneToMany even thou they where designed in MySQL WorkBench as OneToOne non-identifying. Assuming this I preformed a JOIN query to retrieve the necessary information to distinguish 1:1 from 1:n So the SQL goes like the following:

Example for 'table1'

select INFORMATION_SCHEMA.COLUMNS.COLUMN_KEY, INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME, INFORMATION_SCHEMA.COLUMNS.TABLE_NAME from INFORMATION_SCHEMA.COLUMNS
join INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
on INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME=INFORMATION_SCHEMA.KEY_COLUMN_USAGE.COLUMN_NAME
where INFORMATION_SCHEMA.KEY_COLUMN_USAGE.TABLE_NAME='table1' 
and referenced_table_name is not null

Finally...

Pseudo code

if (COLUMN_KEY == "PRI" || COLUMN_KEY == "UNI") { 
    //then you can assume is **OneToOne**
} else {
    //then you can assume is **OneToMany**
}

Hope this helps others with the struggle, feel free to add any suggestions or alternative ways of doing this, thanks all.




回答2:


If you have two tables as follows:

Table: a
a_id    unique autoincrement primary key
a_info  other information

and

Table: b
b_id    unique autoincrement primary key
a_id    a reference to a row in a
b_info  other information

There are inherently, in this design, zero, one, or more rows in b for every row in a. There is inherently a single row in a for each row in b. This is what's usually meant by OneToMany.

If the column a_id in table b happens to have a unique index, then there is either zero or one row in b for each row in a. This is what's usually meant by OneToOne.

If the relationship is to be ManyToMany, you need an extra table, as follows:

Table: a
a_id    unique autoincrement primary key
a_info  other information

and this table

Table: b
b_id    unique autoincrement primary key
b_info  other information

related together by this table

Table: a_b
a_id    reference to a row in a
b_id    reference to a row in b

This relation table implements ManyToMany. Its unique primary key is the concatenation of its two columns.

This is a fairly conventional design; you should be able to pick it out when you're reverse-engineering an existing schema. The thing to look for is the relation (a_b) style tables.



来源:https://stackoverflow.com/questions/26103542/how-to-determine-programmatically-mysql-relationship-type-11-1n-nm-betwee

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!