Jan K.: 1 aus 3 Feldern muss gefüllt werden

MySQL 5.1.41

Hallo Forum,

kommen wir gleich zur Sache:

  
CREATE TABLE file (  
	fileid 			INT UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,  
	filename_upload		varchar(255) NOT NULL,  
	filename_system		varchar(255) NOT NULL,  
	extid			INT UNSIGNED,  
	frontuseid		INT UNSIGNED,  
	userid			INT UNSIGNED DEFAULT NULL,  
	betriebid		INT UNSIGNED DEFAULT NULL,  
	unternid		INT UNSIGNED DEFAULT NULL,  
	CHECK (	(userid!=NULL AND betriebid=NULL AND unternid=NULL) OR  
			(userid=NULL AND betriebid!=NULL AND unternid=NULL) OR  
			(userid=NULL AND betriebid=NULL AND unternid!=NULL) )  
) ENGINE = InnoDB;  
  
INSERT INTO fileid VALUES(DEFAULT,"filename_upload.dat","mussichmirnochueberlegen",1,1,DEFAULT,DEFAULT,DEFAULT);  

Ich möchte erreichen, dass ein INSERT oder UPDATE auf die Tabelle file nur unter 3 Bedingungen ausgeführt wird.
Und zwar:
userid=INT betriebid=NULL unternid=NULL
ODER
userid=NULL betriebid=INT unternid=NULL
ODER
userid=NULL betriebid=NULL unternid=INT

Im die obrige Tabelle kann ich aber reinstecken was ich mag :), das check() greift nicht (scheinbar bei MySQL generell nicht nach meinen Recherchen).

Hat jemand von euch eine gescheite Idee wie ich das anstellen kann?

Grüße aus Berlin,
Jan

  1. Entweder du löst es auf programmiertechnischer Seite bevor du Query ausführst oder du schreibst nen Trigger.

    http://dev.mysql.com/doc/refman/5.1/de/create-trigger.html

    1. Besten Dank für die Hilfe!

      1. Hallo Forum,

        wie Tom vorgeschlagen hat, habe ich die Aufgabe mit einem Trigger gelöst.
        Verbesserungsvorschläge sind erwünscht!

          
        DROP TABLE IF EXISTS file;  
          
        CREATE TABLE file (  
        	fileid 				INT UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,  
        	filename_upload		varchar(255) NOT NULL COMMENT 'NTFS max. filename length=255 UTF8 chars',  
        	filename_system		varchar(255) NOT NULL COMMENT 'Konflikte mit filenames andere user vermeidenNOW()',  
        	extid				INT UNSIGNED,  
        	frontuseid			INT UNSIGNED,  
        	userid				INT UNSIGNED DEFAULT NULL,  
        	betriebid			INT UNSIGNED DEFAULT NULL,  
        	unternid			INT UNSIGNED DEFAULT NULL,  
        	FOREIGN KEY (userid) REFERENCES user(userid)  
        ) ENGINE = InnoDB;  
          
          
        DROP TRIGGER IF EXISTS files_insert_nur1PK_aus3PK;  
          
        DELIMITER $$  
        CREATE TRIGGER files_insert_nur1PK_aus3PK BEFORE INSERT ON file  
        FOR EACH ROW BEGIN  
        	IF 		(NEW.userid IS NULL AND NEW.betriebid IS NULL AND NEW.unternid IS NULL) OR  
        			(NEW.userid IS NULL AND NEW.betriebid>0 AND NEW.unternid>0) OR  
        			(NEW.userid>0 AND NEW.betriebid IS NULL AND NEW.unternid>0) OR  
        			(NEW.userid>0 AND NEW.betriebid>0 AND NEW.unternid IS NULL) OR  
        			(NEW.userid>0 AND NEW.betriebid>0 AND NEW.unternid>0)  
        		THEN 	  
        			/* Fehlerbedingung=TRUE -> Fehler provozieren um weitere Abarbeitung zu verhindern */  
        			INSERT INTO Es_darf_nur_1_aus_3_ID_FK_insertet_werden VALUES(NULL);  
        	END IF;  
          
        END$$  
        DELIMITER ;  
          
        #Folgendes INSERT muss funktionieren  
        INSERT INTO file VALUES(DEFAULT,"filenameupload.dat","filenameupload.dat",1,1,DEFAULT,8,DEFAULT);  
        #Folgendes INSERT muss einen Fehler provozieren  
        INSERT INTO file VALUES(DEFAULT,"filenameupload.dat","filenameupload.dat",1,1,2,8,9);  
        
        
  2. Hallo,

    MySQL 5.1.41

    CREATE TABLE file (  
    -- [...]  
      
        CHECK (	(userid!=NULL AND betriebid=NULL AND unternid=NULL) OR  
    		(userid=NULL AND betriebid!=NULL AND unternid=NULL) OR  
     		(userid=NULL AND betriebid=NULL AND unternid!=NULL) )  
    ) ENGINE = InnoDB;
    

    [...] check() greift nicht (scheinbar bei MySQL generell nicht nach meinen Recherchen).

    nicht nur scheinbar. Das Verhalten ist im Handbuch dokumentiert:

    <zitat>
        The CHECK clause is parsed but ignored by all storage engines.
    </zitat>

    Das gilt übrigens auch für MySQL 5.5 und 5.6.

    Freundliche Grüße

    Vinzenz

  3. Hello Jan,

    benutze stattdessen eine Stored Routine oder einen Tigger für OnInsert.

    Diese kannst Du dann gezielt aussteigen lassen.

    Damit Du einen Fehlerwert bekommst, der auswertbar ist, müsstest Du beim Trigger (noch) einen schmutzigen Trick anwenden und im Fehlerfalle eine nicht vorhandene Spalte ansprechen.

    Den sauberen Weg mit einer gezielten User-defined Exception kann MySQL mWn immer noch nicht.

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
  4. Mal davon abgesehen dass der CHECK nicht aktiv wird.
    Den Vergleich mit NULL mache ich mit IS NULL, ich weiß nicht was bei = passiert und ob das so richtig funktioniert.

    1. Hallo,

      Mal davon abgesehen dass der CHECK nicht aktiv wird.
      Den Vergleich mit NULL mache ich mit IS NULL, ich weiß nicht was bei = passiert und ob das so richtig funktioniert.

      Nö. Hab' ich übersehen :-(

      mysql> SELECT NULL=NULL AS test;
      +------+
      | test |
      +------+
      | NULL |
      +------+
      1 row in set (0.00 sec)

      mysql> SELECT NULL!=NULL AS test;
      +------+
      | test |
      +------+
      | NULL |
      +------+
      1 row in set (0.00 sec)

      mysql> SELECT ('Test' != NULL) AS test;
      +------+
      | test |
      +------+
      | NULL |
      +------+
      1 row in set (0.00 sec)

      Freundliche Grüße

      Vinzenz

    2. Tach!

      Mal davon abgesehen dass der CHECK nicht aktiv wird.
      Den Vergleich mit NULL mache ich mit IS NULL, ich weiß nicht was bei = passiert und ob das so richtig funktioniert.

      Wann immer NULL als Operand auftritt ist das Ergebnis NULL. Also auch ein NULL mit NULL verglichen ergibt nicht true sondern NULL, was im booleschen Kontext zu false wird. Lediglich die speziell auf NULL ausgerichteten Operatoren (IS NULL etc.) können NULL "richtig" auswerten.

      dedlfix.