Obecnie używam tego typu SQL w MySQL do wstawiania wielu wierszy wartości w jednym zapytaniu:
INSERT INTO `tbl` (`key1`,`key2`) VALUES ('r1v1','r1v2'),('r2v1','r2v2'),...
Przy odczytach dotyczących PDO, przygotowane instrukcje użytkowania powinny dać mi większe bezpieczeństwo niż statyczne zapytania.
Chciałbym zatem wiedzieć, czy możliwe jest wygenerowanie „wstawiania wielu wierszy wartości jednym zapytaniem” z przygotowanych zestawień.
Jeśli tak, czy mogę wiedzieć, jak mogę to wdrożyć?
php
pdo
insert
prepared-statement
hoball
źródło
źródło
$stmt->execute($data);
php.net/manual/en/ ... W zasadzie wszystkie parametry są przekazywane jako ciągi znaków. Po prostu przejrzyj dane po utworzeniu zapytania i ręczniebindValue
lubbindParam
przekaż typ jako trzeci argument.Odpowiedzi:
Wstawianie wielu wartości z przygotowanymi wyciągami PDO
Wstawianie wielu wartości w jednej instrukcji wykonania. Dlaczego, ponieważ według tej strony jest szybszy niż zwykłe wkładki.
więcej wartości danych lub prawdopodobnie masz pętlę, która wypełnia dane.
Dzięki przygotowanym wstawkom musisz znać pola, do których wstawiasz, oraz liczbę pól do utworzenia? symbole zastępcze do powiązania parametrów.
Tak w zasadzie chcemy, aby wyglądała instrukcja insert.
Teraz kod:
Chociaż w moim teście różnica wynosi tylko 1 sekundę, gdy używam wielu wkładek i regularnie przygotowanych wkładek z jedną wartością.
źródło
placeholders()
w kółko. Wywołaj go raz przed pętląsizeof($datafields)
zi dołącz wynikowy ciąg znaków do$question_marks[]
wnętrza pętli.Taka sama odpowiedź jak pan Balagtas, nieco jaśniejsza ...
Najnowsze wersje MySQL i PHP PDO zrobić wsparcia wielorzędowe
INSERT
oświadczenia.Przegląd SQL
SQL będzie wyglądał mniej więcej tak, przy założeniu, że chcesz utworzyć tabelę z trzema kolumnami
INSERT
.ON DUPLICATE KEY UPDATE
działa zgodnie z oczekiwaniami nawet z wielorzędowym INSERT; dołącz to:Przegląd PHP
Twój kod PHP będzie podążał za zwykłymi wywołaniami
$pdo->prepare($qry)
i$stmt->execute($params)
PDO.$params
będzie jednowymiarową tablicą wszystkich wartości do przekazania doINSERT
.W powyższym przykładzie powinien zawierać 9 elementów; PDO użyje każdego zestawu 3 jako pojedynczego wiersza wartości. (Wstawienie 3 wierszy po 3 kolumny = tablica 9-elementowa).
Realizacja
Poniższy kod jest napisany dla przejrzystości, a nie wydajności. Pracuj z
array_*()
funkcjami PHP, aby uzyskać lepsze sposoby mapowania lub przeglądania danych, jeśli chcesz. To, czy możesz używać transakcji, zależy oczywiście od typu Twojej tabeli MySQL.Zarozumiały:
$tblName
- nazwa ciągu tabeli do WSTAWIANIA$colNames
- 1-wymiarowa tablica nazw kolumn tabeli Te nazwy kolumn muszą być prawidłowymi identyfikatorami kolumn MySQL; uciekaj przed nimi za pomocą grawisów (``), jeśli nie są$dataVals
- tablica wielowymiarowa, gdzie każdy element jest 1-wymiarową tablicą wiersza wartości do INSERTPrzykładowy kod
źródło
$rowPlaces
nie są już potrzebne:$allPlaces = implode(',', array_fill(0, count($dataVals), '('.str_pad('', (count($colNames)*2)-1, '?,').')'));
votes
dodać unikatowyunique_index
(user
,email
,address
);array_push($dataToInsert, ...array_values($dataVals));
będzie wtedy znacznie szybszeforeach ($dataVals as $row => $data) {}
Widziałem wielu użytkowników, którzy zalecają iterację po instrukcjach INSERT zamiast budowania jako pojedynczego zapytania łańcuchowego, tak jak zrobiła to wybrana odpowiedź. Zdecydowałem się przeprowadzić prosty test z tylko dwoma polami i bardzo prostą instrukcją wstawiania:
Podczas gdy samo zapytanie zajmowało milisekundy lub mniej, drugie zapytanie (pojedynczy ciąg) było konsekwentnie 8 razy szybsze lub więcej. Gdyby to zostało zbudowane tak, aby odzwierciedlało import tysięcy wierszy w wielu innych kolumnach, różnica mogłaby być ogromna.
źródło
bind_param
instrukcji w drugiej procedurze importu”?(?,?)
, prawda?Zaakceptowana odpowiedź Herberta Balagtasa działa dobrze, gdy tablica $ data jest mała. W przypadku większych tablic danych $ funkcja array_merge staje się zbyt wolna. Mój plik testowy do utworzenia tablicy danych $ ma 28 kolumn i około 80 000 wierszy. Ukończenie ostatecznego scenariusza zajęło 41 sekund .
Użycie funkcji array_push () do utworzenia $ insert_values zamiast array_merge () dało 100-krotne przyspieszenie z czasem wykonania 0,41 s .
Problematyczny array_merge ():
Aby wyeliminować potrzebę array_merge (), możesz zamiast tego zbudować następujące dwie tablice:
Tych tablic można następnie używać w następujący sposób:
źródło
array_push($data, ...array_values($row))
zamiast$data = array_merge($data, array_values($row));
. O wiele szybciej.array_push()
jest dostępna nawet w php 4.array_push()
, ale dlatego, że @Mark używa rozpakowywania argumentów. Zauważyłeś...array_values()
wezwanie?array_values()
jest również dostępny w php 4. Nie jestem pewien, czy to masz na myśliargument unpacking
.Dwa możliwe podejścia:
Lub:
Jeśli dane dla wszystkich wierszy znajdują się w jednej tablicy, użyłbym drugiego rozwiązania.
źródło
$stmt->execute();
powinien znajdować się poza pętlą foreach?Po prostu nie jest to sposób, w jaki używasz przygotowanych wypowiedzi.
Wstawianie jednego wiersza na zapytanie jest całkowicie w porządku, ponieważ jedną przygotowaną instrukcję można wykonać wielokrotnie z różnymi parametrami. W rzeczywistości jest to jedna z największych zalet, ponieważ umożliwia wstawienie dużej liczby rzędów w wydajny, bezpieczny i wygodny sposób.
Więc być może możliwe jest zaimplementowanie proponowanego schematu, przynajmniej dla ustalonej liczby wierszy, ale jest prawie pewne, że nie jest to tak naprawdę to, czego chcesz.
źródło
Krótsza odpowiedź: spłaszcz tablicę danych uporządkowanych według kolumn
Podczas wstawiania 1000 lub więcej rekordów nie chcesz przechodzić przez każdy rekord, aby je wstawić, gdy potrzebujesz tylko zliczenia wartości.
źródło
Oto moje proste podejście.
źródło
On the readings on PDO, the use prepared statements should give me a better security than static queries.
$workouts_id
, co może mieć$value
całkiem nieoczekiwane dane. Nie możesz zagwarantować, że może nie teraz, ale w przyszłości inny programista sprawi, że te dane będą niezabezpieczone. Myślę więc, że bardziej trafne jest wykonanie zapytania przygotowanego przez PDO.Oto klasa, którą napisałem, wykonująca wiele wkładek z opcją czyszczenia:
źródło
Oto jak to zrobiłem:
Najpierw zdefiniuj nazwy kolumn, których będziesz używać, lub pozostaw je puste, a pdo przyjmie, że chcesz użyć wszystkich kolumn w tabeli - w takim przypadku musisz podać wartości wierszy w dokładnej kolejności, w jakiej pojawiają się w tabeli .
Teraz załóżmy, że masz już przygotowaną dwuwymiarową tablicę. Wykonaj iterację i utwórz ciąg z wartościami wierszy, takimi jak:
Teraz właśnie sprawdziłeś, czy $ wiersze zostały już zdefiniowane, a jeśli nie, utwórz go i zapisz wartości wierszy oraz niezbędną składnię SQL, aby była poprawną instrukcją. Zwróć uwagę, że ciągi znaków powinny znajdować się w podwójnych cudzysłowach i pojedynczych cudzysłowach, więc zostaną natychmiast rozpoznane jako takie.
Pozostaje tylko przygotować instrukcję i wykonać w następujący sposób:
Do tej pory testowano do 2000 wierszy, a czas wykonania jest ponury. Przeprowadzę więcej testów i wrócę tutaj, jeśli będę miał coś więcej do wniesienia.
Pozdrowienia.
źródło
Ponieważ nie zostało to jeszcze zasugerowane, jestem prawie pewien, że LOAD DATA INFILE jest nadal najszybszym sposobem ładowania danych, ponieważ wyłącza indeksowanie, wstawia wszystkie dane, a następnie ponownie włącza indeksy - wszystko w jednym żądaniu.
Zapisanie danych jako csv powinno być dość trywialne, biorąc pod uwagę fputcsv. MyISAM jest najszybszy, ale nadal masz dużą wydajność w InnoDB. Są jednak inne wady, więc wybrałbym tę trasę, jeśli wstawiasz dużo danych i nie zawracasz sobie głowy wierszami poniżej 100.
źródło
Chociaż stare pytanie, wszystkie składki bardzo mi pomogły, więc oto moje rozwiązanie, które działa w mojej własnej
DbContext
klasie.$rows
Parametr jest po prostu tablicą tablic asocjacyjnych reprezentującymi wiersze lub modele:field name => insert value
.Jeśli używasz wzorca, który używa modeli, pasuje to dobrze, gdy dane modelu są przekazywane jako tablica, powiedzmy z
ToRowArray
metody w klasie modelu.źródło
$tableName
jest on widoczny dla użytkownika, a tak nie jest, znajduje się w DAL. Czy możesz rozwinąć swoje roszczenia? Samo mówienie rzeczy nie jest pomocne.$tableName
?Oto inne (wąskie) rozwiązanie tego problemu:
Najpierw musisz policzyć dane tablicy źródłowej (tutaj: $ aData) za pomocą count (). Następnie używasz array_fill () i generujesz nową tablicę zawierającą tyle wpisów, ile ma tablica źródłowa, każdy z wartością „(?,?)” (Liczba symboli zastępczych zależy od używanych pól; tutaj: 2). Następnie wygenerowaną tablicę należy implodować, a jako klej należy użyć przecinka. W pętli foreach musisz wygenerować kolejny indeks dotyczący liczby używanych symboli zastępczych (liczba symboli zastępczych * bieżący indeks tablicy + 1). Musisz dodać 1 do wygenerowanego indeksu po każdej powiązanej wartości.
źródło
Za pomocą tej funkcji możesz wstawić wiele wierszy w jednym zapytaniu:
$ wiersz to tablica tablic wartości. W twoim przypadku możesz wywołać funkcję z
Ma to tę zaletę, że używasz przygotowanych instrukcji podczas wstawiania wielu wierszy za pomocą jednego zapytania. Bezpieczeństwo!
źródło
Oto moje rozwiązanie: https://github.com/sasha-ch/Aura.Sql oparciu o bibliotekę auraphp / Aura.Sql.
Przykład użycia:
Mile widziane są raporty błędów.
źródło
Mój przykład z prawdziwego świata, aby wstawić wszystkie niemieckie kody pocztowe do pustej tabeli (aby później dodać nazwy miast):
Jak widać jest w pełni elastyczny. Nie musisz sprawdzać liczby kolumn ani sprawdzać, na jakiej pozycji znajduje się Twoja kolumna. Wystarczy ustawić dane wstawiania:
Jestem dumny z niektórych konstruktorów ciągów zapytań, ponieważ działają one bez ciężkich funkcji tablicowych, takich jak array_merge. Zwłaszcza vsprintf () było dobrym znaleziskiem.
Wreszcie musiałem dodać 2x while (), aby uniknąć przekroczenia limitu pamięci. Zależy to od limitu pamięci, ale w ogóle jest to dobre ogólne rozwiązanie, aby uniknąć problemów (a 10 zapytań jest nadal znacznie lepsze niż 10.000).
źródło
test.php
Database.php
źródło
Miałem ten sam problem i tak sobie radzę i stworzyłem dla siebie funkcję (i możesz jej użyć, jeśli ci to pomoże).
Przykład:
INSERT INTO kraje (kraj, miasto) VALUES (Niemcy, Berlin), (Francja, Paryż);
Jeśli insertMultipleData ($ table, $ multi_params) zwraca TRUE , Twoje dane zostały wstawione do Twojej bazy danych.
źródło
Na podstawie moich eksperymentów stwierdziłem, że instrukcja mysql insert z wieloma wierszami wartości w jednej transakcji jest najszybsza.
Jeśli jednak danych jest za dużo,
max_allowed_packet
ustawienie mysql może ograniczyć wstawianie pojedynczej transakcji z wieloma wierszami wartości. W związku z tym następujące funkcje zawiodą, gdy dane będą większe niżmax_allowed_packet
rozmiar mysql :singleTransactionInsertWithRollback
singleTransactionInsertWithPlaceholders
singleTransactionInsert
Najbardziej skutecznym scenariuszem wstawiania ogromnych danych jest
transactionSpeed
metoda, jednak wyżej wymienione metody pochłaniają więcej czasu. Aby rozwiązać ten problem, możesz podzielić dane na mniejsze części i wielokrotnie wywoływać wstawianie pojedynczej transakcji lub zrezygnować z szybkości wykonywania, używająctransactionSpeed
metody.Oto moje badania
Wyniki dla 100 000 wpisów dla tabeli zawierającej tylko dwie kolumny są takie, jak poniżej
źródło
To zadziałało dla mnie
źródło
co z czymś takim:
Pomysł polega na tym, aby cyklicznie zmieniać wartości tablicy, dodając „numery identyfikacyjne” do każdej pętli dla przygotowanych symboli zastępczych instrukcji, jednocześnie dodając wartości do tablicy dla parametrów wiązania. Jeśli nie lubisz używać indeksu „klucza” z tablicy, możesz dodać $ i = 0 i $ i ++ wewnątrz pętli. Każda z nich działa w tym przykładzie, nawet jeśli masz tablice asocjacyjne z nazwanymi kluczami, nadal działałoby, pod warunkiem, że klucze byłyby unikalne. Przy odrobinie pracy byłoby dobrze również w przypadku tablic zagnieżdżonych.
** Zauważ, że substr usuwa zmienne $ sql z ostatniej spacji i przecinka, jeśli nie masz spacji, musisz zmienić to na -1 zamiast -2.
źródło
Większość podanych tutaj rozwiązań do stworzenia przygotowanego zapytania jest bardziej złożona niż powinna. Korzystając z wbudowanych funkcji PHP, możesz łatwo utworzyć instrukcję SQL bez znacznego narzutu.
Biorąc pod uwagę
$records
tablicę rekordów, w której każdy rekord jest sam w sobie tablicą indeksowaną (w postacifield => value
), poniższa funkcja wstawi rekordy do podanej tabeli$table
, na połączeniu PDO$connection
, używając tylko jednej przygotowanej instrukcji. Zauważ, że jest to rozwiązanie PHP 5.6+ ze względu na użycie funkcji rozpakowywania argumentów w wywołaniuarray_push
:źródło