Aktualizacja zduplikowanego klucza taka sama jak przy wstawianiu

158

Szukałem w okolicy, ale nie znalazłem, czy to możliwe.

Mam takie zapytanie MySQL:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

Identyfikator pola ma „unikalny indeks”, więc nie może być ich dwóch. Teraz, jeśli ten sam identyfikator jest już obecny w bazie danych, chciałbym go zaktualizować. Ale czy naprawdę muszę ponownie określić wszystkie te pola, na przykład:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

Lub:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

Podałem wszystko już we wkładce ...

Dodatkowa uwaga, chciałbym skorzystać z obejścia, aby uzyskać identyfikator!

id=LAST_INSERT_ID(id)

Mam nadzieję, że ktoś może mi powiedzieć, jaki jest najbardziej skuteczny sposób.

Roy
źródło
9
AFAIK, metoda przekształcania każdej kolumny, a=VALUES(a), b=VALUES(b), ...jest drogą, którą musisz iść. Tak to robię dla wszystkich moich INSERT ON DUPLICATE KEY UPDATEwypowiedzi.
Valdogg21
4
Gwoli wyjaśnienia, wszystko, co tu podano, jest poprawne, o ile rozumiesz, że odpowiedź na pytanie brzmi: czy musisz powtórzyć każdą kolumnę, którą chcesz zaktualizować? Tak, jeśli chcesz wstawić lub zaktualizować 8 kolumn w 25-kolumnowej tabeli, musisz podać 8 kolumn dwa razy - raz w części wstawiania i raz w części aktualizacyjnej. (Możesz pominąć klucz podstawowy w części dotyczącej aktualizacji, ale najłatwiej jest wstępnie sformatować ciąg „a = 1, b = 2, c = 3” i osadzić go dwukrotnie). Jednak NIE musisz przekształcać pozostałych 17 kolumn nie chcesz się zmieniać. Jeśli stanie się UPDATE, zachowają swoje istniejące wartości.
Timberline,
3
Dodałem ten komentarz, ponieważ pytania i odpowiedzi pozostawiły mnie niepewnym co do niewymienionych kolumn z tego rodzaju INSERTem, które następnie przetestowałem po tym, jak dokładnie dowiedziałem się, co przetestować. INSERT ON DUPLICATE KEY UPDATE pozostawia niewymienione kolumny bez zmian w UPDATE, ale nadaje im domyślne wartości w przypadku INSERT. Z REPLACE INTO niewymienione kolumny zawsze otrzymują wartości domyślne, nigdy nie istniejące.
Timberline,
1
Sprawdź stackoverflow.com/questions/2472229/… , myślę, że jest to czego szukasz
Chococroc

Odpowiedzi:

82

UPDATEOświadczenie podane tak, że starsze pola mogą zostać zaktualizowane do nowej wartości. Jeśli Twoje starsze wartości są takie same, jak nowe, po co w ogóle miałbyś je aktualizować?

Np. jeśli kolumny ado gjuż ustawiony 2do 8; nie byłoby potrzeby ponownej aktualizacji.

Alternatywnie możesz użyć:

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

Aby uzyskać idod LAST_INSERT_ID; musisz określić aplikację zaplecza, której używasz do tego samego.

W przypadku LuaSQL a conn:getlastautoid()pobiera wartość.

hjpotter92
źródło
6
To tylko przykład. W zapytaniach z życia wziętych istnieją różnice. Moje pytanie jest bardzo proste: czy naprawdę muszę określić wszystkie pola (i wartości w moim pierwszym przykładzie), jeśli są takie same jak we wkładce? Chcę tylko wstawić wszystko lub jeśli istnieje unikalne dopasowanie wartości: zaktualizuj wszystko.
Roy
1
„Dodatkowa uwaga, chciałbym również skorzystać z obejścia, aby uzyskać identyfikator!”
Agamemnus,
1
@Tresdin, O ile wiem, to tylko cukier syntaktyczny. Osobiście nigdy nie widziałem nikogo używającego a = WARTOŚCI (a), b = WARTOŚCI (b) itd. Może możesz przypisać wiele wartości do kolumny? Nie jestem jednak pewien, dlaczego nie miałbyś po prostu wcześniej połączyć danych.
DuckPuncher
54
Jeśli tabela tblma już wiersz (1,10), to: INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)ustawi a = 2 . Podczas gdy INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=austawi a = 10
Bùi Việt Thành
2
Po co wszystkie głosy za? To nie działa. Jak zauważył @ Bùi Việt Thành, a=anic nie aktualizuje. (Zapytania w pierwotnym pytaniu również niczego nie aktualizują, ale wydaje się, że aktualizacja jest tym, co zamierzał OP.)
Roger Dueck
41

Istnieje rozszerzenie SQL specyficzne dla MySQL, które może być tym, czego chcesz - REPLACE INTO

Jednak nie działa to tak samo, jak „PRZY DUPLIKOWANEJ AKTUALIZACJI”

  1. Usuwa stary wiersz, który koliduje z nowym wierszem, a następnie wstawia nowy wiersz. Dopóki nie masz klucza podstawowego w tabeli, byłoby dobrze, ale jeśli tak, to jeśli jakakolwiek inna tabela odwołuje się do tego klucza podstawowego

  2. Nie możesz odwoływać się do wartości w starych wierszach, więc nie możesz zrobić odpowiednika

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

Chciałbym skorzystać z obejścia, aby uzyskać identyfikator!

To powinno działać - last_insert_id () powinno mieć poprawną wartość, o ile klucz podstawowy jest automatycznie zwiększany.

Jednak, jak powiedziałem, jeśli faktycznie używasz tego klucza podstawowego w innych tabelach, REPLACE INTOprawdopodobnie nie będzie to dla ciebie akceptowalne, ponieważ usuwa stary wiersz, który zderzył się za pomocą unikalnego klucza.

Ktoś inny zasugerował, zanim będziesz mógł ograniczyć pisanie, wykonując:

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
Danack
źródło
Więc nie mogę trzymać się klucza podstawowego przed replace intozapytaniem?
Roy
Nie - ten wiersz zostanie usunięty i utworzony zostanie nowy wiersz, który będzie miał nowy klucz podstawowy.
Danack,
czy nie spowolniłoby to procesu w przypadku dużych zbiorów danych, takich jak importowanie plików CSV zawierających 100 KB lub więcej wierszy?
Muhammad Omer Aslam
24

Nie ma innego wyjścia, wszystko muszę określić dwukrotnie. Pierwsza w przypadku wkładki, druga w przypadku aktualizacji.

Roy
źródło
9
To jest niepoprawne i nie powinno być akceptowaną odpowiedzią. Odpowiedź od @Danack jest poprawna.
Wayne Workman
2
Powinieneś ponownie przeczytać pytanie @WayneWorkman, to jest poprawna odpowiedź.
Roy,
24

Oto rozwiązanie Twojego problemu:

Próbowałem rozwiązać problem taki jak twój i chcę zasugerować przetestowanie od prostego aspektu.

Wykonaj następujące kroki: Ucz się z prostego rozwiązania.

Krok 1: Utwórz schemat tabeli za pomocą tego zapytania SQL:

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

Tabela <code> user </code> z danymi

Krok 2: Utwórz indeks dwóch kolumn, aby zapobiec powielaniu danych, używając następującego zapytania SQL:

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

lub Utwórz indeks dwóch kolumn z GUI w następujący sposób: Utwórz indeks z GUI Wybierz kolumny, aby utworzyć indeks Indeksy tabeli <code> user </code>

Krok 3: Zaktualizuj, jeśli istnieje, wstaw, jeśli nie używasz następujących zapytań:

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

Tabela <code> user </code> po uruchomieniu powyższego zapytania


źródło
1
Dziękuję za tę solucję - zrozumiałem, bardzo doceniam, dziękuję!
Michael Nilsson
FYI przechowywanie haseł w postaci zwykłego tekstu jest okropnym pomysłem, podobnie jak próba zapobieżenia powielaniu haseł na poziomie bazy danych.
OrangeDog
10

Na wypadek, gdybyś był w stanie wykorzystać język skryptowy do przygotowania zapytań SQL, możesz ponownie użyć par pole = wartość, używając SETzamiast (a,b,c) VALUES(a,b,c).

Przykład z PHP:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

Przykładowa tabela:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Chris
źródło
4
Powinieneś uciekać przed wartościami lub wykonywać przygotowaną instrukcję ze swoimi wartościami.
Chinoto Vokro,
1
@ChinotoVokro - zgadzam się. To jest tylko przedstawienie „jak byś mógł” i nie używa nawet żadnych funkcji zapytań w tym przykładzie.
Chris
1
Jest to dobra alternatywa dla określenia tablicy kluczy i tablicy wartości. Dzięki.
Mark Carpenter Jr
5

Można rozważyć użycie REPLACE INTOskładni, ale ostrzegam, po duplikat klucza podstawowego / wyjątkowy, to usuwa wiersz i WKŁADKI nowy.

Nie musisz ponownie określać wszystkich pól. Należy jednak wziąć pod uwagę możliwe zmniejszenie wydajności (w zależności od projektu tabeli).

Ostrzeżenia:

  • Jeśli masz klucz podstawowy AUTO_INCREMENT, otrzymasz nowy
  • Indeksy prawdopodobnie będą wymagały aktualizacji
Matej
źródło
występuje naruszenie ograniczenia, jeśli indeks jest również kluczem obcym dla innej tabeli, jest wyzwalany przez usunięcie części.
user3290180
4

Wiem, że jest późno, ale mam nadzieję, że komuś pomoże ta odpowiedź

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

Możesz przeczytać samouczek poniżej:

https://mariadb.com/kb/en/library/insert-on-duplicate-key-update/

http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/

Galang Piliang
źródło
Dobra odpowiedź. Jednak używanie VALUES () do odwoływania się do nowych wierszy jest przestarzałe od wersji MySQL 8.0.20 . Nowe podejście, opisane szczegółowo w linku, polega na użyciu aliasu dla wiersza. W tym przykładzie alias to new:INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473
@ user697473 Otrzymuję błąd: „Masz błąd w składni SQL; sprawdź w podręczniku, który odpowiada Twojej wersji serwera MariaDB, prawidłową składnię do użycia w pobliżu 'AS new ON DUPLICATE KEY UPDATE a = new.a'” , a moje zapytanie działało (z wyjątkiem sytuacji, gdy oczywiście wystąpiły zduplikowane klucze), zanim zaimplementowałem tę klauzulę. Jakieś pomysły?
aaron
@aaron - przepraszam, brak świetnych pomysłów. MySQL 8.0.20 został właśnie wydany pod koniec kwietnia; być może MariaDB jeszcze nie nadrobiła zaległości. To może wyjaśniać, dlaczego powoduje to błąd.
user697473
@ user697473 Tak, właśnie wypróbowałem metodę a = VALUES (a) w oryginalnej odpowiedzi i zadziałała, mimo wszystko dzięki.
aaron
-7

możesz użyć wstawiania ignorowania w takim przypadku, zignoruje, jeśli otrzyma zduplikowane rekordy INSERT IGNORE ...; - bez NA DUPLIKATU KLUCZA

pitu
źródło
Chciałem go wstawić, gdy go nie ma, w przeciwnym razie zaktualizuj. Nie ignoruj ​​tego.
Roy,
dlaczego chcesz go aktualizować, jeśli aktualizujesz z poprzednią / tą samą wartością, jeśli jest to nowa wartość, to jest z nią w porządku, jeśli nie wstawiaj ignoruj ​​pracę dla tego
pitu
2
Najprawdopodobniej dlatego, że wartości mogą się zmienić, a on chce utworzyć rekord, jeśli taki nie istnieje, ale zaktualizować istniejący rekord, jeśli robi (ze zmianami) bez narzutu w obie strony z powrotem do aplikacji, a następnie z powrotem do bazy danych jeszcze raz.
mopsyd