2013-08-08 24 views
15

Scenariusz:Definiowanie Composite Klucz z automatycznego przyrostu w MySQL

Mam tabeli, która odwołuje się dwa klucze obce, a dla każdej unikalnej kombinacji tych kluczy obcych, posiada własną kolumnę auto_increment. Trzeba zaimplementować kompozytowy klucz, który pomoże zidentyfikować wiersz jako unikalny z użyciem kombinacji tych trzech (jeden dla kluczy obcych i kolumny auto_increment i jedną drugą kolumnę nieunikalny wartości)

Tabela:

CREATE TABLE `issue_log` (
`sr_no` INT NOT NULL AUTO_INCREMENT , 
    `app_id` INT NOT NULL , 
    `test_id` INT NOT NULL , 
    `issue_name` VARCHAR(255) NOT NULL , 
primary key (app_id, test_id,sr_no) 
); 

oczywiście, nie musi być coś nie tak z moim zapytaniu, z powodu której błąd wrzucony jest:

eRROR 1075: Niepoprawna definicja tabeli; może być tylko jedno auto kolumna i musi być zdefiniowany jako kluczowy

Co usiłuję osiągnąć:

Mam tabela Application (z APP_ID jako jej klucz podstawowy), każdy Aplikacja ma zestaw problemów do rozwiązania, a każda aplikacja ma wiele testów (więc test_id col) Sr_no col powinien wzrosnąć dla unikalnych app_id i test_id.

tj Dane w tabeli powinna wyglądać następująco:

silnik

enter image description here

Baza danych jest InnoDB. Chcę to osiągnąć z jak największą prostotą (tj. Unikać wyzwalaczy/procedur, jeśli to możliwe - co sugerowano w podobnych przypadkach w innych pytaniach).

Odpowiedz

18

Nie możesz pozwolić, aby MySQL wykonał to automatycznie dla tabel InnoDB - musisz użyć wyzwalacza lub procedury lub użytkownik inny silnik bazy danych, taki jak MyISAM. Automatyczny inkrementację można wykonać tylko dla jednego klucza podstawowego.

coś jak następuje powinien działać

DELIMITER $$ 

CREATE TRIGGER xxx BEFORE INSERT ON issue_log 
FOR EACH ROW BEGIN 
    SET NEW.sr_no = (
     SELECT IFNULL(MAX(sr_no), 0) + 1 
     FROM issue_log 
     WHERE app_id = NEW.app_id 
     AND test_id = NEW.test_id 
    ); 
END $$ 

DELIMITER ; 
+0

OK i jakie zmiany należy wprowadzić w zapytaniu Utwórz tabelę? Nie mogę uczynić sr_no kluczem podstawowym, ponieważ nie będzie wyjątkowy. I nie mogę zrobić Autoinkrementacji bez uczynienia go Kluczem Podstawowym ... –

+0

Po prostu wyłącz autoinkrementację - nie może zrobić tego, co chcesz. Klucz podstawowy pozostaje taki sam. – noz

+0

Przykro mi, że nie pracowałem z wyzwalaczami, a ten kod wyzwalający wyrzuca "Błąd składni" - nic więcej nie zdefiniowano. Czy możesz oczyścić kod, a kiedy już to zrobisz, wstaw tutaj komentarz? Błąd: BŁĄD 1064: Wystąpił błąd w składni SQL; sprawdź instrukcję, która odpowiada twojej wersji serwera MySQL dla właściwej składni do użycia w pobliżu '.sr_no = (wybierz ifnull (wybierz max (sr_no) +1 z issue_log gdzie app_id = new.app_id' w linii 3 –

3

Można użyć unique klucza kompozytowe do sr_no, app_id & test_id. Nie można używać przyrostowego w sr_no, ponieważ nie jest to unikatowe.

CREATE TABLE IF NOT EXISTS `issue_log` (
    `sr_no` int(11) NOT NULL, 
    `app_id` int(11) NOT NULL, 
    `test_id` int(11) NOT NULL, 
    `issue_name` varchar(255) NOT NULL, 
    UNIQUE KEY `app_id` (`app_id`,`test_id`,`sr_no`) 
) ENGINE=InnoDB ; 

Mam wykomentowane unikatu w sql fiddle wykazać (usunąć # w wierszu 22 schematu i odbudować schemat)

6

Można to zrobić z MyISAM i silników BDB. InnoDB tego nie obsługuje. Cytat z podręcznika MySQL 5.0.

W przypadku tabel MyISAM i BDB można określić AUTO_INCREMENT w drugiej kolumnie w indeksie z wieloma kolumnami. W tym przypadku wygenerowana wartość dla kolumny AUTO_INCREMENT jest obliczana jako MAX (auto_increment_column) + 1 WHERE prefix = given-prefix.

http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html

+0

Dla twoich informacji, cytowanie odpowiednich informacji wraz z linkiem jest zawsze preferowane w przypadku przepełnienia stosu, odpowiedzi mogą zostać usunięte tylko w celu skierowania pytania pytającego do linku. – OGHaza

3

nie w pełni zrozumieć swoje wymagania przyrost na kolumnie test_id, ale jeśli chcesz ~ sekwencję autoIncrement restartujący na każdej unikalnej kombinacji (app_id, test_id), można zrobić INSERT ... SELECT z tej samej tabeli, tak jak poniżej:

mysql> INSERT INTO `issue_log` (`sr_no`, `app_id`, `test_id`, `issue_name`) SELECT 
      IFNULL(MAX(`sr_no`), 0) + 1 /* next sequence number */, 
      3 /* desired app_id */, 
      1 /* desired test_id */, 
      'Name of new row' 
      FROM `issue_log` /* specify the table name as well */ 
     WHERE `app_id` = 3 AND `test_id` = 1 /* same values as in inserted columns */ 

ten zakłada definicji tabeli bez zadeklarowanej kolumnie AUTO_INCREMENT. Zasadniczo emulujesz zachowanie autorelementacji za pomocą klauzuli IFNULL (MAX()) + 1, ale emulacja ręczna działa na dowolnych kolumnach, w przeciwieństwie do wbudowanej autoinkrementacji.

Należy pamiętać, że INSERT ... SELECT jako pojedyncze zapytanie zapewnia atomowość operacji. InnoDB zablokuje odpowiedni indeks, a wiele współbieżnych procesów może wykonać tego rodzaju zapytanie, wciąż tworząc sekwencje nie będące w konflikcie.

+0

Projekt się skończył. Miejmy nadzieję, że pomoże to komuś, kto się potknął. Nie jestem wtedy pewien co do mojego pytania: P –

+0

Wstaw-wybierz niekoniecznie atomowy. To zależy od ustawień blokowania/poziomu izolacji. Zobacz http://stackoverflow.com/questions/21438033/making-an-insert-select-statemento-atomic, lub http://dba.stackexchange.com/questions/73540/mysql-consistent-nonlocking-reads-vs- wstaw-wybierz. – HardlyKnowEm