Проблема SQLite "невозможно открыть файл базы данных" - PullRequest
1 голос
/ 07 сентября 2011

Я прихожу к вам после многих часов поиска в Google и чтения других дискуссий о SQLite в StackOverflow, но я определенно не могу найти никакого объяснения моей проблемы, поэтому вот оно:

  • Контекст:
    Я разрабатываю приложение для iPad, которое несколько раз должно иметь дело с некоторыми "большими" объемами данных.В одном из них я должен импортировать координаты точек из файла .kml (xml от Google для географических данных) в мою базу данных, чтобы потом использовать их позже с помощью MKMapView и загружать их быстрее, чем при синтаксическом анализе xml, когда требуется показать конкретный файл.layer.

  • Подробности:
    Импорт очень прост: при работе с этими файлами меня интересуют только 2 таблицы:

    • Одна, содержащая определения и детали зон: на данный момент integer в качестве идентификатора и text для именования.
    • Одна, содержащая две real для хранения координат иinteger ссылка на первую таблицу для определения, к какой точке зоны относится часть.
      Итак, пока я читаю мой файл, я сначала создаю запись для новой зоны, а затем вставляю точки во вторую таблицу с идентификаторомпоследней зоны, созданной в первой таблице ... ничего сложного!

    Но ...

  • Проблема:
    После долгого запуска я получаю исключение из SQLite сзнаменитое сообщение «Невозможно открыть файл базы данных», а потом приходит сообщение, что я больше ничего не могу сделать с базой данных.Это исключение может происходить случайным образом при создании зоны или методах вставки точек.

  • Мои отражения:
    Учитывая многочисленные точки в этих файлах, я подозревал, что памятьили насыщение диска, но другие части моего приложения отбросили эти точки (на мой взгляд).
    Во-первых, память: случается, что, когда возникает исключение, приложение использует около 10 или 12 МБ ОЗУ.Это может показаться довольно большим, но это из-за загруженного в память файла ~ 10MB .kml, так что это объяснимо.Кроме того, приложение MKMapView в моем приложении работает с тоннами слоев листов с высоким разрешением над картой и, как следствие, приводит к пикам памяти, которые могут позволить себе 20 или даже 25 МБ, не приводя к падению iPad.
    Во-вторых, диск:при перезагрузке базы данных и заполнении только 2 таблиц, описанных выше, размер файла БД при возникновении исключения всегда составляет около 2,2 или 2,5 МБ, но когда я заполняю другие таблицы (другие части моих приложений работают хорошо!), файл БДоколо 6 или 7 МБ, и устройство не жалуется вообще.

  • И что?!
    CPU-гнев и паника?Я так не думаю, потому что некоторые из других таблиц моей базы данных заполнены в том же ритме без проблем ... и запуск моего приложения в симуляторе тоже дает сбой, а ядро ​​i7 просто смеется над работой.
    SQLite badиспользовать?Вот и мы!На мой взгляд, это единственное решение!Но я действительно не могу понять, что здесь происходит, потому что я обрабатываю свои запросы так же, как и в других частях приложения, которые, повторяя себя, работают как шарм!

  • Подробности SQLite:
    У меня есть класс DB, который я использую во избежание создания / освобождения объекта SqliteConnection каждый запрос Iделать, и все мои методы, связанные с базой данных, содержатся в этом классе, чтобы быть уверенным, что я не буду играть с соединением где-либо еще, не зная его.Вот соответствующие методы этого класса:

    public void     saveZone(ObjZone zone)  { //at this point, just creates an entry with a name and let sqlite give it a new id
        lock (connection) { //SqliteConnection object
            try {
                openConnection();
                SqliteCommand cmd = connection.CreateCommand();
                cmd.CommandText = zone.id == 0 ?
                    "insert into ZONES (Z_NAME) values (" + format(zone.name) + ") ;" :
                    "update ZONES set Z_NAME = " + format(zone.name) + " where Z_ID = " + format(zone.id) + " ;";
                cmd.ExecuteNonQuery();
                if (zone.id == 0) {
                    cmd.CommandText = "select Z_ID from ZONES where ROWID = last_insert_rowid() ;";
                    zone.id = uint.Parse(cmd.ExecuteScalar().ToString());
                }
                cmd.Dispose();
            }
            catch (Exception e) {
                Log.failure("DB.saveZone(" + zone.ToString() + ") : [" + e.GetType().ToString() + "] - " +
                    e.Message + "\n" + e.StackTrace); //custom Console.WriteLine() method with some formating
                throw e;
            }
            finally {
                connection.Close();
            }
        }
    }
    
    public void     setPointsForZone(List<CLLocationCoordinate2D> points, uint zone_id) { //registers points for a given zone
        lock (connection) {
            try {
                openConnection();
                SqliteCommand cmd = connection.CreateCommand();
                cmd.CommandText = "delete from ZONESPOINTS where Z_ID = " + format(zone_id);
                cmd.ExecuteNonQuery();
                foreach(CLLocationCoordinate2D point in points) {
                    cmd.CommandText = "insert into ZONESPOINTS values " +
                        "(" + format(zi_id) + ", " + format(point.Latitude.ToString().Replace(",", ".")) + ", "
                        + format(point.Longitude.ToString().Replace(",", ".")) + ");";
                    cmd.ExecuteNonQuery();
                    cmd.Dispose();
                }
            }
            catch (Exception e) {
                Log.failure("DB.setPointsForZone(" + zone_id + ") : [" + e.GetType().ToString() + "] - " + e.Message);  
                throw e;
            }
            finally {
                connection.Close();
            }
        }
    }
    

    И чтобы было как можно яснее, вот некоторые из методов, на которые ссылаются два выше (я использую этот пользовательский метод openConnection(), потому что я использую иностранныйограничения клавиш в большинстве моих таблиц и каскадное поведение не включены по умолчанию, но они мне нужны.):

    void openConnection() {
        try {
            connection.Open();
            SqliteCommand cmd = connection.CreateCommand();
            cmd.CommandText = "PRAGMA foreign_keys = ON";
            cmd.ExecuteNonQuery();
            cmd.Dispose();
        }
        catch (Exception e) {
            Log.failure("DB.openConnection() : [" + e.GetType().ToString() + "] - " + e.Message);
            throw e;
        }
    }
    
    public static string format(object o) {
        return "'" + o.ToString().Replace("'", "''") + "'";
    }
    

Ну , извините за романЯ могу уже поблагодарить вас за чтение всего этого, нет ?!В любом случае, если я пропустил что-то полезное, дайте мне знать, и я запишу это как можно скорее.Я надеюсь, что кто-нибудь сможет помочь мне, в любом случае, спасибо заранее!
(И мои извинения за английский мой бедный французский.)

РЕДАКТИРОВАТЬ
Моя проблема«решена»!После нескольких изменений для отладки изменений, без больших модификаций и безуспешности я вернул код в состояние, в котором я его разместил ... и теперь он работает.Но я действительно был бы признателен, если бы кто-то мог дать мне объяснение того, что могло случиться!Кажется, что поведение SQLite (по крайней мере, на iPad - никогда не использовало его где-либо еще) иногда может быть довольно неясным ...: /

1 Ответ

2 голосов
/ 07 сентября 2011

Я бы не стал скрещивать пальцы на этом, но попробовал бы две вещи:

  1. Если возможно, предварительно обработайте файл KML во второй базе данных SQLite и используйте эту базу данных для импорта данных восновная база данных (с учетом более низких требований к памяти / процессору)
  2. Транзакция импортированных данных небольшими партиями.

HTH

РЕДАКТИРОВАТЬ: вы, возможно, уже проверили это, но в любом случае: невозможно открыть базу данных .

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