Jak "wstawić, jeśli nie istnieje" w MySQL?
Zacząłem od googlowania i znalazłem ten Artykuł , który mówi o tablicach mutex.
Mam tabelę z ~14 milionami rekordów. Jeśli chcę dodać więcej danych w tym samym formacie, czy istnieje sposób, aby upewnić się, że rekord, który chcę wstawić, nie istnieje już bez użycia pary zapytań(tj. jedno zapytanie do sprawdzenia i jedno do wstawienia jest puste)?
Czy ograniczenie unique
Na polu gwarantuje, że insert
nie powiedzie się, jeśli jest już tam?
Wygląda na to, że z jedynie ograniczeniem, kiedy wystawiam insert przez php, skrypt wykrzywia się.
10 answers
Użyj INSERT IGNORE INTO table
Zobacz http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html
Istnieje również INSERT … ON DUPLICATE KEY UPDATE
składnia, można znaleźć wyjaśnienia na dev.mysql.com
Post z bogdan.org.ua według webcache Google:
18 października 2007
Na początek: od najnowszego MySQL, składnia przedstawiona w tytule NIE JEST możliwe. Ale istnieje kilka bardzo łatwych sposobów, aby osiągnąć to, co na oczekiwane przy użyciu istniejących funkcjonalności.
Istnieją 3 możliwe rozwiązania: użycie INSERT IGNORE, REPLACE lub WSTAW ... PRZY AKTUALIZACJI DUPLIKATU KLUCZA.
Wyobraź sobie, że mamy tabelę:
CREATE TABLE `transcripts` ( `ensembl_transcript_id` varchar(20) NOT NULL, `transcript_chrom_start` int(10) unsigned NOT NULL, `transcript_chrom_end` int(10) unsigned NOT NULL, PRIMARY KEY (`ensembl_transcript_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Teraz wyobraź sobie, że mamy automatyczny rurociąg importujący transkrypty meta-danych z Ensembl, i że z różnych powodów rurociąg może zostać złamana na każdym etapie egzekucji. Dlatego musimy zapewnić dwa rzeczy:
Powtarzające się egzekucje rurociąg nie zniszczy naszych baza danych
Powtarzające się egzekucje nie zginą z powodu " duplikatu błędy klucza podstawowego.
Metoda 1: Użycie REPLACE
To bardzo proste:
REPLACE INTO `transcripts` SET `ensembl_transcript_id` = 'ENSORGT00000000001', `transcript_chrom_start` = 12345, `transcript_chrom_end` = 12678;
Jeśli rekord istnieje, zostanie nadpisany; jeśli jeszcze nie istnieje, zostanie stworzony. Jednak stosowanie tej metody nie jest skuteczne w naszym przypadku: nie musimy nadpisywać istniejących rekordów, jest dobrze żeby je pominąć.
Metoda 2: użycie INSERT IGNORE również bardzo proste:
INSERT IGNORE INTO `transcripts` SET `ensembl_transcript_id` = 'ENSORGT00000000001', `transcript_chrom_start` = 12345, `transcript_chrom_end` = 12678;
Tutaj, jeśli 'ensembl_transcript_id' jest już obecny w baza danych, zostanie po cichu pominięta (zignorowana). (Dokładniej, oto cytat z podręcznika MySQL reference: "jeśli używasz ignoruj słowo kluczowe, błędy, które występują podczas wykonywania instrukcji INSERT to traktowane jako ostrzeżenia. Na przykład, bez ignorowania, wiersz, który duplikuje istniejący unikalny indeks lub wartość klucza głównego w tabela powoduje błąd duplicate-key i polecenie jest przerywane.".) Jeśli rekord jeszcze nie istnieje, zostanie utworzony.
[[6]} ta druga metoda ma kilka potencjalnych słabości, w tym nie-aborcji zapytania w przypadku wystąpienia jakiegokolwiek innego problemu (patrz manual). Dlatego powinien być stosowany, jeśli wcześniej przetestowany bez Ignoruj słowo kluczowe.Metoda 3: użycie INSERT ... przy aktualizacji duplikatu klucza:
Trzecią opcją jest użycie
INSERT … ON DUPLICATE KEY UPDATE
składni, a w część aktualizacji po prostu nie rób nic nie ma znaczenia (pusty) operacja, jak obliczanie 0+0 (Geoffray sugeruje wykonanie id = id przypisanie dla silnika optymalizacji MySQL zignorować to operacji). Zaletą tej metody jest to, że ignoruje ona tylko duplikaty kluczowe zdarzenia, i nadal przerywa na inne błędy.Jako ostatnia uwaga: ten post został zainspirowany przez Xaprb. Radziłbym też zapoznaj się z innym postem na temat pisania elastycznych zapytań SQL.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-21 06:53:53
Rozwiązanie:
INSERT INTO `table` (`value1`, `value2`)
SELECT 'stuff for value1', 'stuff for value2' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM `table`
WHERE `value1`='stuff for value1' AND `value2`='stuff for value2' LIMIT 1)
Explanation:
Najskrytsze zapytanie
SELECT * FROM `table`
WHERE `value1`='stuff for value1' AND `value2`='stuff for value2' LIMIT 1
Używany jako WHERE NOT EXISTS
-warunek wykrywa, czy istnieje już wiersz z danymi do wstawienia. Po znalezieniu jednego wiersza tego rodzaju, zapytanie może zostać zatrzymane, stąd LIMIT 1
(Mikro-optymalizacja, może zostać pominięta).
Zapytanie pośrednie
SELECT 'stuff for value1', 'stuff for value2' FROM DUAL
Przedstawia wartości, które mają być wstawione. DUAL
odnosi się do specjalnego jednego wiersza, jednej kolumny tabeli obecny przez domyślne we wszystkich bazach danych Oracle (zobacz https://en.wikipedia.org/wiki/DUAL_table ). na serwerze MySQL w wersji 5.7.26 otrzymałem poprawne zapytanie przy pominięciu FROM DUAL
, ale starsze wersje (jak 5.5.60) wydają się wymagać informacji FROM
. Za pomocą WHERE NOT EXISTS
zapytanie pośrednie zwraca pusty zestaw wyników, jeśli najbardziej wewnętrzne zapytanie znalazło pasujące dane.
Zapytanie zewnętrzne
INSERT INTO `table` (`value1`, `value2`)
Wstawia dane, jeśli jakiekolwiek są zwracane przez zapytanie pośrednie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-22 13:57:28
Przy duplicate key update , lub insert ignore mogą być realne rozwiązania z MySQL.
Przykład na duplikat aktualizacji klucza Aktualizacja na podstawie mysql.com
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE table SET c=c+1 WHERE a=1;
Przykład insert ignore na podstawie mysql.com
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name [(col_name,...)]
{VALUES | VALUE} ({expr | DEFAULT},...),(...),...
[ ON DUPLICATE KEY UPDATE
col_name=expr
[, col_name=expr] ... ]
Lub:
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name
SET col_name={expr | DEFAULT}, ...
[ ON DUPLICATE KEY UPDATE
col_name=expr
[, col_name=expr] ... ]
Lub:
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name [(col_name,...)]
SELECT ...
[ ON DUPLICATE KEY UPDATE
col_name=expr
[, col_name=expr] ... ]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-16 17:36:16
Każde proste ograniczenie powinno wykonać zadanie, jeśli wyjątek jest akceptowalny. Przykłady:
- Klucz podstawowy, jeśli nie zastępczy
- unikalne ograniczenie na kolumnie
- multi-column unique constraint
Przepraszam, to wydaje się zwodniczo proste. Wiem, że źle to wygląda z linkiem, którym się z nami dzielisz. ;-(
Ale nigdy nie udzielam tej odpowiedzi, ponieważ wydaje się ona zaspokajać Twoje potrzeby. (Jeśli nie, może to spowodować aktualizację wymagań, które byłyby " a Good Thing " (TM) also).
Edited: jeśli insert złamie unikalne ograniczenie bazy danych, wyjątkiem jest throw na poziomie bazy danych, przekazywany przez sterownik. To z pewnością zatrzyma twój scenariusz, z porażką. W PHP musi być możliwe adresowanie tego przypadku ...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-01 16:15:27
Tutaj znajduje się funkcja PHP, która wstawia wiersz tylko wtedy, gdy wszystkie podane wartości kolumn nie istnieją już w tabeli.
Jeśli jedna z kolumn jest inna, wiersz zostanie dodany.
Jeśli tabela jest pusta, wiersz zostanie dodany.
-
Jeśli istnieje wiersz, w którym wszystkie podane kolumny mają określone wartości, wiersz nie zostanie dodany.
function insert_unique($table, $vars) { if (count($vars)) { $table = mysql_real_escape_string($table); $vars = array_map('mysql_real_escape_string', $vars); $req = "INSERT INTO `$table` (`". join('`, `', array_keys($vars)) ."`) "; $req .= "SELECT '". join("', '", $vars) ."' FROM DUAL "; $req .= "WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE "; foreach ($vars AS $col => $val) $req .= "`$col`='$val' AND "; $req = substr($req, 0, -5) . ") LIMIT 1"; $res = mysql_query($req) OR die(); return mysql_insert_id(); } return False; }
Przykładowe użycie:
<?php
insert_unique('mytable', array(
'mycolumn1' => 'myvalue1',
'mycolumn2' => 'myvalue2',
'mycolumn3' => 'myvalue3'
)
);
?>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-02 18:33:09
REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;
Jeśli rekord istnieje, zostanie nadpisany; jeśli jeszcze nie istnieje, zostanie utworzony.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-06 14:35:08
Spróbuj:
IF (SELECT COUNT(*) FROM beta WHERE name = 'John' > 0)
UPDATE alfa SET c1=(SELECT id FROM beta WHERE name = 'John')
ELSE
BEGIN
INSERT INTO beta (name) VALUES ('John')
INSERT INTO alfa (c1) VALUES (LAST_INSERT_ID())
END
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-10 10:18:28
Istnieje kilka odpowiedzi, które obejmują, jak rozwiązać ten problem, jeśli masz UNIQUE
indeks, który możesz sprawdzić za pomocą ON DUPLICATE KEY
lub INSERT IGNORE
. Nie zawsze tak jest, a ponieważ UNIQUE
ma ograniczenie długości (1000 bajtów), możesz nie być w stanie tego zmienić. Na przykład musiałem pracować z metadanymi w WordPress (wp_postmeta
).
W końcu rozwiązałem to dwoma zapytaniami:
UPDATE wp_postmeta SET meta_value = ? WHERE meta_key = ? AND post_id = ?;
INSERT INTO wp_postmeta (post_id, meta_key, meta_value) SELECT DISTINCT ?, ?, ? FROM wp_postmeta WHERE NOT EXISTS(SELECT * FROM wp_postmeta WHERE meta_key = ? AND post_id = ?);
ZAPYTANIE 1 jest zwykłym zapytaniem UPDATE
bez efektu, gdy dany zbiór danych nie istnieje. Query 2 is an INSERT
która zależy od NOT EXISTS
, tzn. {[7] } jest wykonywana tylko wtedy, gdy zbiór danych nie istnieje.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-18 18:38:12
Warto zauważyć, że INSERT IGNORE nadal będzie zwiększał klucz podstawowy, niezależnie od tego, czy deklaracja zakończyła się sukcesem, czy nie, tak jak zwykła INSERT.
Spowoduje to luki w głównych kluczach, które mogą sprawić, że programista będzie niestabilny psychicznie. Lub jeśli aplikacja jest źle zaprojektowana i zależy od doskonałych przyrostowych kluczy podstawowych, może to być ból głowy.
Spójrz na innodb_autoinc_lock_mode = 0
(ustawienie serwera i ma niewielki wpływ na wydajność), lub użyj najpierw SELECT, aby upewnij się, że Twoje zapytanie nie zawiedzie(które również zawiera trafienie wydajności i dodatkowy kod).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-07 21:21:07
Update or insert without known primary key
Jeśli masz już klucz unique lub primary, Pozostałe odpowiedzi z INSERT INTO ... ON DUPLICATE KEY UPDATE ...
lub REPLACE INTO ...
powinny działać poprawnie(zauważ, że zastąp na usuwa, jeśli istnieje, a następnie wstawia - w ten sposób nie aktualizuje częściowo istniejących wartości).
Ale jeśli masz wartości dla some_column_id
i some_type
, których kombinacja jest znana jako unikalna. I chcesz zaktualizować some_value
, jeśli istnieje, lub wstawić, jeśli nie istnieje. I chcesz to zrobić w jednym zapytaniu (aby uniknąć wykorzystania transakcji). To może być rozwiązanie:
INSERT INTO my_table (id, some_column_id, some_type, some_value)
SELECT t.id, t.some_column_id, t.some_type, t.some_value
FROM (
SELECT id, some_column_id, some_type, some_value
FROM my_table
WHERE some_column_id = ? AND some_type = ?
UNION ALL
SELECT s.id, s.some_column_id, s.some_type, s.some_value
FROM (SELECT NULL AS id, ? AS some_column_id, ? AS some_type, ? AS some_value) AS s
) AS t
LIMIT 1
ON DUPLICATE KEY UPDATE
some_value = ?
Zasadniczo zapytanie wykonuje się w ten sposób (mniej skomplikowane, niż może się wydawać):
- wybierz istniejący wiersz poprzez dopasowanie klauzuli
WHERE
. - Unia, która daje wynik z potencjalnym nowym wierszem (Tabela
s
), gdzie wartości kolumn są jawnie podane (s.id jest NULL, więc wygeneruje nowy identyfikator auto-increment). - Jeśli znaleziono istniejący wiersz, to potencjalny nowy wiersz z tabeli
s
jest odrzucony (ze względu na ograniczenie 1 w tabelit
), A zawsze wywołaON DUPLICATE KEY
, która będzieUPDATE
kolumnąsome_value
. - Jeśli istniejący wiersz nie został znaleziony, to potencjalny nowy wiersz jest wstawiany(jak podano w tabeli
s
).
uwaga: każda tabela w relacyjnej bazie danych powinna mieć co najmniej podstawową kolumnę auto-increment id
. Jeśli tego nie masz, dodaj go, nawet jeśli nie potrzebujesz go na pierwszy rzut oka. Jest to zdecydowanie potrzebne do tej "sztuczki".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-06 08:26:22