简体   繁体   中英

PHP MySQL PDO: get last inserted ROW not ID

I have currently a script to insert new data automatically when using the form to add a new element.

I use

 public string PDO::lastInsertId ([ string $name = NULL ] )

Logic of course. Now I am only wondering if this might cause any problems in case a table has no ID field and no auto increment field.

Aside the fact if this means there is a bad database structure used. I want to make sure that this routine works in any situation. Good database or a bad database structure. It should not matter.

What I mean is that I would like to get back the row with data so I can search for it.

EG

1

Insert new data into the database

$query = $pdo->prepare("INSERT INTO `tablename` SET `data_name`='John', `data_familyname`='Doe', 'data_age`=45, `data_wife`='Sarah Doe'");
$query->execute();

2

Fetch the last inserted row entirely

///$LastID = $pdo->lastInsertId(); 
$query = $pdo->prepare("SELECT `data_name`,`data_familyname`,`data_age`,`data_wife` FROM `tablename` WHERE LAST IDFIELD = ".$pdo->lastInsertId());
$query->execute();
$row = $query->fetch();

Row contains

array(
        'data_name'         =>      'John',
        'data_familyname'   =>      'Doe',
        'data_age'          =>      '45',
        'data_wife'         =>      'Sarah Doe'
    );

3

Then update the last inserted row with exactly the same data as just inserted

$query = $pdo->prepare("UPDATE `tablename` SET `data_name`='Chris' WHERE `data_name`=? AND `data_familyname`=? AND 'data_age`=? AND `data_wife`=?");
$query->execute(array_values($row));

The last query is basically:

UPDATE `tablename` SET `data_name`='Chris' 
WHERE `data_name`='John' AND `data_familyname`='Doe' AND 'data_age`='45' AND `data_wife`='Sarah Doe'

Of course there is the possibility that duplicate data exists. And you want to modify only the last row. That is where I meant aside from the fact if the database has any good structure.

But to prevent duplicate data one could add:

Row contains

array(
        'data_id'           =>      20,
        'data_name'         =>      'John',
        'data_familyname'   =>      'Doe',
        'data_age'          =>      '45',
        'data_wife'         =>      'Sarah Doe'
    );

The point is

Step 2

Does not work since I just made it up a couple of minutes a go. However I would like to know if there are any similar functions or routines that do the same as I just mentioned.

Hope that it is clear what I want.

My solution eventually is:

$sth = $pdo_connection->prepare("INSERT INTO `pdo_test` SET 
                            `pdo_name`              =       'John Doe',
                            `pdo_description`       =       'John Doe who served in Vietnam and every other war game as a default name',
                            `pdo_text`              =       'Some text BLABLA'
                        ");
$sth ->execute();

$stmt               = $pdo_connection->query("SELECT LAST_INSERT_ID()");
$result             = $stmt->fetch(PDO::FETCH_ASSOC);
$lastID             = $result['LAST_INSERT_ID()'];


$stmt               = $pdo_connection->prepare("SHOW COLUMNS FROM `pdo_test` WHERE EXTRA LIKE '%AUTO_INCREMENT%'");
$stmt->execute();
$row                = $stmt->fetch(PDO::FETCH_ASSOC);
$AutoIncrementField = $row['Field'];


$stmt               = $pdo_connection->prepare('SELECT * FROM `pdo_test` WHERE `'.$AutoIncrementField.'` = ?');
$stmt->execute(array($lastID));
$result             = $stmt->fetch(PDO::FETCH_ASSOC);

echo print_r($result);

Result:

Array
(
    [pdo_id]            =>      64
    [pdo_name]          =>      John Doe
    [pdo_counter]       =>      0
    [pdo_description]   =>      John Doe who served in Vietnam and every other war game as a default name
    [pdo_text]          =>      Some text BLABLA
)

But an AutoIncrement field seems to be essential.

Second solution but still with an AutoIncrement field.

(as proposed by: Edwin Lambregts)

Note that this solution is not failsafe!!! Because the query selects the last inserted row... But by ALL instances! Hence if another user just inserted a new row... You will get to see his input and not yours. This can occur if the update happens in between.

$sth = $pdo_connection->prepare("INSERT INTO `pdo_test` SET 
                            `pdo_name`              =       'John Doe',
                            `pdo_description`       =       'John Doe who served in Vietnam and every other war game as a default name',
                            `pdo_text`              =       'Some text BLABLA'
                        ");
$sth ->execute();


$stmt               = $pdo_connection->prepare("SHOW COLUMNS FROM `pdo_test` WHERE EXTRA LIKE '%AUTO_INCREMENT%'");
$stmt->execute();
$row                = $stmt->fetch(PDO::FETCH_ASSOC);
$AutoIncrementField = $row['Field'];


$stmt               = $pdo_connection->prepare('SELECT * FROM `pdo_test` ORDER BY `'.$AutoIncrementField.'` DESC LIMIT 0,1');
$stmt->execute();
$result             = $stmt->fetch(PDO::FETCH_ASSOC);

The outcome is:

Array
(
    [pdo_id]            =>      67
    [pdo_name]          =>      John Doe
    [pdo_counter]       =>      0
    [pdo_description]   =>      John Doe who served in Vietnam and every other war game as a default name
    [pdo_text]          =>      Some text BLABLA
)

I'm not sure what your question is but, in order to not mess the things up in your table you have to implement this things:

  1. Have a PK (primary key) on the table. It provides you a way to uniquely identify each row. It should be either a field or a combination of fields that uniquely identify each row. In theory, he best candidate for a PK is an intrinsic property of the entity stored in the table that identifies the entity. For example, for a Country table a good candidate for the PK is the full country name. In the real world, the AUTO_INCREMENT fields were invented because:

    • some tables just don't have a column or a set of columns that can be used as PK ; for example, a table used to store log entries generated by the program - all the entries must be stored even if some of them are identical; an artificial PK is created for them using an AUTO_INCREMENT field;
    • when the type of the best candidate column for PK is string (country name in the example above) or when the candidate for PK is a set of two or more columns, using that as PK will have negative performance impact on the table; an artificial PK (created as an AUTO_INCREMENT integer field) produces a smaller index that improves the speed of the queries that use it.
  2. On INSERT you can either generate a value for the PK and put it into the query or rely on the database to generate one for you (if the PK is an AUTO_INCREMENT field).

    • If you generate a value for the PK you need to be sure it does not already exist in the table. You can either check if it doesn't exist before INSERT or you can try to INSERT and check if it succeeded. Even if you check before, the INSERT can still fail because another instance of the script can run on the same time, generate and INSERT the same value for PK . In this situation you need to generate another value and try to INSERT again and this workflow repeats until it succeeds. It doesn't look like a good strategy for me.
    • You can let the database generate an unique value for your PK (by providing NULL for the PK in the INSERT query or not providing it at all) then ask it about the value it generated. MySQL provides the LAST_INSERT_ID() function for this purpose. An excerpt from the documentation:

      The ID that was generated is maintained in the server on a per-connection basis . This means that the value returned by the function to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client . This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions.

      The database takes care of everything: the value is unique, it doesn't interfere with other instances of the script (or even other code) that tries to insert in the same table on the same time; the INSERT succeeds, the PK is unique, I get back the value that identifies my row and not someone else's row.

Having an additional column that is not an intrinsic property of the entities stored in the table is a small price to pay for the benefits it produces.

This is just a guideline and an artificial AUTO_INCREMENT column should be added only when it helps . In the example above with the countries table, an AUTO_INCREMENTED column for PK is not needed:

  • the table contains only several hundred rows; there are no insertions or deletions in it (in fact there are, but only once in a while; it's possible that your application is retired before a change in the Country table is needed);
  • there is a good candidate for the PK : is the ISO-3166 country code; there are even two flavors of it: 2-letter and 3-letter - pick your favorite; it is a CHAR[2] (or CHAR[3] ) and because the table is so small having it as PK doesn't affect the performance in any way.

Getting the last inserted record/row certainly is possible without the usage of id's.

MySQL syntax:

SELECT column_name FROM table_name ORDER BY column_name DESC LIMIT 1

It will simply show the last inserted record.

Source: http://www.w3schools.com/sql/sql_func_last.asp

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM