Как сравнить два формата файлов .db в Android? - PullRequest
0 голосов
/ 26 октября 2019

Я делаю приложение для Android, которое позволяет пользователю создавать резервные копии файлов .db на своем устройстве. Эти файлы .db являются базой данных из моего приложения, сгенерированной sqlite. Когда пользователь восстановит эти файлы, я хотел бы проверить, не редактировал ли он его, и он все еще находится в формате базы данных моего приложения (например, созданного с помощью кода ниже). Для уточнения, пожалуйста, прокомментируйте.

String SQL_CREATE_FLIGHT_TABLE = "CREATE TABLE " + flightsEntery.TABLE_NAME + "("
                + flightsEntery.COLUMN_id + " INTEGER PRIMARY KEY AUTOINCREMENT, "
                + flightsEntery.COLUMN_mat + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_ori + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_des + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_data + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_mil + " INTEGER NOT NULL DEFAULT 0, "
                + flightsEntery.COLUMN_Npou + " INTEGER NOT NULL DEFAULT 1, "
                + flightsEntery.COLUMN_IFRapp + " INTEGER NOT NULL, "
                + flightsEntery.COLUMN_Func + " INTEGER NOT NULL, "
                + flightsEntery.COLUMN_OBS + " TEXT, "
                + flightsEntery.COLUMN_Hdec + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_Hpou + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_Hsol + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_Hhora + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_Hdiu + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_Hnot + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_Hifr + " INTEGER NOT NULL DEFAULT 0, "
                + flightsEntery.COLUMN_Hvfr + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_Hnav + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_Sobcapota + " TEXT NOT NULL DEFAULT '00:00', "
                + flightsEntery.COLUMN_Hcorte + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_Hacc + " TEXT NOT NULL, "
                + flightsEntery.COLUMN_VooPara + " INTEGER NOT NULL DEFAULT 0, "
                + flightsEntery.COLUMN_VooReb + " INTEGER NOT NULL DEFAULT 0, "
                + flightsEntery.COLUMN_Trab + " INTEGER NOT NULL DEFAULT 0, "
                + flightsEntery.COLUMN_OrdyData + " INTERGER NOT NULL, "
                + flightsEntery.COLUMN_CivF + " INTEGER NOT NULL DEFAULT 0, "
                + flightsEntery.COLUMN_CivD + " INTEGER NOT NULL DEFAULT 0, "
                + flightsEntery.COLUMN_DiB + " INTEGER NOT NULL DEFAULT 0);";

Я бы с удовольствием попробовал любую идею, спасибо!

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

1 Ответ

1 голос
/ 26 октября 2019

Я полагаю, что вы могли бы использовать следующее, которое берет два файла и сравнивает схему (sqlite_master) между этими двумя на основе запроса, который объединяет две группы по столбцам name, type и sql (если они являютсято же самое, тогда было бы 2 на группу) и выводит те, для которых нет 2 строк (то есть несоответствие). Если в возвращенном курсоре нет строк, то совпадение схемы.

public class CompareDBSchemas {

    public static boolean areDBSchemasEqual(File db1File, File db2File, boolean compareSizes) {
        boolean rv = true;
        if (!(db1File.exists() && db2File.exists())) return false;
        if (compareSizes) {
            if (db1File.length() != db2File.length()) return false;
        }
        SQLiteDatabase db1 = SQLiteDatabase.openDatabase(db1File.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        db1.execSQL("ATTACH '" +
                    db2File.getPath() +  "' AS other");

        /*
            WITH cte AS (SELECT * FROM main.sqlite_master UNION ALL SELECT * FROM other.sqlite_master)
            SELECT * FROM cte GROUP BY type,name,sql HAVING count() <> 2
         */
        Cursor csr = db1.rawQuery("WITH cte AS (" +
                "SELECT * FROM main.sqlite_master UNION ALL SELECT * FROM other.sqlite_master"  +
                        ") " +
                        "SELECT * FROM cte GROUP BY type,name,sql HAVING count() <> 2 ",
                null
        );
        if (csr.getCount() > 0) {
            rv = false;
        }
        csr.close();
        db1.close();
        return rv;
    }

    public static boolean areDBSchemasEqual(File db1File, File db2File) {
        return areDBSchemasEqual(db1File,db2File,false);
    }
}
  • Обратите внимание, что в приведенном выше есть опция для определения различий в размерах базы данных, которая может обнаруживать измененные данные, а не только изменение схемы.

Пример использования

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

DatabseHlpr.java

public class DatabaseHlpr extends SQLiteOpenHelper {

    public static final String TABLE_NAME = "mytable";
    public static final String MYTABLE_COL_ID = BaseColumns._ID;
    public static final String MYTABLE_COL_COMMON = "common";
    public static final String MYTABLE_COL_SCHEMA1ONLY = "schema1only";

    private int mSchema;
    private SQLiteDatabase mDB;


    public DatabaseHlpr(@Nullable Context context, @Nullable String name, int schema) {
        super(context, name, null,1);
        this.mSchema = schema;
        mDB = this.getWritableDatabase();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        switch (mSchema) {
            case 1:
                useSchema1(db);
                break;
            default:
                useSchema0(db);
        }
    }

    private void useSchema1(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(" +
                MYTABLE_COL_ID + " INTEGER PRIMARY KEY, " +
                MYTABLE_COL_COMMON + " TEXT, " +
                MYTABLE_COL_SCHEMA1ONLY + " TEXT " +
                ")");
    }

    private void useSchema0(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(" +
                MYTABLE_COL_ID + " INTEGER PRIMARY KEY, " +
                MYTABLE_COL_COMMON + " TEXT" +
                ")");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    static final String DB1NAME = "db1", DB2NAME = "db2", DB3NAME = "db3";
    DatabaseHlpr mDB1Hlpr, mDB2Hlpr, mDB3Hlpr;
    File db1File, db2File,db3File;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDB1Hlpr = new DatabaseHlpr(this,DB1NAME,0);
        mDB2Hlpr = new DatabaseHlpr(this,DB2NAME,0);
        mDB3Hlpr = new DatabaseHlpr(this,DB3NAME,1);
        mDB1Hlpr.close();
        mDB2Hlpr.close();
        mDB3Hlpr.close();
        db1File = new File(this.getDatabasePath(DB1NAME).getPath());
        db2File = new File(this.getDatabasePath(DB2NAME).getPath());
        db3File = new File(this.getDatabasePath(DB3NAME).getPath());
        String result = " the same ";
        if (!CompareDBSchemas.areDBSchemasEqual(db1File,db2File,false)) {
            result = " NOT the same ";
        }
        Log.d("RESULTINFO",
                "Database Schemas are " + result +
                        " for " +
                        "\n\t" +db1File.getPath() +
                        "\n and \n\t" + db2File.getPath()
        );

        result = " the same ";
        if (!CompareDBSchemas.areDBSchemasEqual(db1File,db3File)) {
            result = " NOT the same ";
        }

        Log.d("RESULTINFO",
                "Database Schemas are " + result +
                        " for " +
                        "\n\t" +db1File.getPath() +
                        "\n and \n\t" + db3File.getPath()
        );
    }
}
  • Обратите внимание, что выше было написано, чтобы продемонстрировать процесс сравнения и, таким образом, поиск файлов является простым и удобным,Он не предназначен для отражения того, как будут извлечены файлы.

Результат: -

2019-10-27 07:19:23.688 28976-28976/aso.so58566618dbcompareschema D/RESULTINFO: Database Schemas are  the same  for 
        /data/user/0/aso.so58566618dbcompareschema/databases/db1
     and 
        /data/user/0/aso.so58566618dbcompareschema/databases/db2
2019-10-27 07:19:23.693 28976-28976/aso.so58566618dbcompareschema D/RESULTINFO: Database Schemas are  NOT the same  for 
        /data/user/0/aso.so58566618dbcompareschema/databases/db1
     and 
        /data/user/0/aso.so58566618dbcompareschema/databases/db3

ДОПОЛНИТЕЛЬНО

Если вы хотели проверить количество строк(данные вставлены или удалены) и фактические данные (данные изменены при обновлении), затем можно использовать следующую адаптацию класса CompareDBSchemas: -

public class CompareDBSchemas {

    private static final String
            SQLITE_MASTER = "sqlite_master",
            SQLITE_MASTER_TYPE_COLUMN = "type",
            SQLITE_MASTER_NAME_COLUMN = "name",
            SQLITE_MASTER_SQL_COLUMN = "sql",
            SQLITE_MASTER_TABLE_TYPE = "table",
            SQLITE_SYSTEMTABLES = "sqlite_",
            ANDROID_METADATA = "android_metadata",
            CTE_NAME = "cte", MAIN_SCHEMA = "main", OTHER_SCHEMA = "other"
    ;

    public static boolean areDBSchemasEqual(File db1File, File db2File, boolean compareSizes, boolean compareRowCounts, boolean compareData) {
        boolean rv = true;
        if (!(db1File.exists() && db2File.exists())) return false;
        if (compareSizes) {
            if (db1File.length() != db2File.length()) return false;
        }
        SQLiteDatabase db1 = SQLiteDatabase.openDatabase(db1File.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
        db1.beginTransaction();
        db1.execSQL("ATTACH '" +
                    db2File.getPath() +  "' AS " + OTHER_SCHEMA);

        /*
            WITH cte AS (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_master)
            SELECT * FROM cte GROUP BY type,name,sql HAVING count() <> 2
         */
        Cursor csr = db1.rawQuery("WITH " + CTE_NAME + " AS (" +
                "SELECT * FROM " + MAIN_SCHEMA + "." + SQLITE_MASTER +
                        " UNION ALL " +
                        "SELECT * FROM " + OTHER_SCHEMA + "." + SQLITE_MASTER  +
                        ") " +
                        "SELECT * FROM " + CTE_NAME +
                        " GROUP BY " +
                        SQLITE_MASTER_TYPE_COLUMN + "," +
                        SQLITE_MASTER_NAME_COLUMN + "," +
                        SQLITE_MASTER_SQL_COLUMN +
                        " HAVING count() <> 2 ",
                null
        );
        if (csr.getCount() > 0) {
            rv = false;
        }
        if (compareRowCounts && rv) {
            csr = db1.rawQuery("SELECT * FROM main." + SQLITE_MASTER +
                    " WHERE " + SQLITE_MASTER_TYPE_COLUMN +
                    " = '" + SQLITE_MASTER_TABLE_TYPE +
                    "' AND (" + SQLITE_MASTER_NAME_COLUMN +
                    " NOT LIKE '" + SQLITE_SYSTEMTABLES +
                    "%' AND " + SQLITE_MASTER_NAME_COLUMN +
                    " <> '" + ANDROID_METADATA + "')",null);
            while(csr.moveToNext()) {
                if (
                        DatabaseUtils.queryNumEntries(db1,MAIN_SCHEMA +"." + csr.getString(csr.getColumnIndex(SQLITE_MASTER_NAME_COLUMN))) ==
                                DatabaseUtils.queryNumEntries(db1,OTHER_SCHEMA + "." + csr.getString(csr.getColumnIndex(SQLITE_MASTER_NAME_COLUMN)))
                ) continue;
                rv = false;
                break;
            }
        }
        if (compareData && rv) {
            csr.moveToPosition(-1);
            while (csr.moveToNext()) {
                if (
                        isTableDataTheSame(db1,csr.getString(csr.getColumnIndex(SQLITE_MASTER_NAME_COLUMN)))
                ) continue;
                rv = false;
                break;
            }
        }
        db1.endTransaction();
        csr.close();
        db1.close();
        return rv;
    }

    private static boolean isTableDataTheSame(SQLiteDatabase db, String table) {
        boolean rv = true;
        Cursor csr = db.rawQuery("PRAGMA table_info("+ table +")",null);
        StringBuilder columnConcat = new StringBuilder();
        while (csr.moveToNext()) {
            if (columnConcat.length() > 0) columnConcat.append("||");
            columnConcat.append(csr.getString(csr.getColumnIndex(SQLITE_MASTER_NAME_COLUMN)));
        }
        csr = db.rawQuery("WITH " + CTE_NAME +
                        " AS (" +
                        " SELECT " + columnConcat.toString() +
                        " AS comparison FROM " + MAIN_SCHEMA + "." + table +
                        " UNION ALL SELECT " + columnConcat.toString() + " FROM " + OTHER_SCHEMA + "." + table +
                        ") SELECT * FROM " + CTE_NAME +
                        " GROUP BY comparison HAVING count() <> 2",
                null
        );
        if (csr.getCount() > 0) {
            rv = false;
        }
        csr.close();
        return rv;
    }

    public static boolean areDBSchemasEqual(File db1File, File db2File) {
        return areDBSchemasEqual(db1File,db2File,false,false,false);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...