2017-08-28 1 views
1

J'ai une petite question sur les déclencheurs et les contraintes uniques dans MySQL. Je vais donner un exemple rapide.Ajout d'une contrainte ou d'un déclencheur mysql

J'ai deux tables, "Compte" et "Utilisateurs". La relation entre ces deux tables est de 1 (compte) à plusieurs (utilisateurs). Donc, si vous trouvez une table de compte avec un identifiant unique, vous rencontrerez plusieurs utilisateurs pour ce compte. La table User contient une colonne appelée 'user_type'. 'user_type' peut être 'admin', 'guest', 'trial'. Le "compte" doit avoir un utilisateur qui a "admin" comme un "user_type". Il ne peut avoir qu'un 'admin' par compte. Donc, y a-t-il un moyen d'ajouter une contrainte unique pour que n'importe quel compte ne puisse avoir qu'un type d'utilisateur 'admin'? Ou doit-il être un déclencheur puisque c'est MySQL?

Merci!

Répondre

0

Vous auriez un système plus flexible si vous implémentiez des groupes plutôt que d'en faire un attribut de la table utilisateur. Cela dit, évidemment, vous pouvez appliquer votre règle de gestion avec un déclencheur.

Il existe toutefois un moyen d'utiliser une contrainte, au moins partiellement.

Créer une table:

AccountAdmin 
------------ 
account_admin_id (pk) 
account_id (fk) Add unique constraint 
user_id (fk) 

Une fois que vous avez l'index unique sur account_id, vous appliquer votre règle selon laquelle il ne peut y avoir qu'un seul administrateur par compte. Lorsque vous créez un utilisateur qui est un administrateur, vous devez insérer une ligne dans AccountAdmin. La contrainte vous empêchera de le faire si un administrateur existe déjà. Cela dit, pour changer d'un administrateur à l'autre, vous devez d'abord supprimer la ligne de cette table, puis ajouter la nouvelle. Une fois que vous commencez à considérer le type de code dont vous avez besoin pour supporter une fonction comme "change admin" ou "promote to admin", les triggers sont encore moins souhaitables à mon avis.

En ce qui concerne Triggers:

Après avoir fait beaucoup de travail sur les systèmes où il y a une exigence que le système soit extensible à « n » utilisateurs, l'une des choses que j'ai appris à éviter est la création les goulets d'étranglement au niveau de la persistance des données. Les déclencheurs MySQL et les procs stockés ne sont pas intrinsèquement extensibles.

Vous concentrez un goulot d'étranglement dans la base de données et vous limitez la concurrence. Il n'y a aucun moyen de faire cet exercice dans un déclencheur sans verrouiller la table pour s'assurer que lorsque vous interrogez pour vous assurer qu'il n'y a pas d'administrateurs pour votre organisation, une autre requête n'insère pas d'administrateur avant vous. C'est aussi un anti-pattern d'avoir à lancer une exception de base de données et à gérer cela, pour quelque chose qui n'est pas une erreur du tout (c'est-à-dire qu'un admin existe déjà).

S'il existe un moyen de déplacer ce code dans l'application (et dans ce cas, c'est le cas), il s'agit d'une solution plus évolutive.

+0

Sans cette table est un déclencheur le seul chemin que j'ai disponible? –

+1

Comment pourriez-vous avoir dépassé le point d'ajouter une table qui relie 2 tables existantes par leurs clés? – gview

+0

C'est juste un exemple. –

0

La méthode la plus simple pour vous assurer que chaque compte a exactement un administrateur pour avoir une clé étrangère sur la ligne Utilisateur qui est l'administrateur.

CREATE TABLE Users (
    user_id INT AUTO_INCREMENT PRIMARY KEY, 
    account_id INT 
); 

CREATE TABLE Account (
    account_id INT AUTO_INCREMENT PRIMARY KEY, 
    admin_user_id INT NOT NULL 
); 

ALTER TABLE Account ADD FOREIGN KEY (admin_user_id) 
    REFERENCES Users (user_id); 

ALTER TABLE Users ADD FOREIGN KEY (account_id) 
    REFERENCES Account(account_id); 

INSERT INTO Users() VALUES(); 
SET @uid = LAST_INSERT_ID(); 
INSERT INTO Account (admin_user_id) VALUES (@uid); 
UPDATE Users SET account_id = LAST_INSERT_ID() WHERE user_id = @uid; 

Faire en sorte que les utilisateurs pour chaque compte sont limités à un seul administrateur et je suppose plusieurs utilisateurs d'autres types est un peu plus délicat. Mais avec des colonnes virtuelles dans MySQL 5.7, nous pouvons le faire.

ALTER TABLE Users 
    ADD COLUMN user_type ENUM('admin', 'guest', 'trial') NOT NULL DEFAULT 'guest', 
    ADD COLUMN is_admin TINYINT(1) AS (IF(user_type='admin',1,NULL)) STORED, 
    ADD UNIQUE KEY (account_id, is_admin); 

SELECT * FROM Users; 
+---------+------------+-----------+----------+ 
| user_id | account_id | user_type | is_admin | 
+---------+------------+-----------+----------+ 
|  1 |   1 | guest  |  NULL | 
+---------+------------+-----------+----------+ 

INSERT INTO Users (account_id, user_type) VALUES (1, 'admin'); 

SELECT * FROM Users; 
+---------+------------+-----------+----------+ 
| user_id | account_id | user_type | is_admin | 
+---------+------------+-----------+----------+ 
|  1 |   1 | guest  |  NULL | 
|  2 |   1 | admin  |  1 | 
+---------+------------+-----------+----------+ 

INSERT INTO Users (account_id, user_type) VALUES (1, 'admin'); 
ERROR 1062 (23000): Duplicate entry '1-1' for key 'account_id' 

La CLÉ UNIQUE sur la colonne virtuelle ignore les valeurs NULL. Ainsi, n'importe quel nombre d'utilisateurs invités ou d'essai peut exister, et ils sont tous représentés par NULL dans la colonne virtuelle. Mais un administrateur par compte peut exister avec la valeur 1 dans cette colonne virtuelle. Supposons que nous ayons dépassé le point de pouvoir ajouter de nouvelles tables.