Jak uzyskać identyfikator ostatniego zaktualizowanego wiersza w MySQL?

138

Jak uzyskać identyfikator ostatniego zaktualizowanego wiersza w MySQL za pomocą PHP?

Avinash
źródło
2
mysqli_insert_id ($ link) zwróci dokładnie identyfikator ostatniego zaktualizowanego wiersza. Używam tej funkcji w moich projektach w tym samym celu. Możesz go używać zarówno w zapytaniach synchronicznych, jak i asynchronicznych. Po prostu wstaw $ lastupdated_row_id = mysqli_insert_id ($ link) do swojego kodu i będzie działać dla Ciebie.
Naeem Ul Wahhab,
1
Czy nie znasz już identyfikatora wiersza po aktualizacji? Myślę, że muszą być przypadki, w których tego nie robisz.
jlcharette
1
Prawdopodobnie używasz klauzuli Where w zapytaniu aktualizującym, dlaczego nie możesz użyć tej samej klauzuli Where w zapytaniu wybierającym? UPDATE foo WHERE bar = 1; SELECT id FROM foo WHERE bar = 1?
Salman A

Odpowiedzi:

242

Znalazłem odpowiedź na ten problem :)

SET @update_id := 0;
UPDATE some_table SET column_name = 'value', id = (SELECT @update_id := id)
WHERE some_other_column = 'blah' LIMIT 1; 
SELECT @update_id;

EDYCJA przez aefxx

Technikę tę można dodatkowo rozszerzyć, aby pobrać identyfikator każdego wiersza, na który ma wpływ instrukcja aktualizacji:

SET @uids := null;
UPDATE footable
   SET foo = 'bar'
 WHERE fooid > 5
   AND ( SELECT @uids := CONCAT_WS(',', fooid, @uids) );
SELECT @uids;

Spowoduje to zwrócenie ciągu znaków ze wszystkimi identyfikatorami połączonymi przecinkami.

Pomyk
źródło
7
Nie mogę uwierzyć, że to miało 0 głosów poparcia, a komentarz mysql_insert_id (), o który w ogóle nie pytał OP, ma 13. Dzięki Pomyk, sprytna sztuczka!
Jaka Jančar
1
Ponieważ masz WHEREklauzulę, możesz po prostu użyć tej samej WHEREklauzuli w następnym zapytaniuSELECT id FROM footable WHERE fooid > 5
Salman A
4
Może każdy to rozumie, ale jeśli używasz mysql_query (); musisz podzielić to na trzy różne rozmowy, ale to zadziała.
rael_kid
8
Nie ma nawet potrzeby używania zmiennych. LAST_INSERT_ID()może przyjąć argument, który ustawi wartość zwracaną przez następne wywołanie funkcji LAST_INSERT_ID(). Na przykład: UPDATE table SET id=LAST_INSERT_ID(id), row='value' WHERE other_row='blah';. Teraz SELECT LAST_INSERT_ID();zwróci zaktualizowany identyfikator. Aby uzyskać więcej informacji, zobacz odpowiedź newtover.
Joel
3
@SteveChilds Właśnie komentowałem pytanie newtovera i chcę dodać rozwiązanie opisanej pułapki. Te zestawy LAST_INSERT_ID 0 jeśli nie ma nic do UPDATE: UPDATE lastinsertid_test SET row='value' WHERE row='asd' AND LAST_INSERT_ID(id) OR LAST_INSERT_ID(0);. Jednak nie pobiera wielu identyfikatorów, więc ta odpowiedź jest lepsza.
martinczerwi
34

Hm, dziwię się, że wśród odpowiedzi nie widzę najłatwiejszego rozwiązania.

Załóżmy, że item_idjest to kolumna będąca liczbą całkowitą w itemstabeli i aktualizujesz wiersze za pomocą następującej instrukcji:

UPDATE items
SET qwe = 'qwe'
WHERE asd = 'asd';

Następnie, aby poznać najnowszy wiersz, którego dotyczy problem, tuż po instrukcji, należy nieznacznie zaktualizować instrukcję do następującego:

UPDATE items
SET qwe = 'qwe',
    item_id=LAST_INSERT_ID(item_id)
WHERE asd = 'asd';
SELECT LAST_INSERT_ID();

Jeśli chcesz zaktualizować tylko naprawdę zmieniony wiersz, musisz dodać warunkową aktualizację item_id poprzez LAST_INSERT_ID sprawdzając, czy dane zmienią się w wierszu.

newtover
źródło
4
Jest bezpieczny dla wielu użytkowników, ponieważ wielu klientów może wydać instrukcję UPDATE i uzyskać własną wartość sekwencji za pomocą instrukcji SELECT (lub mysql_insert_id ()), bez wpływu lub wpływu na innych klientów, którzy generują własne wartości sekwencji. Zgodnie z dev.mysql.com/doc/refman/5.0/en/…
brutuscat
1
Czy to nie ustawi item_id na ostatnio wstawiony rekord?
arleslie
2
@arleslie, jeśli podążasz za linkiem do dokumentów w komentarzu brutuscata powyżej, zobaczysz, że nie. Jest to zamierzone zachowanie dokładnie w przypadku.
newtover
2
To jest rzeczywiście prawidłowa odpowiedź. Chociaż zapytanie wydaje się nieco sprzeczne z intuicją, to jest dokładnie to, co mówi Dokumentacja MySQL.
Timothy Zorn,
3
W przypadku, gdy zapytanie jest mylące cię, ponieważ LAST_INSERT_ID (wyrażenie) zwróci wartość wyraż na wezwanie UPDATE można również używać go tak:UPDATE items SET qwe = 'qwe' WHERE asd = 'asd' AND LAST_INSERT_ID(item_id); SELECT LAST_INSERT_ID();
martinczerwi
14

Jest to oficjalnie proste, ale niezwykle sprzeczne z intuicją. Jeśli robisz:

update users set status = 'processing' where status = 'pending'
limit 1

Zmień to na:

update users set status = 'processing' where status = 'pending'
and last_insert_id(user_id) 
limit 1

Dodanie last_insert_id(user_id)w whereklauzuli mówi MySQL, aby ustawić swoją wewnętrzną zmienną na identyfikator znalezionego wiersza. Kiedy przekazujesz taką wartość last_insert_id(expr), w końcu zwraca tę wartość, która w przypadku identyfikatorów takich jak tutaj jest zawsze dodatnią liczbą całkowitą i dlatego zawsze zwraca wartość true, nigdy nie kolidując z klauzulą ​​where. Działa to tylko wtedy, gdy rzeczywiście znaleziono jakiś wiersz, więc pamiętaj, aby sprawdzić wiersze, których dotyczy problem. Następnie możesz uzyskać identyfikator na wiele sposobów.

MySQL last_insert_id()

Możesz generować sekwencje bez wywoływania LAST_INSERT_ID (), ale użyteczność korzystania z tej funkcji w ten sposób polega na tym, że wartość ID jest utrzymywana na serwerze jako ostatnia automatycznie wygenerowana wartość. Jest bezpieczny dla wielu użytkowników, ponieważ wielu klientów może wydać instrukcję UPDATE i uzyskać własną wartość sekwencji za pomocą instrukcji SELECT (lub mysql_insert_id ()), bez wpływu lub wpływu innych klientów, którzy generują własne wartości sekwencji.

MySQL mysql_insert_id()

Zwraca wartość wygenerowaną dla kolumny AUTO_INCREMENT przez poprzednią instrukcję INSERT lub UPDATE. Użyj tej funkcji po wykonaniu instrukcji INSERT w tabeli zawierającej pole AUTO_INCREMENT lub po użyciu instrukcji INSERT lub UPDATE do ustawienia wartości kolumny z LAST_INSERT_ID (wyrażenie).

Przyczyną różnic między LAST_INSERT_ID () a mysql_insert_id () jest to, że LAST_INSERT_ID () jest łatwy w użyciu w skryptach, podczas gdy mysql_insert_id () próbuje podać dokładniejsze informacje o tym, co dzieje się z kolumną AUTO_INCREMENT.

PHP mysqli_insert_id()

Wykonanie instrukcji INSERT lub UPDATE za pomocą funkcji LAST_INSERT_ID () również zmodyfikuje wartość zwracaną przez funkcję mysqli_insert_id ().

Kładąc wszystko razem:

$affected_rows = DB::getAffectedRows("
    update users set status = 'processing' 
    where status = 'pending' and last_insert_id(user_id) 
    limit 1"
);
if ($affected_rows) {
    $user_id = DB::getInsertId();
}

(FYI, że klasa DB jest tutaj .)

Vladimir Kornea
źródło
Ważna uwaga: to jest bezpieczne dla wielu połączeń, utrwalone wartości last_insert_id lub mysql_insert_id (i być może mysqli_insert_id, nie sprawdziłem), są dla połączenia. Niesamowite!
Ryan S
11

Jest to ta sama metoda, co odpowiedź Salmana A, ale oto kod, którego naprawdę potrzebujesz, aby to zrobić.

Najpierw edytuj tabelę, aby automatycznie śledziła modyfikacje wiersza. Usuń ostatni wiersz, jeśli chcesz wiedzieć tylko, kiedy wiersz został pierwotnie wstawiony.

ALTER TABLE mytable
ADD lastmodified TIMESTAMP 
    DEFAULT CURRENT_TIMESTAMP 
    ON UPDATE CURRENT_TIMESTAMP;

Następnie, aby znaleźć ostatni zaktualizowany wiersz, możesz użyć tego kodu.

SELECT id FROM mytable ORDER BY lastmodified DESC LIMIT 1;

Cały ten kod pochodzi z MySQL vs PostgreSQL: Dodanie kolumny „Last Modified Time” do tabeli i MySQL Manual: Sorting Rows . Właśnie go złożyłem.

Chad von Nau
źródło
7
Ta metoda może pobrać nieprawidłowy identyfikator, jeśli drugie wstawianie jest wykonywane tuż po pierwszym zapytaniu wstawiania. Jeśli jednak użyjemy „WHERE” z tym zapytaniem, np. SELECT id FROM mytable ORDER BY lastmodified DESC LIMIT 1 WHERE (niektóre warunki związane z ostatnim zapytaniem wstawiającym), to może to uniemożliwić pobranie niewłaściwego identyfikatora.
Naeem Ul Wahhab
1
@ TheNoble-Coder To prawda. Jeśli chcesz uzyskać identyfikator wiersza, który wpłynął na określone zapytanie aktualizujące, użyj techniki Pomyk. Ta technika jest bardziej przydatna, gdy jest używana asynchronicznie, gdy potrzebujesz tylko absolutnej ostatniej aktualizacji.
Chad von Nau
5

Zapytanie:

$sqlQuery = "UPDATE 
            update_table 
        SET 
            set_name = 'value' 
        WHERE 
            where_name = 'name'
        LIMIT 1;";

Funkcja PHP:

function updateAndGetId($sqlQuery)
{
    mysql_query(str_replace("SET", "SET id = LAST_INSERT_ID(id),", $sqlQuery));
    return mysql_insert_id();
}

To dla mnie praca;)

GigolNet Guigolachvili
źródło
podobało mi się to samo, ale działałem
3

Więcej informacji na temat powyżej zaakceptowanej odpowiedzi
Dla tych, którzy zastanawiali się nad :=&=

Istotna różnica między :=i =, która :=działa jako operator przypisania zmiennej wszędzie, podczas gdy =działa w ten sposób tylko w instrukcjach SET, a wszędzie indziej jest operatorem porównania.

Więc SELECT @var = 1 + 1;pozostawi @varniezmienione i zwróci wartość logiczną (1 lub 0 w zależności od bieżącej wartości @var), podczas gdy SELECT @var := 1 + 1;zmieni się @varna 2 i zwróci 2 . [Źródło]

Anon30
źródło
Hej dzięki. To, co mają na myśli, jest w rzeczywistości bardziej interesujące niż przyjęta powyżej odpowiedź.
Green
2

Hej, po prostu potrzebowałem takiej sztuczki - rozwiązałem to w inny sposób, może to zadziała. Należy pamiętać, że nie jest to rozwiązanie skalowalne i będzie bardzo niekorzystne w przypadku dużych zestawów danych.

Podziel zapytanie na dwie części -

najpierw wybierz identyfikatory wierszy, które chcesz zaktualizować, i zapisz je w tabeli tymczasowej.

po drugie, wykonaj oryginalną aktualizację ze stanem w instrukcji aktualizacji zmienionym na where id in temp_table.

Aby zapewnić współbieżność, musisz zablokować tabelę przed tymi dwoma krokami, a następnie zwolnić blokadę na końcu.

Ponownie, działa to dla mnie w przypadku zapytania, które kończy się na limit 1, więc nie używam nawet tabeli tymczasowej, ale zamiast tego po prostu zmienną do przechowywania wyniku pierwszej select.

Wolę tę metodę, ponieważ wiem, że zawsze będę aktualizować tylko jeden wiersz, a kod jest prosty.

olamundo
źródło
2
SET @uids := "";
UPDATE myf___ingtable
   SET id = id
   WHERE id < 5
  AND ( SELECT @uids := CONCAT_WS(',', CAST(id AS CHAR CHARACTER SET utf8), @uids) );
SELECT @uids;

Musiałem PRZESŁAĆ identyfikator (nie wiem dlaczego) ... lub nie mogę pobrać zawartości @uids (to była kropelka). Przy okazji wielkie dzięki za odpowiedź Pomyka!

Gromish
źródło
2

Identyfikator ostatniego zaktualizowanego wiersza to ten sam identyfikator, którego używasz w zapytaniu „updateQuery”, aby znaleźć i zaktualizować ten wiersz. Więc po prostu zapisz (zadzwoń) ten identyfikator w dowolny sposób.

last_insert_id()zależy od AUTO_INCREMENT, ale ostatni zaktualizowany identyfikator nie.

Driada
źródło
3
Co się stanie, jeśli aktualizacja nie zostanie przeprowadzona z identyfikatora, ale raczej z innego zestawu atrybutów?
Sebas
2

Moje rozwiązanie jest takie, że najpierw wybierz "id" (@uids) za pomocą polecenia select, a po zaktualizowaniu tego identyfikatora za pomocą @uids.

SET @uids := (SELECT id FROM table WHERE some = 0 LIMIT 1);
UPDATE table SET col = 1 WHERE id = @uids;SELECT @uids;

zadziałało przy moim projekcie.

Ahmet Uğur
źródło
1

Jeśli robisz tylko wstawki i chcesz mieć jedną z tej samej sesji, zrób zgodnie z odpowiedzią peirix. Jeśli dokonujesz modyfikacji, będziesz musiał zmodyfikować schemat bazy danych, aby przechowywać wpis, który był ostatnio aktualizowany.

Jeśli chcesz mieć identyfikator z ostatniej modyfikacji, która mogła pochodzić z innej sesji (tj. Nie tej, która została właśnie wykonana przez aktualnie działający kod PHP, ale wykonaną w odpowiedzi na inne żądanie), możesz dodać TIMESTAMP do tabeli o nazwie last_modified (więcej informacji można znaleźć pod adresem http://dev.mysql.com/doc/refman/5.1/en/datetime.html ), a następnie podczas aktualizacji ustaw last_modified = CURRENT_TIME.

Po ustawieniu tego możesz użyć zapytania takiego jak: SELECT id FROM table ORDER BY last_modified DESC LIMIT 1; aby pobrać ostatnio zmodyfikowany wiersz.

a1kmm
źródło
-9

Nie ma potrzeby używania tak długiego kodu MySQL. W PHP zapytanie powinno wyglądać mniej więcej tak:

$updateQuery = mysql_query("UPDATE table_name SET row='value' WHERE id='$id'") or die ('Error');
$lastUpdatedId = mysql_insert_id();
Driada
źródło