Avatar billede dennism Nybegynder
28. januar 2011 - 01:04 Der er 15 kommentarer og
1 løsning

MySQL: UNIQUE KEY

Jeg har følgende tabel:
CREATE TABLE IF NOT EXISTS `articles` (
    `id` int(11) NOT NULL auto_increment,
    `active` tinyint(1) NOT NULL DEFAULT 1,
    `date` DATE NOT NULL,
    PRIMARY KEY  (`id`),
    UNIQUE KEY (`active`,`date`),
);


Ideen med min unique key er, at jeg ikke vil have rækker, der har den samme dato og hvor den er active på samme tid (active=1). Dette fungerer naturligvis fint, men problemet er, at når en række bliver slettet, er jeg ikke interesseret i at fjerne fra tabellen - men bare sætte active=0. Dette betyder så, at hvis jeg forsøger at "slette" en række ved at sætte active=0 på en række, som har samme dato som en anden række der tidligere er blevet "slettet", så kan jeg ikke få lov til at "slette" den.

Er der nogen smart løsning på det?
Avatar billede arne_v Ekspert
28. januar 2011 - 01:47 #1
Jeg tror at du skal droppe UNIQUE INDEX og kigge på TRIGGER i.s.f..
Avatar billede dennism Nybegynder
28. januar 2011 - 02:09 #2
Kan man bruge en TRIGGER til at afvise en INSERT/UPDATE, hvis der i forvejen findes en række med samme date og active=1?

Hvis jeg nemt kan sige hvordan, så må du godt lige fortælle hvordan - har ikke lige umiddelbart kunne finde løsningen vha. Google.
Avatar billede arne_v Ekspert
28. januar 2011 - 02:18 #3
Det burde man kunne.

Jeg kan prøve og se om jeg kan bixe et eksempel.
Avatar billede arne_v Ekspert
28. januar 2011 - 03:14 #4
Eksempel:

mysql> CREATE TABLE fun (
    ->    id INTEGER NOT NULL,
    ->    val INTEGER,
    ->    PRIMARY KEY(id)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql>
mysql> DELIMITER //
mysql>
mysql> CREATE TRIGGER duplcheck
    -> BEFORE INSERT
    -> ON fun
    -> FOR EACH ROW BEGIN
    ->    DECLARE n INT;
    ->    SET n = (SELECT COUNT(*) FROM fun WHERE val = new.val);
    ->    IF(n > 0) THEN
    ->        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Ooops - Houston we have a problem!';
    ->    END IF;
    -> END//
Query OK, 0 rows affected (0.00 sec)

mysql>
mysql> DELIMITER ;
mysql>
mysql> INSERT INTO fun VALUES(1, 1);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO fun VALUES(2, 2);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO fun VALUES(3, 2);
ERROR 1644 (45000): Ooops - Houston we have a problem!
mysql>
mysql> DROP TABLE fun;
Query OK, 0 rows affected (0.02 sec)
Avatar billede arne_v Ekspert
28. januar 2011 - 03:14 #5
Jeg mener at SIGNAL kræver version 5.5 og trigger kræver version 5.0!
Avatar billede dennism Nybegynder
28. januar 2011 - 16:07 #6
Mit webhost kører mySQL 5.0, så det er desværre ikke en løsning. Så er der ikke andet for, end at man må kode sig ud af det - og altså selv tjekke for denne precondition inden. Smider du et svar?
Avatar billede arne_v Ekspert
28. januar 2011 - 16:26 #7
MySQL 5.0 har support for triggers.

Så den grundliggende logik kan bruges i 5.0.

SIGNAL findes ikke i 5.0, så du skal bruge et workaround for at afbryde operationen.

Der er masser af eksmepler omkring dette på nettet.
Avatar billede arne_v Ekspert
28. januar 2011 - 16:31 #8
svar
Avatar billede dennism Nybegynder
28. januar 2011 - 16:35 #9
Jeg sidder og søger, man kan ikke rigtigt finde noget - du må meget gerne smide et link, hvis du ved hvad jeg skal bruge.
Avatar billede dennism Nybegynder
28. januar 2011 - 17:15 #11
Jeg har nu forsøgt mig med følgende:


CREATE TABLE fun (
    id INTEGER NOT NULL auto_increment,
    val INTEGER,
    PRIMARY KEY(id)
);

DELIMITER DROP TRIGGER IF EXISTS duplcheck CREATE TRIGGER duplcheck BEFORE INSERT ON fun FOR EACH ROW BEGIN DECLARE n INT; SET n = (SELECT COUNT(*) FROM fun WHERE val = new.val); IF(n > 0) THEN SET NEW='Error'; END IF; END;

INSERT INTO `fun` ( `val`) VALUES ('1')


Jeg kan dog blive ved med at tilføje rækker med val=1. Det var måske det forkerte jeg fandt?
Avatar billede dennism Nybegynder
28. januar 2011 - 17:16 #12
Læsbar udgave:


CREATE TABLE fun (
    id INTEGER NOT NULL auto_increment,
    val INTEGER,
    PRIMARY KEY(id)
);

DELIMITER DROP TRIGGER IF EXISTS duplcheck
CREATE TRIGGER duplcheck
    BEFORE INSERT ON fun
    FOR EACH ROW BEGIN
        DECLARE n INT;
        SET n = (SELECT COUNT(*) FROM fun WHERE val = new.val);
        IF(n > 0) THEN
            SET NEW='Error';
        END IF;
    END;

INSERT INTO `fun` ( `val`) VALUES ('1');
Avatar billede arne_v Ekspert
28. januar 2011 - 21:49 #13
SET NEW='Error';

giver vel ingen fejl.

Det skal være noget som giver en fejl.
Avatar billede dennism Nybegynder
28. januar 2011 - 22:05 #14
Hvad er det så du specifikt foreslår?
Avatar billede arne_v Ekspert
29. januar 2011 - 00:19 #15
Normalt er det ikke et problem at lave noget som giver fejl.

Første ide:

mysql> CREATE TABLE fun (
    ->    id INTEGER NOT NULL,
    ->    val INTEGER,
    ->    PRIMARY KEY(id)
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql>
mysql> DELIMITER //
mysql>
mysql> CREATE TRIGGER duplcheck
    -> BEFORE INSERT
    -> ON fun
    -> FOR EACH ROW BEGIN
    ->    DECLARE n INT;
    ->    SET n = (SELECT COUNT(*) FROM fun WHERE val = new.val);
    ->    IF(n > 0) THEN
    ->        INSERT INTO fun VALUES('This is not an integer',NULL);  -- this will cause an error
    ->    END IF;
    -> END//
Query OK, 0 rows affected (0.01 sec)

mysql>
mysql> DELIMITER ;
mysql>
mysql> INSERT INTO fun VALUES(1, 1);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO fun VALUES(2, 2);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO fun VALUES(3, 2);
ERROR 1442 (HY000): Can't update table 'fun' in stored function/trigger because
it is already used by statement which invoked this stored function/trigger.
mysql>
mysql> SELECT * FROM fun;
+----+------+
| id | val  |
+----+------+
|  1 |    1 |
|  2 |    2 |
+----+------+
2 rows in set (0.00 sec)

mysql>
mysql> DROP TABLE fun;
Query OK, 0 rows affected (0.02 sec)
Avatar billede dennism Nybegynder
29. januar 2011 - 10:59 #16
Tak for hjælpen :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester