我有一个问题,我得到一个单词数组,我必须通过foreach将它们中的每一个插入到数据库中,但整个过程运行得太慢。
foreach($words as $word) {
// even more checks
if(strlen($word) < 3) continue;
$word = $this->db->escapeString(strtolower($word));
// word not found?
$result = $this->db->querySingle("SELECT id FROM words WHERE word = '{$word}'");
if(!$result) {
$this->db->exec("INSERT INTO words (word) VALUES ('{$word}')");
$this->db->exec("INSERT INTO search (reply, word) VALUES ('{$id}', '{$this->db->lastInsertRowID()}')");
// word found
} else $this->db->exec("INSERT INTO search (reply, word) VALUES ('{$id}', '{$result}')");
}
基本上可以说$ words = array('hello','kitten'); 在那个foreach中,它将接受我的第一个字(你好)并检查它是否存在于单词表中。如果它找不到该单词,它会将其插入那里,并将其插入到另一个表中,该变量$ id不会在foreach中更改。但是,如果它找到了它将直接插入第二个表中的单词。在那之后它将采取第二个字(小猫)并做同样的事情。
将php5与sqlite3一起使用
$this->db = new \SQLite3(ROOT . DS . 'db' . DS . 'app.db');
有问题的表格非常清晰
CREATE TABLE words (id INTEGER PRIMARY KEY AUTOINCREMENT, word TEXT);
CREATE TABLE search (reply INTEGER, word INTEGER);
CREATE INDEX search_reply_idx ON search (reply);
CREATE INDEX search_word_idx ON search (word);
这大部分时间都适用于10-15个单词,但如果我有超过150个单词,它将会慢到8秒。我可以将查询合并为一个吗?我错过了什么吗?
感谢。
答案 0 :(得分:2)
此方法基于SQLite支持此语法进行插入的前提(此语法适用于MySQL):
INSERT INTO table (column) VALUES ('value1'), ('value2'), ('value3'), ('valueN');
你可以做的是使用PHP使用SELECT
语句中的逻辑构建SQL插入字符串,然后在最后执行两个查询;一个在单词表上,一个在搜索表上:
$sql_words = 'INSERT INTO words (word) VALUES ';
$sql_search = 'INSERT INTO search (reply, word) VALUES ';
$count = count($words);
$i = 0;
$last_insert_id = '';
$delim = '';
foreach ($words as $word)
{
$result = $this->db->querySingle("SELECT id FROM words WHERE word = '{$word}'");
if ($i < $count) {
$delim = ', ';
} else {
$delim = '';
}
if ($result) {
$sql_search .= "('{$result}','{$word}')".$delim;
} else {
$sql_words .= "('{$word}')".$delim;
$sql_search .= "('{$result}','{$last_insert_id}')".$delim;
}
$last_insert_id = $result;
$i++;
}
$this->db-exec($sql_words);
$this->db->exec($sql_search);
至于这段代码的有效性,我不确定,$id
来自哪里有点困惑,并假设它与$result
相同。
答案 1 :(得分:1)
因此,为了实现超快的性能,有些事情必须改变。 首先,使用交易(有点像mulquin的答案),其次我为单词表中的列字添加了一个唯一索引。这将帮助我跳过检查单词是否已存在的sql,并忽略该事务中的insert语句。
这是将自动创建我的唯一索引的新表
CREATE TABLE words (id INTEGER PRIMARY KEY AUTOINCREMENT, word TEXT UNIQUE)
这是修改过的foreach
$t1 = $t2 = null;
foreach($words as $word) {
// even more checks
if(strlen($word) < 3) continue;
$word = $this->db->escapeString(strtolower($word));
$t1 .= "INSERT OR IGNORE INTO words (word) VALUES ('{$word}');";
$t2 .= "INSERT INTO search (reply, word) SELECT '{$id}', id FROM words WHERE word = '{$word}';";
}
// run transactions
if(!is_null($t1)) $this->db->exec("BEGIN TRANSACTION;" . $t1 . "COMMIT;");
if(!is_null($t2)) $this->db->exec("BEGIN TRANSACTION;" . $t2 . "COMMIT;");
因此,如有疑问,请使用交易:)