foreach中的Sql查询会减慢页面的速度

时间:2013-11-14 00:03:28

标签: php sql sqlite

我有一个问题,我得到一个单词数组,我必须通过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秒。我可以将查询合并为一个吗?我错过了什么吗?

感谢。

2 个答案:

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

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