2011-12-07 10 views
12

Mam tabelę z unikatowego indeksu na dwóch kolumnach, id_parent i porządek_sortowania być precyzyjneMysql tymczasowo stłumić unikatowy indeks

+----+-----------+------------+-------------+-------------+-------------+ 
| id | id_parent | sort_order | some_data | other_data | more_data | 
+----+-----------+------------+-------------+-------------+-------------+ 
| 1 |   1 |   1 | lorem ipsum | lorem ipsum | lorem ipsum | 
| 2 |   1 |   2 | lorem ipsum | lorem ipsum | lorem ipsum | 
| 3 |   1 |   3 | lorem ipsum | lorem ipsum | lorem ipsum | 
+----+-----------+------------+-------------+-------------+-------------+ 

Teraz chcę zaktualizować je do swoich danych oraz ich porządek_sortowania w jednej podróży. sort_order zmieni się z 1 - 2 - 3 na, na przykład 2 - 3 - 1.

Ale kiedy zaczynam wykonywać instrukcje aktualizacji, unikalny indeks blokuje mnie, zgodnie z oczekiwaniami, mówiąc, że nie mogę mieć dwóch wierszy z id_parent = 1 and sort_order = 2. Mogę teraz ustawić 4, zaktualizować inne wiersze w poprawnej kolejności, a następnie ustawić to. Ale wtedy musiałbym wygenerować dodatkową instrukcję i najprawdopodobniej dodać dodatkową logikę do mojego języka skryptowego, aby ustalić poprawną kolejność aktualizacji. Używam również ORM, a staje się jeszcze bardziej niewrażliwy.

Moje pytanie brzmi: czy istnieje sposób, aby mysql tymczasowo zignorował ten indeks? Jak rozpoczęcie specjalnej transakcji, w której indeksy byłyby obliczane tylko tuż przed jej zatwierdzeniem?

+0

Czy Twoje tabele InnoDB lub MyISAM? – Pacerier

Odpowiedz

6

O ile wiem, nie jest to możliwe.

Jedyny raz widziałem coś takiego, że można wyłączyć nie unikatowe klucze na tabelach myisam. Ale nie na InnoDB, a nie na unikalnych klawiszach.

Jednak, aby zaoszczędzić Ci aktualizację lub dwie, nie ma potrzeby posiadania dokładnej liczby 1, 2 i 3. Możesz równie dobrze mieć 4, 5 i 6. Dobrze? Używałbyś go w kolejności według i nic innego, więc dokładne liczby nie są ważne. Zaoszczędzi ci nawet aktualizację, jeśli jesteś sprytny. Z Twojego przykładu:

update table set sort_order = 4 where sort_order = 1 and id = 1 and id_parent = 1; 

Nowa kolejność sortowania to 2, 3, 1. I tylko w jednej aktualizacji.

+1

Wstyd mi, używając 4-5-6 był tak oblisty. – Nameless

+0

Zgadzam się, że jest to niemożliwe przy wielu aktualizacjach. Jednak aktualizacja ich w pojedynczej instrukcji może działać. Tak czy inaczej, to +1 dla rozwiązania, które jest jasne i proste. –

4

„Ale kiedy zaczną Aktualizacja oświadczenia ...” - Rozumiem próbowałeś aktualizację wartości przy użyciu wielu instrukcji UPDATE, jak w pętli. Czy tak jest? A może zaktualizujesz je za jednym razem? Tak, na przykład:

UPDATE atable 
SET sort_order = CASE sort_order WHEN 3 THEN 1 ELSE sort_order + 1 END 
WHERE id_parent = 1 
    AND sort_order BETWEEN 1 AND 3 

Pojedyncza stwierdzenie jest atomowy, tak, do czasu ta aktualizacja końcach wartości sort_order, chociaż zmieniło, pozostanie wyjątkowy.

Nie mogę tego przetestować w MySQL, przepraszam, ale na pewno działa na serwerze SQL i uważam, że zachowanie to respektuje standardy.

+0

Nie pomyślałem o tym. Byłby jednak trudny do użycia z ORM. Jest to bliższe poprawnej odpowiedzi na określone pytanie, a następnie odpowiedź Andreasa, ale prawdopodobnie skończę z jego poradą. Co powinienem zrobić z wybraną odpowiedzią w tym przypadku? Daj mu odpowiedź, której zamierzam użyć, lub odpowiedzieć na pytanie, który temat jest bliższy? – Nameless

+0

Cóż, w przypadku, gdy więcej niż jedna odpowiedź pasuje do * pytania *, prawdopodobnie powinieneś pójść z tą, którą zamierzasz zastosować, aby rozwiązać * rzeczywisty problem *. Więc to pewnie odpowiedź Andreasa, którą powinieneś wybrać. Mogą jednak istnieć inne względy. W każdym razie nie mam nic przeciwko twojej decyzji! –

+3

To nie zadziała, dopóki wiersze nie zostaną zaktualizowane we właściwej kolejności. Brak czasu podczas wykonywania może spowodować naruszenie klucza unikalnego. 'update sorttest set sort = sort + 1-porządku według sortowania;' nie zadziała bez sortowania malejącego, pierwszy wiersz naruszyłby klucz, nawet jeśli drugi wiersz zostanie przeniesiony poza miejsce. –

0

Uwaga: Niektóre komentarze wspomnieli, że odpowiedź poniżej nie zawsze tłumią unikalnych ograniczeń, szczególnie w InnoDB. Jeśli możesz poprawić tę odpowiedź, kliknij "Edytuj" i wprowadź ulepszenia.

Można po prostu dodać tę linię na początku skryptu:

SET UNIQUE_CHECKS=0; 

To wspólne użyć to w połączeniu z:

SET FOREIGN_KEY_CHECKS=0; 

Zmienna UNIQUE_CHECKS wspomniano w docs tutaj:
http://dev.mysql.com/doc/refman/5.0/en/converting-tables-to-innodb.html

+2

Próbowałem tego na 5.6, NIE działa z InnoDB. Zduplikowany klucz nadal zgłaszał błąd. – bluecollarcoder

+0

@bluecollarcoder Czy próbowałeś ustawić * oba * UNIQUE_CHECKS' i 'FOREIGN_KEY_CHECKS' na zero? Czy jesteś pewien, że nie powielasz klucza podstawowego *, który prawdopodobnie nadal jest niedozwolony? –

+1

Unikalny klucz znajduje się w kolumnie, która nie odwołuje się do żadnej innej tabeli, więc "FOREIGN_KEY_CHECKS" jest tutaj nieistotne. A kolumna z unikalnym kluczem zdecydowanie nie jest częścią klucza podstawowego. W jakiej sytuacji udało się uzyskać działanie "UNIQUE_CHECKS = 0", aby działało zgodnie z oczekiwaniami? – bluecollarcoder

Powiązane problemy