#android #sqlite #android-sqlite #sqliteopenhelper
Вопрос:
Я хочу знать, как связать одну базу данных с другой. Например, столбец «Приложения» в таблице » Дни «должен открывать один экземпляр таблицы «Приложения» со следующей датой.
Ответ №1:
Вы можете открыть одну из баз данных, а затем ПОДКЛЮЧИТЬ другую базу данных.
Было бы проще иметь все таблицы в одной базе данных. Наличие единой базы данных со всеми таблицами позволит вам обеспечить целостность ссылок, используя ограничения внешнего ключа (т. Е. Чтобы ссылки действительно ссылались).
Пример присоединения с использованием подкласса SQLiteOpenHelper
Ниже приведен пример подключения второй базы данных, обе базы данных основаны на ваших схемах, с помощью одного помощника базы данных (подкласс SQLiteOpenHelper).
- Как и в случае с SQLiteOpenHelper для основной базы данных, если вторая база данных не существовала, она будет создана вместе с таблицами.
Хранитель базы данных
class DatabaseHelper extends SQLiteOpenHelper {
public static final String DBNAME = "database1";
public static final int DBVERSION = 1;
public static final String OTHER_DBNAME = "database2";
public static final String ATTACH_SCHEMA = "a1";
public static final String TB_DAYTABLE = "daytable";
public static final String COL_DATE_DAYTABLE = "date";
public static final String COL_TOTALTIME_DAYTABLE = "totaltime";
public static final String COl_UNLOCKCOUNTS_DAYTABLE = "unlockcounts";
public static final String COL_APPS_DAYTABLE = "apps";
public static final String TB_APPS = "apps";
public static final String COl_NAME_APPS = "name";
public static final String COl_TOTALTIME_APPS = "totaltime";
public static final String COl_COUNTS_APPS = "counts";
public static final String COL_TIMELINE_APPS = "timeline";
public static final String TB_TIMELINE = "timeline";
public static final String COL_STARTTIME_TIMELINE = "starttime";
public static final String COL_ENDTIME_TIMELINE = "endtime";
public static final String COL_TOTALTIME_TIMELINE = "totaltime";
private static volatile DatabaseHelper instance;
private SQLiteDatabase db;
private DatabaseHelper(@Nullable Context context) {
super(context, DBNAME, null, DBVERSION);
db = this.getWritableDatabase();
db.execSQL("ATTACH DATABASE '" context.getDatabasePath(OTHER_DBNAME) "' AS " ATTACH_SCHEMA);
Cursor csr = db.query(ATTACH_SCHEMA ".sqlite_master",new String[]{"name"},"name=?",new String[]{TB_TIMELINE},null,null,null);
if (!csr.moveToFirst()) {
createAttachedTables(db);
}
}
public static DatabaseHelper getInstance(Context context) {
if (instance == null) {
instance = new DatabaseHelper(context);
}
return instance;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " TB_DAYTABLE " ("
COL_DATE_DAYTABLE " TEXT "
"," COL_TOTALTIME_DAYTABLE " INTEGER "
"," COl_UNLOCKCOUNTS_DAYTABLE " INTEGER "
"," COL_APPS_DAYTABLE " INTEGER "
")" );
db.execSQL("CREATE TABLE IF NOT EXISTS " TB_APPS " ("
COl_NAME_APPS " TEXT "
"," COl_TOTALTIME_APPS " INTEGER "
"," COl_COUNTS_APPS " INTEGER "
"," COL_TIMELINE_APPS " TEXT "
")");
}
private void createAttachedTables(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " ATTACH_SCHEMA "." TB_TIMELINE "("
COL_STARTTIME_TIMELINE " TEXT "
"," COL_ENDTIME_TIMELINE " TEXT "
"," COL_TOTALTIME_TIMELINE " INTEGER "
")");
db.execSQL("CREATE TABLE IF NOT EXISTS " ATTACH_SCHEMA "." TB_APPS "("
COl_NAME_APPS " TEXT "
"," COl_TOTALTIME_APPS " INTEGER "
"," COl_COUNTS_APPS " INTEGER "
"," COL_TIMELINE_APPS " TEXT "
")");
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
Помощник был протестирован (подключение 2-й базы данных и создание таблиц), используя следующее в действии (основная активность):-
public class MainActivity extends AppCompatActivity {
private DatabaseHelper db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = DatabaseHelper.getInstance(this);
}
}
Результат
Открыв базу данных в инспекторе баз данных Android Studio и извлеките данные из обеих схем (т. Е. из соответствующей таблицы sqlite_master), можно увидеть следующее :-
т. е. 2 базы данных подключены, и все 4 таблицы существуют.
использование ПРАГМЫ database_list показывает :-
т. е. снова две базы данных
Дополнительный
Учитывая, в чем, по-видимому, заключается ваша проблема, вы хотите хранить данные об использовании приложения. Я считаю, что двух столов было бы достаточно.
То есть таблица ПРИЛОЖЕНИЙ, похоже, дублируется, поэтому не требуется. Во-вторых, количество разблокировок статистики, общее время и т. Д. Можно рассчитать, Просто сохранив время начала и окончания, связанное с соответствующим приложением.
Таким образом, для определения значений можно было бы использовать схему, описанную ниже:-
- где красная линия показывает ссылку с использованием на соответствующее приложение.
Теперь предположим, что таблица приложений содержит:-
И таблица использования содержит :-
Затем такой запрос, как :-
SELECT
date(usage.startdatetime) AS date,
app.appname, count(*) AS unlocks,
time(sum(strftime('%s',usage.enddatetime) - strftime('%s',usage.startdatetime) ),'unixepoch') AS elapsedtime
FROM app JOIN usage ON app._id = usage.applink
GROUP BY date(usage.startdatetime),app._id
ORDER BY date(usage.startdatetime) ASC,appname ASC
Вернет следующий результат:-
- Таким образом, как вы можете видеть, общее время и количество разблокировок были рассчитаны на лету (т. Е. Нет необходимости хранить значения).
Пример 2 — Решение для 2 таблиц для Android :-
Помощник по базам данных AltDBHelper :-
class AltDBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "appusage.db";
public static final int DBVERSION = 1;
private static SQLiteDatabase db;
private static volatile AltDBHelper instance;
private AltDBHelper(@Nullable Context context) {
super(context, DBNAME, null, DBVERSION);
db = this.getWritableDatabase();
}
public static AltDBHelper getInstance(Context context) {
if (instance == null) {
instance = new AltDBHelper(context);
}
return instance;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(AppTable.getCreateSQL());
db.execSQL(UsageTable.getCreateSQL());
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
}
/*
Insert an App into the database
*/
public long insert(String appName) {
ContentValues cv = new ContentValues();
cv.put(AppTable.APPNAME,appName);
return AltDBHelper.db.insert(AppTable.TBNAME,null,cv);
}
/*
Query to return the Date, App, Number of unlocks and Sum of the Elapsed times (total time)
by each day by each App
* Based upon the query :-
* SELECT
* app.appname,
* date(usage.startdatetime),
* time(sum(strftime('%s',enddatetime) - strftime('%s',startdatetime)),'unixepoch')
* FROM usage JOIN app ON app._id = usage.applink
* GROUP BY date(startdatetime),app._id
* ORDER BY date(startdatetime),app.appName
*
*/
public static final String DATE = "date";
private static final String DATE_COL_EXPRESSION = "date(" UsageTable.STARTDATETIME_QUALIFIED ") AS " DATE;
public static final String APPUNLOCKS = "unlocks";
private static final String APPUNLOCKS_COL_EXPRESSION = "count(*) AS " APPUNLOCKS;
public static final String ELAPSEDTIME = "elapsedtime";
private static final String ELAPSEDTIME_EXPRESSION =
"time("
"sum("
"strftime('%s'," UsageTable.ENDDATETIME_QUALIFIED ") "
" - "
" strftime('%s'," UsageTable.STARTDATETIME_QUALIFIED ") "
")"
",'unixepoch'"
")"
" AS " ELAPSEDTIME;
private static final String APPSTATS_TABLE =
AppTable.TBNAME " JOIN " UsageTable.TBNAME
" ON "
AppTable.APPID_QUALIFIED " = "
UsageTable.APPLINK_QUALIFIED;
private static final String DATEPART_GROUPBY = "date(" UsageTable.STARTDATETIME_QUALIFIED ")";
private static final String APPPART_GROUPBY = AppTable.APPID_QUALIFIED;
private static final String GROUPBY = DATEPART_GROUPBY "," APPPART_GROUPBY;
public Cursor getAppDailyStatsAsCursor(Long id) {
String whereClause = null;
String[] whereArgs = null;
if (id != null) {
whereClause = AppTable.APPID_QUALIFIED "=?";
whereArgs = new String[]{String.valueOf(id)};
}
return AltDBHelper.db.query(
APPSTATS_TABLE,
new String[]{
DATE_COL_EXPRESSION,
AppTable.APPNAME_QUALIFIED,
APPUNLOCKS_COL_EXPRESSION,
ELAPSEDTIME_EXPRESSION
},
whereClause,
whereArgs,
GROUPBY,
null,
"date(" UsageTable.STARTDATETIME_QUALIFIED ") " " ASC" "," AppTable.APPNAME " ASC"
);
}
/* The App Table name and column definitions */
public static final class AppTable {
public static final String TBNAME = "app";
public static final String APPID = BaseColumns._ID;
public static final String APPID_QUALIFIED = TBNAME "." APPID;
public static final String APPID_UNIQUE = TBNAME APPID;
public static final String APPNAME = "appname";
public static final String APPNAME_QUALIFIED = TBNAME "." APPNAME;
public static String getCreateSQL() {
return "CREATE TABLE IF NOT EXISTS " TBNAME "("
APPID " INTEGER PRIMARY KEY "
"," APPNAME " TEXT UNIQUE "
")";
}
}
/* The Usage table name and column definitions also method for inserting rows (alternative) */
public static final class UsageTable {
public static final String TBNAME = "usage";
public static final String USAGEID = BaseColumns._ID;
public static final String USAGEID_QUALIFIED = TBNAME "." USAGEID;
public static final String USAGEID_UNIQUE = TBNAME USAGEID;
public static final String APPLINK = "applink";
public static final String APPLINK_QUALIFIED = TBNAME "." APPLINK;
public static final String STARTDATETIME = "startdatetime";
public static final String STARTDATETIME_QUALIFIED = TBNAME "." STARTDATETIME;
public static final String ENDDATETIME = "enddatetime";
public static final String ENDDATETIME_QUALIFIED = TBNAME "." ENDDATETIME;
public static String getCreateSQL() {
return "CREATE TABLE IF NOT EXISTS " TBNAME "("
USAGEID " INTEGER PRIMARY KEY"
"," APPLINK " INTEGER REFERENCES " AppTable.TBNAME "(" AppTable.APPID ") ON DELETE CASCADE ON UPDATE CASCADE"
"," STARTDATETIME " TEXT "
"," ENDDATETIME " TEXT "
")";
}
public static long insert(long appId, String startDateTime, String endDateTime) {
ContentValues cv = new ContentValues();
cv.put(APPLINK,appId);
cv.put(STARTDATETIME,startDateTime);
cv.put(ENDDATETIME,endDateTime);
return AltDBHelper.db.insert(TBNAME,null,cv);
}
}
}
используя вышесказанное в activit, MainActivity, чтобы получить тот же результат, что и в дополнительном разделе, описанном выше :-
public class MainActivity extends AppCompatActivity {
private DatabaseHelper db;
private AltDBHelper altdb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = DatabaseHelper.getInstance(this); /* The original answer attaching a second database */
/* using alternative with just 2 tables */
altdb = AltDBHelper.getInstance(this); /* get an Instance of the DBHelper */
/* Add data for App1 along with some usage data */
long a1 = altdb.insert("App1");
AltDBHelper.UsageTable.insert(a1,"2021-01-01 15:30:00","2021-01-01 16:00:00");
AltDBHelper.UsageTable.insert(a1,"2021-01-01 17:21:34","2021-01-01 19:33:12");
AltDBHelper.UsageTable.insert(a1,"2021-01-02 09:11:27","2021-01-02 09:15:28");
AltDBHelper.UsageTable.insert(a1,"2021-01-03 00:00:00","2021-01-03 05:00:59");
/* Likewise for App2 */
long a2 = altdb.insert("App2");
AltDBHelper.UsageTable.insert(a2,"2021-01-01 13:04:27","2021-01-01 13:06:07");
AltDBHelper.UsageTable.insert(a2,"2021-01-03 12:51:51","2021-01-03 12:55:55");
AltDBHelper.UsageTable.insert(a2,"2021-01-02 03:33:30","2021-01-02 03:33:35");
AltDBHelper.UsageTable.insert(a2,"2021-01-02 01:01:01","2021-01-02 01:03:04");
AltDBHelper.UsageTable.insert(a2,"2021-01-01 14:25:36","2021-01-01 15:26:37");
/* Add just App3 with no usage data */
long a3 = altdb.insert("App3");
/* Get the Daily App Usage */
Cursor csr = altdb.getAppDailyStatsAsCursor(null);
DatabaseUtils.dumpCursor(csr); /* Dump the Cursor (just to show what the Cursor contains) */
csr.close(); /* done with the Cursor so close it */
}
}
- Как видно, исходный код, показывающий, как подключить базу данных, был оставлен.
Когда вышеописанное запускается в первый раз (не предназначено для повторного запуска, так как это просто демонстрация), дамп курсора выглядит следующим образом :-
2021-05-30 18:29:23.739 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@9ba4107
2021-05-30 18:29:23.742 I/System.out: 0 {
2021-05-30 18:29:23.742 I/System.out: date=2021-01-01
2021-05-30 18:29:23.743 I/System.out: appname=App1
2021-05-30 18:29:23.743 I/System.out: unlocks=2
2021-05-30 18:29:23.743 I/System.out: elapsedtime=02:41:38
2021-05-30 18:29:23.744 I/System.out: }
2021-05-30 18:29:23.744 I/System.out: 1 {
2021-05-30 18:29:23.744 I/System.out: date=2021-01-01
2021-05-30 18:29:23.744 I/System.out: appname=App2
2021-05-30 18:29:23.745 I/System.out: unlocks=2
2021-05-30 18:29:23.745 I/System.out: elapsedtime=01:02:41
2021-05-30 18:29:23.745 I/System.out: }
2021-05-30 18:29:23.746 I/System.out: 2 {
2021-05-30 18:29:23.746 I/System.out: date=2021-01-02
2021-05-30 18:29:23.746 I/System.out: appname=App1
2021-05-30 18:29:23.747 I/System.out: unlocks=1
2021-05-30 18:29:23.747 I/System.out: elapsedtime=00:04:01
2021-05-30 18:29:23.747 I/System.out: }
2021-05-30 18:29:23.747 I/System.out: 3 {
2021-05-30 18:29:23.748 I/System.out: date=2021-01-02
2021-05-30 18:29:23.748 I/System.out: appname=App2
2021-05-30 18:29:23.749 I/System.out: unlocks=2
2021-05-30 18:29:23.749 I/System.out: elapsedtime=00:02:08
2021-05-30 18:29:23.749 I/System.out: }
2021-05-30 18:29:23.749 I/System.out: 4 {
2021-05-30 18:29:23.750 I/System.out: date=2021-01-03
2021-05-30 18:29:23.750 I/System.out: appname=App1
2021-05-30 18:29:23.750 I/System.out: unlocks=1
2021-05-30 18:29:23.750 I/System.out: elapsedtime=05:00:59
2021-05-30 18:29:23.751 I/System.out: }
2021-05-30 18:29:23.751 I/System.out: 5 {
2021-05-30 18:29:23.751 I/System.out: date=2021-01-03
2021-05-30 18:29:23.751 I/System.out: appname=App2
2021-05-30 18:29:23.751 I/System.out: unlocks=1
2021-05-30 18:29:23.752 I/System.out: elapsedtime=00:04:04
2021-05-30 18:29:23.752 I/System.out: }
2021-05-30 18:29:23.752 I/System.out: <<<<<
Комментарии:
1. Это лучший ответ, который я когда-либо видел в stack overflow. Спасибо за подробный ответ, это сработало, но мне потребовалось некоторое время, чтобы понять весь код.