0

我有一个问题,我得到了一组单词,我必须通过 foreach 将它们中的每一个插入到 DB 中,但是整个事情运行得太慢了。

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 中,它将获取我的第一个单词 (hello) 并检查它是否存在于单词表中。如果它找不到单词,它将把它插入那里,并将它插入到另一个表中,其中的变量 $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 秒。我可以将查询合并为一个吗?我错过了什么吗?

谢谢。

4

2 回答 2

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.

于 2013-11-14T01:37:07.140 回答
1

因此,为了实现超快的性能,必须改变一些事情。首先,事务的使用(有点像 mulquin 的回答),其次,我为 word 表中的列 word 添加了唯一索引。这将帮助我跳过检查单词是否已经存在的 sql,而忽略该事务中的插入语句。

这是将自动创建我的唯一索引的新表

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;");

因此,如有疑问,请使用交易 :)

于 2013-11-14T02:02:53.917 回答