2010-12-17 13 views
6

Moja tabela ma dwie kolumny: "id" (Auto Increment, Primary) i "number" (Unique). Teraz chcę:MySQL - Jeśli istnieje, uzyskaj klucz podstawowy. Inaczej dodaj wpis

  • jeśli liczba już istnieje, zwróć identyfikator;
  • W przeciwnym razie dodaj wpis do tabeli i zwróć jej identyfikator.

Jaka jest najskuteczniejsza metoda wykonania tej pracy?

Uwaga:

  • Istnieje duże prawdopodobieństwo, że liczba jest nowy;
  • Tabela będzie zawierać setki tysięcy rekordów.

Dziękujemy!

+1

najlepszym sposobem, jaki mogę wymyślić jest procedura składowana, nie mogę podać przykładu w tej chwili, ale spójrz na to –

Odpowiedz

6
INSERT IGNORE INTO table (number) VALUES (42); 

SELECT id FROM table WHERE number = 42; 

To prawdopodobnie najbardziej wydajny w MySQL. Możesz użyć procedury składowanej, aby je zgrupować, co może, ale nie musi być nieco bardziej wydajne.

EDIT:

Jeśli uważasz, że będzie to rzadkie, że nowe numery wymyślić, to będzie jeszcze szybciej:

SELECT id FROM table WHERE number = 42; 

if (!id) { 

    INSERT INTO table WHERE number = 42; 
    id = SELECT @LAST_INSERT_ID; 

} 

Jest możliwa sytuacja wyścigu tutaj jeśli współbieżnych wątków jednocześnie zaznaczyć następnie wstaw ten sam numer w tym samym czasie. W takim przypadku późniejsza wkładka nie powiedzie się. Możesz odzyskać od tego, ponownie wybierając ten warunek błędu.

+0

Mam zaktualizowane moje pytanie. W tabeli znajdowałyby się setki tysięcy rekordów, a prawdopodobieństwo uzyskania nowego numeru byłoby większe. Jaką różnicę tworzy procedura składowana dla wielu rekordów? – Saad

+0

W tym przypadku zrobiłbym wkładkę, a następnie wybierz, jest to prostsze i nie ma warunków wyścigu. Zakładając, że serwer bazy danych znajduje się w pobliżu serwera WWW, procedura składowana niewiele zmieni. Można to łatwo przetestować, pisząc obie ścieżki kodu, a następnie testując je pod kątem stresu. – Chris

4

Oto jeden z takich funkcji składowanej robi to, co można opisać:

CREATE FUNCTION `spNextNumber`(pNumber int) RETURNS int(11) 
BEGIN 
DECLARE returnValue int; 
SET returnValue := (SELECT Number FROM Tbl WHERE Number = pNumber LIMIT 1); 
IF returnValue IS NULL THEN 
    INSERT IGNORE INTO Tbl (Number) VALUES (pNumber); 
    SET returnValue := pNumber; -- LAST_INSERT_ID() can give you the real, surrogate key 
END IF; 
RETURN returnValue; 
END 
+0

Jak korzystać z tego: WSTAW IGNORE INTO People (Number) VALUES (pNumber); SET returnValue: = (WYBIERZ liczbę od osób WHERE Number = pNumber LIMIT 1); Myślę, że byłby bardziej wydajny? – Saad

+0

to będzie zależeć od częstotliwości duplikatów, jak wspomina Chris powyżej ... próba niepotrzebnego wstawienia i złapanie z IGNORE będzie prawdopodobnie bardziej kosztowne niż wybór ... limitu 1. Jednak do jego punktu o wyścigu warunek Dodałem IGNORE do wkładki w tej odpowiedzi. – Tahbaza

+0

Dzięki za pomoc Tahbaza! Poprawiłem moje pytanie i dodałem trochę więcej szczegółów, czy możesz potwierdzić, czy twoje rozwiązanie jest nadal najlepsze, czy chciałbyś je poprawić? – Saad

1

wiem, że to jest stary, ale jest to powszechny problem. Tak więc, dla dobra każdego, kto szuka rozwiązania, istnieją 4 różne sposoby wykonania tego zadania za pomocą testów wydajności. http://mikefenwick.com/blog/insert-into-database-or-return-id-of-duplicate-row-in-mysql/.

+2

Podczas gdy ten link może odpowiedzieć na pytanie, lepiej umieścić tutaj istotne części odpowiedzi i podać link do odsyłacza. Odpowiedzi dotyczące linków mogą stać się nieprawidłowe, jeśli strona z linkami się zmieni. –

+1

https://web.archive.org/web/20150915085727/http://mikefenwick.com:80/blog/insert-into-database-or-return-id-of-duplicate-row-in-mysql/ – dt192

Powiązane problemy