From 98cd278c0201f15adc20ebd7d6a0ae8507a34066 Mon Sep 17 00:00:00 2001 From: Val Bancer Date: Mon, 5 Sep 2016 21:28:07 +0200 Subject: [PATCH] improved performance of insertMulti() in Mysql --- lib/Cake/Model/Datasource/Database/Mysql.php | 53 ++++++++++++++++++ .../Model/Datasource/Database/MysqlTest.php | 56 +++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/lib/Cake/Model/Datasource/Database/Mysql.php b/lib/Cake/Model/Datasource/Database/Mysql.php index 21f763dc4cb..df511767c09 100644 --- a/lib/Cake/Model/Datasource/Database/Mysql.php +++ b/lib/Cake/Model/Datasource/Database/Mysql.php @@ -849,4 +849,57 @@ protected function _unsigned($real) { return strpos(strtolower($real), 'unsigned') !== false; } +/** + * Inserts multiple values into a table. Uses a single query in order to insert + * multiple rows. + * + * @param string $table The table being inserted into. + * @param array $fields The array of field/column names being inserted. + * @param array $values The array of values to insert. The values should + * be an array of rows. Each row should have values keyed by the column name. + * Each row must have the values in the same order as $fields. + * @return bool + */ + public function insertMulti($table, $fields, $values) { + $table = $this->fullTableName($table); + $holder = implode(',', array_fill(0, count($fields), '?')); + $fields = implode(', ', array_map(array(&$this, 'name'), $fields)); + $pdoMap = array( + 'integer' => PDO::PARAM_INT, + 'float' => PDO::PARAM_STR, + 'boolean' => PDO::PARAM_BOOL, + 'string' => PDO::PARAM_STR, + 'text' => PDO::PARAM_STR + ); + $columnMap = array(); + $rowHolder = "({$holder})"; + $sql = "INSERT INTO {$table} ({$fields}) VALUES "; + $countRows = count($values); + for ($i = 0; $i < $countRows; $i++) { + if ($i !== 0) { + $sql .= ','; + } + $sql .= " $rowHolder"; + } + $statement = $this->_connection->prepare($sql); + foreach ($values[key($values)] as $key => $val) { + $type = $this->introspectType($val); + $columnMap[$key] = $pdoMap[$type]; + } + $valuesList = array(); + $i = 1; + foreach ($values as $value) { + foreach ($value as $col => $val) { + $valuesList[] = $val; + $statement->bindValue($i, $val, $columnMap[$col]); + $i++; + } + } + $result = $statement->execute(); + $statement->closeCursor(); + if ($this->fullDebug) { + $this->logQuery($sql, $valuesList); + } + return $result; + } } diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php index dcae76f6579..ee9c0f57e9a 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php @@ -4174,4 +4174,60 @@ public function testIsConnected() { $this->assertTrue($this->Dbo->isConnected(), 'Should be connected.'); } +/** + * Test insertMulti with id position. + * + * @return void + */ + public function testInsertMultiId() { + $this->loadFixtures('Article'); + $Article = ClassRegistry::init('Article'); + $db = $Article->getDatasource(); + $datetime = date('Y-m-d H:i:s'); + $data = array( + array( + 'user_id' => 1, + 'title' => 'test', + 'body' => 'test', + 'published' => 'N', + 'created' => $datetime, + 'updated' => $datetime, + 'id' => 100, + ), + array( + 'user_id' => 1, + 'title' => 'test 101', + 'body' => 'test 101', + 'published' => 'N', + 'created' => $datetime, + 'updated' => $datetime, + 'id' => 101, + ) + ); + $result = $db->insertMulti('articles', array_keys($data[0]), $data); + $this->assertTrue($result, 'Data was saved'); + + $data = array( + array( + 'id' => 102, + 'user_id' => 1, + 'title' => 'test', + 'body' => 'test', + 'published' => 'N', + 'created' => $datetime, + 'updated' => $datetime, + ), + array( + 'id' => 103, + 'user_id' => 1, + 'title' => 'test 101', + 'body' => 'test 101', + 'published' => 'N', + 'created' => $datetime, + 'updated' => $datetime, + ) + ); + $result = $db->insertMulti('articles', array_keys($data[0]), $data); + $this->assertTrue($result, 'Data was saved'); + } }