В SQLite подготовленные операторы действительно улучшают производительность? - PullRequest
28 голосов
/ 09 ноября 2009

Я слышал, что подготовленные операторы с SQLite должны повысить производительность. Я написал некоторый код, чтобы проверить это, и не увидел никакой разницы в производительности с их использованием. Я подумал, что мой код неверен. Пожалуйста, дайте мне знать, если вы видите какие-либо ошибки в том, как я это делаю ...

[self testPrep:NO dbConn:dbConn];
[self testPrep:YES dbConn:dbConn];

reuse=0
recs=2000
2009-11-09 10:39:18 -0800
processing...
2009-11-09 10:39:32 -0800

reuse=1
recs=2000
2009-11-09 10:39:32 -0800
processing...
2009-11-09 10:39:46 -0800

-(void)testPrep:(BOOL)reuse dbConn:(sqlite3*)dbConn{
    int recs = 2000;
    NSString *sql;
    sqlite3_stmt *stmt;

    sql = @"DROP TABLE test";
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);

    sql = @"CREATE TABLE test (id INT,field1 INT, field2 INT,field3 INT,field4 INT,field5 INT,field6 INT,field7 INT,field8 INT,field9 INT,field10 INT)";
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);

    for(int i=0;i<recs;i++){
        sql = @"INSERT INTO test (id,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10) VALUES (%d,1,2,3,4,5,6,7,8,9,10)";
        sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);
    }

    sql = @"BEGIN";
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);

    if (reuse){
        sql = @"select * from test where field1=?1 and field2=?2 and field3=?3 and field4=?4 and field5=?5 and field6=?6 and field6=?6 and field8=?8 and field9=?9 and field10=?10 and id=?11";
        sqlite3_prepare_v2(dbConn, [sql UTF8String], -1, &stmt, NULL);
    }

    NSLog(@"reuse=%d",reuse);
    NSLog(@"recs=%d",recs);
    NSDate *before = [NSDate date];
    NSLog([before description]);
    NSLog(@"processing...");
    for(int i=0;i<recs;i++){
        if (!reuse){
            sql = @"select * from test where field1=?1 and field2=?2 and field3=?3 and field4=?4 and field5=?5 and field6=?6 and field6=?6 and field8=?8 and field9=?9 and field10=?10 and id=?11";
            sqlite3_prepare_v2(dbConn, [sql UTF8String], -1, &stmt, NULL);
        }
        sqlite3_bind_int(stmt, 1, 1);
        sqlite3_bind_int(stmt, 2, 2);
        sqlite3_bind_int(stmt, 3, 3);
        sqlite3_bind_int(stmt, 4, 4);
        sqlite3_bind_int(stmt, 5, 5);
        sqlite3_bind_int(stmt, 6, 6);
        sqlite3_bind_int(stmt, 7, 7);
        sqlite3_bind_int(stmt, 8, 8);
        sqlite3_bind_int(stmt, 9, 9);
        sqlite3_bind_int(stmt, 10, 10);
        sqlite3_bind_int(stmt, 11, i);

        while(sqlite3_step(stmt) == SQLITE_ROW) {
        }
        sqlite3_reset(stmt);
    }

    sql = @"BEGIN";
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);

    NSDate *after = [NSDate date];
    NSLog([after description]);
}

Ответы [ 3 ]

22 голосов
/ 09 ноября 2009

Подготовленные операторы повышают производительность за счет кэширования плана выполнения для запроса после того, как оптимизатор запросов нашел лучший план.

Если используемый вами запрос не имеет сложного плана (например, простого выбора / вставки без объединений), то подготовленные операторы не принесут вам значительных улучшений, поскольку оптимизатор быстро найдет лучший план.

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

6 голосов
/ 07 декабря 2009

Да - это огромная разница, если вы используете sqlite3_exec() против sqlite3_prepare_v2() / sqlite3_bind_xxx() / sqlite3_step() для массовых вставок.

sqlite3_exec() - это только удобный метод. Внутренне он просто вызывает ту же последовательность sqlite3_prepare_v2() and sqlite3_step(). Ваш пример кода вызывает sqlite3_exec() снова и снова для литеральной строки:

for(int i=0;i<recs;i++){
    sql = @"INSERT INTO test (id,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10) VALUES (%d,1,2,3,4,5,6,7,8,9,10)";
    sqlite3_exec(dbConn, [sql UTF8String],NULL,NULL,NULL);
}

Я не знаю, как работает синтаксический анализатор SQLite, но, возможно, синтаксический анализатор достаточно умен, чтобы распознать, что вы используете одну и ту же буквальную строку, а затем пропускает повторный анализ / повторную компиляцию при каждой итерации.

Если вы попробуете тот же эксперимент с изменяющимися значениями - вы увидите гораздо большую разницу в производительности.

0 голосов
/ 16 августа 2011

Использование prepare + step вместо execute возможно значительное улучшение производительности. В некоторых случаях выигрыш в производительности во время выполнения составляет более 100%.

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