Id absolu et Id relatif dans la même table MySQL

Id absolu et Id relatif dans la même table MySQL - SQL/NoSQL - Programmation

Marsh Posté le 10-03-2017 à 17:25:56    

Bonjour à tou(te)s,
 
Je travaille sur un système de demandes où chaque demande doit avoir:

  • une date de saisie ("Saisie" )
  • un identifiant global unique auto-incrémenté par MySQL ("Id" )
  • un identifiant relatif à l'année de saisie ("IdAn" )


Pour calculer l'identifiant relatif à l'année "IdAn" pour une demande donnée, on soustrait le premier "Id" ayant la même année de saisie de l'Id de la demande en question (et on ajoute 1).
 
Je pourrais faire ça au niveau de mon code PHP, mais je cherche à gérer ça au niveau de MySQL.
 
Je pourrais intégrer systématiquement une sous-requête du style "SELECT MIN(Id) FROM Demande WHERE YEAR(Saisie) = ...", mais vu le nombre de requêtes de sélection faites, ça ne me semble pas du tout optimal.
 
Je pourrais me baser sur une vue établissant les correspondances entre les années et les premiers Id des demandes, mais ça ne me semble pas plus optimal.
 
Je pensais éventuellement à une "vue matérialisée", mais ça me semble un peu lourd à gérer au niveau des mises à jour, et je ne trouve pas ça forcément très élégant.
 
Le plus optimal me semblait donc être l'ajout d'un champ "IdAn" dans ma table "Demande" : on "doublonne" l'information, donc ce n'est pas très "canonique", mais ça me semble être la solution la plus performante.
 
Le problème se situe au niveau de la mise à jour du champ.
 
J'ai essayé de recourir à un trigger:

Code :
  1. DELIMITER |
  2. CREATE TRIGGER t_after_insert_id_an AFTER INSERT
  3. ON Demande FOR EACH ROW
  4. BEGIN
  5.     SET @id:=NEW.Id;
  6.     SET @an:=( SELECT YEAR(Saisie) FROM Demande WHERE Id = @id );
  7.     SET @mid:=( SELECT MIN(Id) FROM Demande WHERE YEAR(Saisie) = @an );
  8.     SET NEW.IdAn = (@id - @mid + 1)
  9. END |
  10. DELIMITER ;


Malheureusement, le bousin semble couiner du fait que je lance des sous-requêtes sur la table Demande dans un trigger qui se déclenche après insertion dans cette même table Demande.
 
Du coup, là, je suis un peu en panne de bonnes idées... En auriez-vous à me soumettre ? Est-ce que vous voyez une autre manière à la fois élégante et performante d'aborder le problème ? Vous remerciant par avance pour vos réponses.

Reply

Marsh Posté le 10-03-2017 à 17:25:56   

Reply

Marsh Posté le 16-03-2017 à 12:33:05    

Finalement, j'ai opté pour une procédure stockée avec paramètre que j'appellerai en lieu et place d'une commande INSERT:

Code :
  1. DROP PROCEDURE IF EXISTS creer_Demande;
  2. DELIMITER |
  3. CREATE PROCEDURE creer_Demande(IN pObjet VARCHAR(255), IN pDescription MEDIUMTEXT, IN pIdDemandeur VARCHAR(5))
  4. BEGIN
  5.         DECLARE mid BIGINT;
  6.         DECLARE nb BIGINT;
  7.         SELECT MIN(Id) INTO mid FROM Demande WHERE YEAR(Saisie) = YEAR(NOW()) GROUP BY YEAR(Saisie);
  8.         SELECT COUNT(*) INTO nb FROM Demande WHERE YEAR(Saisie) = YEAR(NOW());
  9.         INSERT INTO Demande (Objet, Description, Saisie, IdDemandeur) VALUES (pObjet, pDescription, NOW(), pIdDemandeur);
  10.         IF nb > 0 THEN
  11.                 UPDATE Demande SET IdAn = (LAST_INSERT_ID() - mid + 1) WHERE Id = LAST_INSERT_ID();
  12.         ELSE
  13.                 UPDATE Demande SET IdAn = 1 WHERE Id = LAST_INSERT_ID();
  14.         END IF;
  15. END|
  16. DELIMITER ;


Question bonus: a votre connaissance, est-il possible d'"intercepter" une commande INSERT pour y substituer une procédure ?
 
Je m'explique. En gros, si une personne ne connaissant pas le fonctionnement de l'appli PHP et de la base MySQL veux insérer "manuellement" une demande, il ne pensera pas à appeler ma procédure creer_Demande et il fera tout simplement un INSERT INTO Demande... L'idée serait donc qu'un trigger, par exemple, détecte l'appel à INSERT, le "neutralise", et appelle creer_Demande à la place, ou plus simplement émette une erreur en refusant d'insérer un enregistrement de cette façon.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed