单个execSQL查询中可以指定的SQL变量限制是多少?

27
我正在尝试提高Android数据库插入的速度。目前我所做的是生成一个字符串,例如:
INSERT INTO mytable (column1, column2) VALUES ('value1', 'value2')
SELECT ? as title, ? as musician_id, ? as album_id, ? as genre
UNION SELECT ?, ?, ?, ?
UNION SELECT ?, ?, ?, ?
UNION SELECT ?, ?, ?, ?
UNION SELECT ?, ?, ?, ?
UNION SELECT ?, ?, ?, ?

然后使用以下命令来执行它:

SQLiteDatabase database = //initialized in some way
String insertQuery; // the string of the query above
String [] parameters; // the parameters to use in the insertion.
database.execSQL(insertQuery.toString(), parameters);

当我尝试插入大约2000行时,出现了以下错误:

Caused by: android.database.sqlite.SQLiteException: too many SQL variables (code 1): , while compiling: INSERT INTO songs (title, musician_id, album_id, genre)
SELECT ? as title, ? as musician_id, ? as album_id, ? as genre
UNION SELECT ?, ?, ?, ?
UNION SELECT ?, ?, ?, ?

我尝试插入大约200行时一切正常。

我想这显而易见——我试图在单个execSQL中传递太多变量。有人知道限制是多少,以便我可以将要插入的行拆分为适当的批次吗?


1
你应该在这里查看文档:http://www.sqlite.org/limits.html - ebarrenechea
1
你可以使用二分查找的方法来解决这个问题:首先尝试2000,如果不行就尝试1000,然后是500(或1500)... - Sam
2
@BorisStrandjev 看起来这个错误信息来自 sqlite3.c,你可以在这里看到:https://raw.github.com/android/platform_external_sqlite/master/dist/sqlite3.c - ebarrenechea
顺便说一句,@Sam是正确的,限制设置为999,并且它在源代码中被硬编码,除非在那里进行更改,否则这就是限制。 - ebarrenechea
@ebarrenchea 不错的发现!我建议使用该链接中的两个代码片段发布答案:1)其中SQLITE_MAX_VARIABLE_NUMBER默认为999,2)关于“极端用户想要准备超过32767个变量的语句”的段落(天哪!)。 - Sam
显示剩余4条评论
2个回答

42

限制是在sqlite3.c文件中硬编码的,限制值设为999。不幸的是,它只能在编译时更改。以下是相关片段:

/*
** The maximum value of a ?nnn wildcard that the parser will accept.
*/
#ifndef SQLITE_MAX_VARIABLE_NUMBER
# define SQLITE_MAX_VARIABLE_NUMBER 999
#endif


/*
** The datatype ynVar is a signed integer, either 16-bit or 32-bit.
** Usually it is 16-bits.  But if SQLITE_MAX_VARIABLE_NUMBER is greater
** than 32767 we have to make it 32-bit.  16-bit is preferred because
** it uses less memory in the Expr object, which is a big memory user
** in systems with lots of prepared statements.  And few applications
** need more than about 10 or 20 variables.  But some extreme users want
** to have prepared statements with over 32767 variables, and for them
** the option is available (at compile-time).
*/
#if SQLITE_MAX_VARIABLE_NUMBER<=32767
typedef i16 ynVar;
#else
typedef int ynVar;
#endif

4
只有在编译时更改,那么这个编译时间是指Android操作系统的编译时间吗?那么对于收到此错误的Android应用程序来说,没有办法更改这个限制吗?如果不行,我将必须检查我的IN子句参数的大小,并确保它们永远不会超过999个的限制,将它们分开成单独的查询再进行连接。 - Jeff Lockhart
1
实际上,根据文档和经过端到端测试验证,参数编号从1开始,因此限制为998。文档中指出:“数字通常从1开始,并随着每个新参数递增一次。” https://www.sqlite.org/limits.html - Akhha8
1
实际上,限制是998,因为参数编号从1开始,根据文档,并通过端到端测试进行验证,文档中写道:“数字通常从1开始,并且每个新参数递增一次。” https://www.sqlite.org/limits.html - undefined

16

我正在尝试提高Android数据库插入的速度。目前我的做法是生成一个字符串。

你考虑过使用TRANSACTION吗?我建议你使用它,而不是你目前的方法。我认为使用UNION子句并不是什么“胜利”,而且还有更好、更安全的方法来实现这一点。

db.beginTransaction();
try {
   for (int i = 0 ; i < length ; i++ ) { // or another kind of loop etc.
     // make insert actions
   }
   db.setTransactionSuccessful(); // now commit changes
}
finally {
    db.endTransaction();
}

你认为事务会加速我的插入操作吗? - Boris Strandjev
@BorisStrandjev 如果我不这样做,我就不能回答。如果您想更加“确定”,您可以进行一些测试,例如进行 5,000 到 50,000 次插入,有无使用事务并测量时间。 - Simon Dorociak
1
这是我在SO上学到的最有用的东西。真的!您可以在我的基准测试项目中看到事务与其他数据库方法的比较:https://code.google.com/p/gdg-sofia-prototypes/source/browse/#svn%2Ftrunk%2FAndroidPersistenceBenchmark。速度最快。 - Boris Strandjev
1
我真的希望我能够分配我的接受 - 你们两个都值得它! - Boris Strandjev
@BorisStrandjev 非常感谢你。我非常高兴。我会将您项目的链接保存到书签中,稍后再查看(现在我有自己的本科论文应用程序要处理 :/)。 - Simon Dorociak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接