Получение окончательного значения для этого запроса MySQL - PullRequest
2 голосов
/ 04 апреля 2010

Моя база данных настроена с тремя таблицами - code, tags и code_tags для пометки сообщений.Это будет SQL-запрос, обрабатываемый при отправке сообщения.Каждый тег нарезается PHP и индивидуально вставляется с помощью этих запросов.

INSERT IGNORE INTO tags (tag) VALUES ('$tags[1]');
SELECT tags.id FROM tags WHERE tag = '$tags[1]' ORDER BY id DESC LIMIT 1;
INSERT INTO code_tags (code_id, tag_id) VALUES ($codeid, WHAT_GOES_HERE?)

WHAT_GOES_HERE?ценность в конце это то, что мне нужно знать.Это должен быть идентификатор тега, который получил второй запрос.Как я могу вставить этот идентификатор в третий запрос?

Надеюсь, я все правильно объяснил.Я перефразирую при необходимости.

Редактировать: Спасибо за вашу помощь, но я все еще немного борюсь в отношении того, что было указано - если оно уже там, я не могу получить вставленный идентификатор...

Ответы [ 2 ]

2 голосов
/ 04 апреля 2010

Если вы используете INSERT IGNORE и новая запись игнорируется (из-за нарушения уникального ключа) mysql_insert_id () и LAST_INSERT_ID () не имеют значимого значения.

Но вы можете использовать INSERT ... ON DUPLICATE KEY UPDATE и LAST_INSERT_ID ( expr ) , чтобы установить ожидаемые данные, которые LAST_INSERT_ID () вернет в футляр из дублета.

Шаг за шагом:
Предположим, у вас есть таблица tags вроде

CREATE TABLE tags (
  id int auto_increment,
  tag varchar(32),
  dummy int NOT NULL DEFAULT 0, /* for demo purposes only */
  primary key(id),
  unique key(tag)
)

Двойная вставка тега приводит к нарушению duplicate key из-за unique key(tag). Это, вероятно, причина, по которой вы использовали INSERT IGNORE. В этом случае MySQL игнорирует нарушение, но новая запись также игнорируется. Проблема в том, что вам нужен идентификатор записи, имеющей tag = 'xyz', независимо от того, была ли она недавно создана или уже была в базе данных. Но прямо сейчас mysql_insert_id () / LAST_INSERT_ID () может предоставить идентификатор новой записи, а не проигнорированной.

С помощью INSERT ...ON DUPLICATE вы можете реагировать на такие повторные нарушения ключа. Если новая запись может быть вставлена ​​(без нарушения), она ведет себя как «обычная» вставка. Но в случае нарушения дублирующего ключа часть после ON DUPLICATE KEY выполняется как инструкция UPDATE для записи с этим конкретным значением индекса, уже существующим в таблице. Например. (с пустой таблицей tags)

INSERT INTO tags (tag) VALUES ('tag A') ON DUPLICATE KEY UPDATE dummy=dummy+1

Это просто вставит запись, как если бы не было предложения ON DUPLICATE .... id получает следующее значение автоинкремента, подставляет значение по умолчанию 0 и tag = 'tag A'. Давайте предположим, что вновь созданное значение auto_increment было 1. Результирующая запись, сохраненная в MySQL, имеет вид (id = 1, tag = 'tag A', dummy = 0), а LAST_INSERT_ID () вернет 1 сразу после этого запроса. Пока все хорошо.
Теперь, если вы снова вставляете ту же запись с тем же запросом, происходит нарушение из-за первой записи (id = 1, 'tag = tag A', dummy = 0). Для это уже существующая запись оператора UPDATE после выполнения ON DUPLICATE KEY, то есть запись становится (id = 1, tag = 'tag A', dummy = 1 ). Но поскольку новая запись не была создана, нового значения auto_increment также не было, и LAST_INSERT_ID () становится бессмысленным . Так что все та же проблема, что и с INSERT IGNORE.
Но есть «специальная» конструкция, которая позволяет вам установить значение, которое LAST_INSERT_ID () должен возвращать после выполнения оператора ON DUPLICATE KEY UPDATE.

id=LAST_INSERT_ID(id)

Выглядит странно, но в действительности он только устанавливает значение, которое LAST_INSERT_ID () вернет.
Если вы используете утверждение

  INSERT INTO
    tags
    (tag)
  VALUES
    ('xyz')
  ON DUPLICATE KEY UPDATE
    id=LAST_INSERT_ID(id)

LAST_INSERT_ID () всегда будет возвращать идентификатор записи, имеющей tag = 'xyz', независимо от того, была ли она добавлена ​​частью INSERT или «обновлена» частью ON DUPLICATE KEY.
То есть если ваш следующий запрос

INSERT INTO
  code_tags
  (code_id, tag_id)
VALUES
  (4711, LAST_INSERT_ID())

используется тег.id для тега 'xyz'.

Автономный пример сценария использует PDO и подготовленные операторы . Он должен делать более или менее то, что вы хотите достичь.

$pdo = new PDO("mysql:host=localhost;dbname=test", 'localonly', 'localonly'); 
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// set up temporary table and demo data
$pdo->exec('CREATE TEMPORARY TABLE tmpTags (id int auto_increment, tag varchar(32), primary key(id), unique key(tag))');
$pdo->exec('CREATE TEMPORARY TABLE tmpCode_tags (code_id int, tag_id int)');
$pdo->exec("INSERT INTO tmpTags (tag) VALUES ('tagA'), ('tagB')");

// prepare the statements
// set id=LAST_INSERT_ID(id), so LAST_INSERT_ID() gets a value even if the record is "ignored"
$stmtTags = $pdo->prepare('
  INSERT INTO
    tmpTags
    (tag)
  VALUES
    (:tag)
  ON DUPLICATE KEY UPDATE
    id=LAST_INSERT_ID(id)
');
$stmtTags->bindParam(':tag', $tag);

$stmtCodeTags = $pdo->prepare('INSERT INTO tmpCode_tags (code_id, tag_id) VALUES (:codeid, LAST_INSERT_ID())');
$stmtCodeTags->bindParam(':codeid', $codeid);

// and some new records we want to insert
$testdata = array(
  array('codeid'=>1, 'tags'=>'tagA tagC'), // tagA is already in the table "tags", tagC is a "new" tag
  array('codeid'=>2, 'tags'=>'tagC tagD tagE') // tagC will already be inserted, tagD and atgE are "new"
);

// process (test)data
foreach($testdata as $data) {
  // the parameter :codeid of $stmtCodeTags is bound to $codeid; assign it the "current" value
  $codeid = $data['codeid'];
  // split the tags
  $tags = explode(' ', $data['tags']);
  foreach($tags as $tag) {
    // the parameter :tag is bound to $tag
    // nothing more to do than to execute the statement
    $stmtTags->execute();
    // the parameter :codeid is bound to $codeid which was set to $codeid=$data['codeid']
    // again nothing more to do than to execute the statement
    $stmtCodeTags->execute();
  }
}
unset($stmtTags);
unset($stmtCodeTags);


// let's see what we've got
$query = '
  SELECT
    ct.code_id, t.tag
  FROM
    tmpCode_tags as ct
  JOIN
    tmpTags as t
  ON
    ct.tag_id=t.id
'; 
foreach( $pdo->query($query, PDO::FETCH_NUM) as $row ) {
  echo join(', ', $row), "\n";
}

печать

1, tagA
1, tagC
2, tagC
2, tagD
2, tagE

edit2: В случае, если PDO-часть сценария и подготовленные операторы пугают, вот то же самое, используя старый модуль php-mysql. Но я призываю вас использовать параметризованные подготовленные высказывания. У нет , чтобы быть PDO, но мне это нравится. Например. модуль mysql i также предоставляет подготовленные операторы, старый модуль mysql - нет.

$mysql = mysql_connect('localhost', 'localonly', 'localonly') or die(mysql_error());
mysql_select_db('test', $mysql) or die(mysql_error());

// set up temporary table and demo data
mysql_query('CREATE TEMPORARY TABLE tmpTags (id int auto_increment, tag varchar(32), primary key(id), unique key(tag))', $mysql) or die(mysql_error());
mysql_query('CREATE TEMPORARY TABLE tmpCode_tags (code_id int, tag_id int)', $mysql) or die(mysql_error());
mysql_query("INSERT INTO tmpTags (tag) VALUES ('tagA'), ('tagB')", $mysql) or die(mysql_error());


// and some new records we want to insert
$testdata = array(
  array('codeid'=>1, 'tags'=>'tagA tagC'), // tagA is already in the table "tags", tagC is a "new" tag
  array('codeid'=>2, 'tags'=>'tagC tagD tagE') // tagC will already be inserted, tagD and atgE are "new"
);

// "prepare" the statements.
// This is nothing like the server-side prepared statements mysqli and pdo offer.
// we have to insert the parameters into the query string, i.e. the parameters must
// be escaped so that they cannot mess up the statement.
// see mysql_real_escape_string() for string literals within the sql statement.
$qsTags = "
  INSERT INTO
    tmpTags
    (tag)
  VALUES
    ('%s')
  ON DUPLICATE KEY UPDATE
    id=LAST_INSERT_ID(id)  
";

$qsCodeTags = "
  INSERT INTO
    tmpCode_tags
    (code_id, tag_id)
  VALUES
    ('%s', LAST_INSERT_ID())
";

foreach($testdata as $data) {
  // in this example codeid is a simple number
  // let's treat it as a string literal in the statement anyway
  $codeid = mysql_real_escape_string($data['codeid'], $mysql);
  $tags = explode(' ', $data['tags']);
  foreach($tags as $tag) {
    // now $tag is certainly a string parameter
    $tag = mysql_real_escape_string($tag, $mysql);
    $query = sprintf($qsTags, $tag);
    mysql_query($query, $mysql) or die(mysql_error());

    $query = sprintf($qsCodeTags, $codeid);
    mysql_query($query, $mysql) or die(mysql_error());
  }
}

// let's see what we've got
$query = '
  SELECT
    ct.code_id, t.tag
  FROM
    tmpCode_tags as ct
  JOIN
    tmpTags as t
  ON
    ct.tag_id=t.id
'; 
$result = mysql_query($query, $mysql) or die(mysql_error());
while ( false!==($row=mysql_fetch_row($result)) ) {
  echo join(', ', $row), "\n";
}
1 голос
/ 04 апреля 2010

Если я понимаю, что вы пытаетесь достичь правильно, второй запрос не требуется - используйте mysql_insert_id , чтобы получить идентификатор ранее вставленной строки, то есть, я предполагаю, что вам нужно для "WHAT_GOES_HERE" ».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...