Каков предел переменных SQL, которые можно указать в одном запросе execSQL?

Я пытаюсь улучшить скорость вставки в базу данных Android. Что я сейчас делаю, так это генерирую строку вроде:

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. Кто-нибудь знает, каков предел, чтобы я мог разделить строки, которые я вставляю в соответствующие партии?


person Boris Strandjev    schedule 09.03.2013    source источник
comment
Вам следует ознакомиться с документацией здесь: sqlite.org/limits.html   -  person ebarrenechea    schedule 09.03.2013
comment
@ebarrenchea Я почти уверен, что ограничение наложено не SQLite, а API Android SQLite. Вы нашли признаки обратного?   -  person Boris Strandjev    schedule 09.03.2013
comment
Вы можете подойти к этому с помощью бинарного поиска: 2000 не работает, поэтому попробуйте 1000, затем 500 (или 1500)...   -  person Sam    schedule 09.03.2013
comment
Хм, я искал переменную в sqlite.org/limits.html и нашел: SQLITE_MAX_VARIABLE_NUMBER, который по умолчанию 999. Это пункт № 9: максимальное количество параметров хоста в одном операторе SQL. Начните поиск с 998, 999, 1000. По иронии судьбы 999 — это последнее число (симметрично) двоичного поиска с 2000 ударит.   -  person Sam    schedule 09.03.2013
comment
@BorisStandjev Похоже, это сообщение об ошибке исходит от sqlite3.c, как вы можете видеть здесь: raw.github.com/android/platform_external_sqlite/master/dist/   -  person ebarrenechea    schedule 09.03.2013
comment
Кстати, @Sam прав, предел установлен на 999, и он жестко запрограммирован в исходном коде, поэтому, если он там не изменен, это предел.   -  person ebarrenechea    schedule 09.03.2013
comment
@ebarrenchea Хорошая находка! Я предлагаю опубликовать ответ, используя два фрагмента из этой ссылки: 1) где SQLITE_MAX_VARIABLE_NUMBER по умолчанию равен 999, 2) отрывок о том, что экстремальный пользователь хочет иметь подготовленные операторы с более чем 32767 переменными (Ого!).   -  person Sam    schedule 09.03.2013
comment
@ebarrenchea Теперь я также проверил число 999 в эмпирическом тестировании. Я настаиваю, чтобы вы разместили ответ на вопрос, чтобы я мог принять вас.   -  person Boris Strandjev    schedule 09.03.2013
comment
Спасибо! Я опубликовал ответ, и @Sam, спасибо за совет о том, как мне его опубликовать. :)   -  person ebarrenechea    schedule 09.03.2013


Ответы (2)


Ограничение жестко закодировано в 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
person ebarrenechea    schedule 09.03.2013
comment
Изменилось только во время компиляции, тогда это время компиляции ОС Android? Значит, нет способа изменить это для приложения Android, которое получает эту ошибку? Если нет, мне придется проверить размер моих параметров предложения IN и убедиться, что они никогда не превышают этот предел 999, разделив их на отдельные запросы для объединения. - person Jeff Lockhart; 15.06.2015

Я пытаюсь улучшить скорость вставки в базу данных 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();
}
person Simon Dorociak    schedule 09.03.2013
comment
Как вы думаете, транзакция ускорит мои вставки? - person Boris Strandjev; 09.03.2013
comment
@BorisStrandjev, если бы я этого не сделал, я бы не стал отвечать. Если вы хотите быть более уверенным в этом, вы можете сделать несколько тестов, когда вы, например, будете выполнять 5 000 - 50 000 вставок с использованием и без использования транзакции и замерять время. - person Simon Dorociak; 09.03.2013
comment
@BorisStrandjev хорошо, дайте мне знать. - person Simon Dorociak; 09.03.2013
comment
Это самая полезная вещь, которую я когда-либо изучал в SO. Действительно! Вы можете увидеть, как транзакция сравнивается с несколькими другими подходами к базам данных, в моем проекте сравнительного анализа: code.google.com/p/gdg-sofia-prototypes/source/browse/. еще быстрее. - person Boris Strandjev; 09.03.2013
comment
Я действительно хотел бы разделить свое согласие - вы оба этого заслужили! - person Boris Strandjev; 09.03.2013
comment
@BorisStrandjev большое спасибо. Я очень рад. Я сохраню ссылку на ваш проект в закладки, чтобы посмотреть позже (теперь у меня есть своя работа с заявкой на бакалаврскую работу :/). - person Simon Dorociak; 09.03.2013