Android SQLite - 连接两个表并将结果呈现为列表
Android SQLite - Joining two tables and render the result as list
我在 sqlite 中有两个表:
projects
---id
---projectname
outlets
---id
---pid
---outletname
---status
我想渲染 projectname
和 outletname
,其中 status
满足条件。我想将结果输出呈现为列表。我正在使用以下代码:
private void showProjectStatus() {
sqLiteDatabase = sqLiteHelper.getWritableDatabase();
cursor = sqLiteDatabase.rawQuery("select projectName, OutletName from projects inner join outlets on outlets.pid = projects.id where outlets.Status = 1"+"", null);
//cursor = sqLiteDatabase.rawQuery("SELECT * FROM '"+SQLiteHelper.TABLE_NAME1+"' where Status = 1"+"", null);
上面的光标完美运行。
ID_Array.clear();
ProjectName_Array.clear();
OutletName_Array.clear();
if (cursor != null && cursor.getCount() > 0) {
if (cursor.moveToFirst()) {
do {
ID_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_ID)));
OutletName_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_PID)));
ProjectName_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_OutletName)));
我需要帮助来渲染上面的内容。如何将游标返回的值直接呈现为列表而不是引用表中的实际列。
} while (cursor.moveToNext());
}
} else {
Toast.makeText( this, "No data to display!", Toast.LENGTH_SHORT ).show();
}
我需要帮助来渲染上面的内容。如何将游标 return 编辑的值直接呈现为列表,而不是引用 table 中的实际列。
简而言之,您不能使用标准的 API(使用替代方法似乎不会更困难,因为您将得到一个结果集而不是 Cursor)。因此,您可以从 Cursor 获得结果。
查看您的代码,您可能会感到困惑,因为您正试图从 Cursor 获取不存在的值。
通过使用 SELECT projectName, OutletName FROM .....
你会得到一个类似(2 列)的 Cursor :-
您是说您想要 Cursor 中的 2 列,名称为 projectName 和 OutletName。尝试使用 cursor.getColumnIndex(SQLiteHelper.Table1_Column_ID)
将导致 -1 被 returned(在 Cursor 中找不到列名)并且 Cursor 中没有偏移量为 -1 的列。
PID 也一样。
如果除了 outletName 和 projectName 列之外还需要 ID 和 PID 列,则将查询更改为:-
cursor = sqLiteDatabase.rawQuery("select projects.id,pid,projectName, OutletName from projects inner join outlets on outlets.pid = projects.id where outlets.Status = 1"+"", null).
在这种情况下,光标会像(4 列):-
假设您想要所有四列,在单独的数组中进行 exach,然后可能考虑:-
private void showProjectStatus() {
ID_Array.clear();
ProjectName_Array.clear();
OutletName_Array.clear();
sqLiteDatabase = sqLiteHelper.getWritableDatabase();
Cursor cursor = sqliteDatabase.query(
"projects inner join outlets on outlets.pid = projects.id",
new String[]{
"projects." + SQLiteHelper.Table1_Column_ID,
SQLiteHelper.Table1_Column_PID,
"projectName", //<<<<< defined value not used as unknown
SQLiteHelper.Table1_Column_OutletName
},
"outlets.Status=1",
null,null,null,null
);
while (cursor.moveToNext()) {
ID_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_ID));
ProjectName_Array.add(cursor.getString(cursor.getColumnIndex("projectName"));
OutletName_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_OutletName));
}
cursor.close()
}
- 注意 SQLiteHelper.Table1_Column_ProjectName 或 SQLiteHelper.Table1_Column_projectName 或任何可能被定义为尚未使用的值。
- 应该注意 Cursor getColumnIndex has/had 一个错误,因为它不依赖于大小写。因此,列名应该与查询中使用的列名完全相同。
- 检查从任何 SQLiteDatabase 方法中输入的 Cursor return 是否为 null 是没有用的。有效的 Cursor 将始终被 returned。如果没有行,游标的计数将为 0,并且任何尝试 将 移动到第一行 (-1) 之前 以外的位置将 return false(因此 while 循环如何工作)。
- 通常建议使用 query 便捷方法而不是 rawQuery 方法(但是在上面都可以使用)。
注意以上代码为原理代码;它尚未 运行 或测试,因此可能包含一些错误。
However 因为您似乎想要驱动 ListView 那么如果您使用 CursorAdapter[ 的子类=81=],适配器可以代您完成大部分工作。
SimpleCursorAdapter 这样的适配器非常灵活,允许布局显示多列。 CursorAdapters 的显着优势之一是 ItemSelection(单击 LongClick)事件将 id 传递给方法(对于非 CursorAdapters,传递的值是 long 的位置)。为此,游标 必须 有一个列名称为 _id (Base.Columns._ID
) 并且该列应该是 rowid (即 table 中的列是使用 INTEGER PRIMARY KEY
(可选地使用 AUTOINCREMENT)定义的。
SimpleCursorAdapter 示例
SQLiteHelper.java
public class SQLiteHelper extends SQLiteOpenHelper {
public static final String DBNAME = "mydb";
public static final int DBVERSION = 1;
public static final String TABLE_NAME1 = "projects";
public static final String TABLE_NAME2 = "outlets";
public static final String TABLE1_COLUMN_ID = BaseColumns._ID;
public static final String TABLE1_COLUMN_PROJECTNAME = "projectname";
public static final String TABLE2_COLUMN_ID = BaseColumns._ID;
public static final String TABLE2_COLUMN_PID = "pid";
public static final String TABLE2_COLUMN_OUTLETNAME = "outletname";
public static final String TABLE2_COLUMN_STATUS = "status";
public SQLiteHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
}
@Override
public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createProjectsTableSQL =
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME1 +
"(" +
TABLE1_COLUMN_ID + " INTEGER PRIMARY KEY," +
TABLE1_COLUMN_PROJECTNAME + " TEXT" +
")";
String createOutletsTableSQL =
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME2 +
"(" +
TABLE2_COLUMN_ID + " INTEGER PRIMARY KEY," +
TABLE2_COLUMN_PID + " INTEGER REFERENCES " + TABLE_NAME1 + "(" +
TABLE1_COLUMN_ID +
") ON DELETE CASCADE ON UPDATE CASCADE, " +
TABLE2_COLUMN_OUTLETNAME + " TEXT," +
TABLE2_COLUMN_STATUS + " INTEGER" +
")";
db.execSQL(createProjectsTableSQL);
db.execSQL(createOutletsTableSQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public long addProject(String projectName) {
ContentValues cv = new ContentValues();
cv.put(TABLE1_COLUMN_PROJECTNAME,projectName);
return this.getWritableDatabase().insert(TABLE_NAME1,null,cv);
}
public long addOutlet(String outletName, long projectId, boolean status) {
ContentValues cv = new ContentValues();
cv.put(TABLE2_COLUMN_OUTLETNAME,outletName);
cv.put(TABLE2_COLUMN_PID,projectId);
cv.put(TABLE2_COLUMN_STATUS,status);
return this.getWritableDatabase().insert(TABLE_NAME2,null,cv);
}
public Cursor getProjectsWithOutletsWithStatusOn() {
return this.getWritableDatabase().query(
TABLE_NAME1 + " INNER JOIN "
+ TABLE_NAME2 + " ON " +
TABLE_NAME1 + "." + TABLE1_COLUMN_ID + "=" +
TABLE_NAME2 + "." + TABLE2_COLUMN_PID,
new String[]{
TABLE_NAME1 + "." + TABLE1_COLUMN_ID,
TABLE2_COLUMN_PID,
TABLE1_COLUMN_PROJECTNAME,
TABLE2_COLUMN_OUTLETNAME,
TABLE2_COLUMN_STATUS //? optional as will always be 1
},
TABLE2_COLUMN_STATUS + "=1",
null,null,null,null
);
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
SQLiteHelper mHelper;
Cursor mCursor;
SimpleCursorAdapter mAdapter;
ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = this.findViewById(R.id.listview);
mHelper = new SQLiteHelper(this);
addSomeDataIfNone();
manageListView();
}
@Override
protected void onDestroy() {
super.onDestroy();
mCursor.close(); // close the Cursor (more important for invoked activities)
}
@Override
protected void onResume() {
super.onResume();
manageListView(); // refresh Listview when resuming activity as data may have changed
}
private void addSomeDataIfNone() {
if (DatabaseUtils.queryNumEntries(mHelper.getWritableDatabase(),SQLiteHelper.TABLE_NAME1) < 1) {
long idp1 = mHelper.addProject("Project001");
mHelper.addOutlet("OutletP1001",idp1,false);
mHelper.addOutlet("OutletP1002",idp1,true);
mHelper.addOutlet("Outletp1003",idp1,true);
long idp2 = mHelper.addProject("Prject002");
mHelper.addOutlet("OutletP2001",idp2,true);
mHelper.addOutlet("OutletP2002",idp2,false);
}
}
private void manageListView() {
mCursor = mHelper.getProjectsWithOutletsWithStatusOn();
if (mAdapter == null) {
mAdapter = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_2,
mCursor,
new String[]{
SQLiteHelper.TABLE1_COLUMN_PROJECTNAME,
SQLiteHelper.TABLE2_COLUMN_OUTLETNAME
},
new int[]{
android.R.id.text1,
android.R.id.text2
},
0
);
mListView.setAdapter(mAdapter);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(view.getContext(),
"You clicked " +
"Project = " + mCursor.getString(mCursor.getColumnIndex(SQLiteHelper.TABLE1_COLUMN_PROJECTNAME)) +
" Outlet = " + mCursor.getString(mCursor.getColumnIndex(SQLiteHelper.TABLE2_COLUMN_OUTLETNAME)) +
" Project ID = " + mCursor.getString(mCursor.getColumnIndex(SQLiteHelper.TABLE1_COLUMN_ID)) +
" Outlet Status = " + (mCursor.getInt(mCursor.getColumnIndex(SQLiteHelper.TABLE2_COLUMN_STATUS))> 0)
,
Toast.LENGTH_SHORT).show();
}
});
} else {
mAdapter.swapCursor(mCursor);
}
}
}
结果
我在 sqlite 中有两个表:
projects
---id
---projectname
outlets
---id
---pid
---outletname
---status
我想渲染 projectname
和 outletname
,其中 status
满足条件。我想将结果输出呈现为列表。我正在使用以下代码:
private void showProjectStatus() {
sqLiteDatabase = sqLiteHelper.getWritableDatabase();
cursor = sqLiteDatabase.rawQuery("select projectName, OutletName from projects inner join outlets on outlets.pid = projects.id where outlets.Status = 1"+"", null);
//cursor = sqLiteDatabase.rawQuery("SELECT * FROM '"+SQLiteHelper.TABLE_NAME1+"' where Status = 1"+"", null);
上面的光标完美运行。
ID_Array.clear();
ProjectName_Array.clear();
OutletName_Array.clear();
if (cursor != null && cursor.getCount() > 0) {
if (cursor.moveToFirst()) {
do {
ID_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_ID)));
OutletName_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_PID)));
ProjectName_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_OutletName)));
我需要帮助来渲染上面的内容。如何将游标返回的值直接呈现为列表而不是引用表中的实际列。
} while (cursor.moveToNext());
}
} else {
Toast.makeText( this, "No data to display!", Toast.LENGTH_SHORT ).show();
}
我需要帮助来渲染上面的内容。如何将游标 return 编辑的值直接呈现为列表,而不是引用 table 中的实际列。
简而言之,您不能使用标准的 API(使用替代方法似乎不会更困难,因为您将得到一个结果集而不是 Cursor)。因此,您可以从 Cursor 获得结果。
查看您的代码,您可能会感到困惑,因为您正试图从 Cursor 获取不存在的值。
通过使用 SELECT projectName, OutletName FROM .....
你会得到一个类似(2 列)的 Cursor :-
您是说您想要 Cursor 中的 2 列,名称为 projectName 和 OutletName。尝试使用 cursor.getColumnIndex(SQLiteHelper.Table1_Column_ID)
将导致 -1 被 returned(在 Cursor 中找不到列名)并且 Cursor 中没有偏移量为 -1 的列。
PID 也一样。
如果除了 outletName 和 projectName 列之外还需要 ID 和 PID 列,则将查询更改为:-
cursor = sqLiteDatabase.rawQuery("select projects.id,pid,projectName, OutletName from projects inner join outlets on outlets.pid = projects.id where outlets.Status = 1"+"", null).
在这种情况下,光标会像(4 列):-
假设您想要所有四列,在单独的数组中进行 exach,然后可能考虑:-
private void showProjectStatus() {
ID_Array.clear();
ProjectName_Array.clear();
OutletName_Array.clear();
sqLiteDatabase = sqLiteHelper.getWritableDatabase();
Cursor cursor = sqliteDatabase.query(
"projects inner join outlets on outlets.pid = projects.id",
new String[]{
"projects." + SQLiteHelper.Table1_Column_ID,
SQLiteHelper.Table1_Column_PID,
"projectName", //<<<<< defined value not used as unknown
SQLiteHelper.Table1_Column_OutletName
},
"outlets.Status=1",
null,null,null,null
);
while (cursor.moveToNext()) {
ID_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_ID));
ProjectName_Array.add(cursor.getString(cursor.getColumnIndex("projectName"));
OutletName_Array.add(cursor.getString(cursor.getColumnIndex(SQLiteHelper.Table1_Column_OutletName));
}
cursor.close()
}
- 注意 SQLiteHelper.Table1_Column_ProjectName 或 SQLiteHelper.Table1_Column_projectName 或任何可能被定义为尚未使用的值。
- 应该注意 Cursor getColumnIndex has/had 一个错误,因为它不依赖于大小写。因此,列名应该与查询中使用的列名完全相同。
- 检查从任何 SQLiteDatabase 方法中输入的 Cursor return 是否为 null 是没有用的。有效的 Cursor 将始终被 returned。如果没有行,游标的计数将为 0,并且任何尝试 将 移动到第一行 (-1) 之前 以外的位置将 return false(因此 while 循环如何工作)。
- 通常建议使用 query 便捷方法而不是 rawQuery 方法(但是在上面都可以使用)。
注意以上代码为原理代码;它尚未 运行 或测试,因此可能包含一些错误。
However 因为您似乎想要驱动 ListView 那么如果您使用 CursorAdapter[ 的子类=81=],适配器可以代您完成大部分工作。
SimpleCursorAdapter 这样的适配器非常灵活,允许布局显示多列。 CursorAdapters 的显着优势之一是 ItemSelection(单击 LongClick)事件将 id 传递给方法(对于非 CursorAdapters,传递的值是 long 的位置)。为此,游标 必须 有一个列名称为 _id (Base.Columns._ID
) 并且该列应该是 rowid (即 table 中的列是使用 INTEGER PRIMARY KEY
(可选地使用 AUTOINCREMENT)定义的。
SimpleCursorAdapter 示例
SQLiteHelper.java
public class SQLiteHelper extends SQLiteOpenHelper {
public static final String DBNAME = "mydb";
public static final int DBVERSION = 1;
public static final String TABLE_NAME1 = "projects";
public static final String TABLE_NAME2 = "outlets";
public static final String TABLE1_COLUMN_ID = BaseColumns._ID;
public static final String TABLE1_COLUMN_PROJECTNAME = "projectname";
public static final String TABLE2_COLUMN_ID = BaseColumns._ID;
public static final String TABLE2_COLUMN_PID = "pid";
public static final String TABLE2_COLUMN_OUTLETNAME = "outletname";
public static final String TABLE2_COLUMN_STATUS = "status";
public SQLiteHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
}
@Override
public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createProjectsTableSQL =
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME1 +
"(" +
TABLE1_COLUMN_ID + " INTEGER PRIMARY KEY," +
TABLE1_COLUMN_PROJECTNAME + " TEXT" +
")";
String createOutletsTableSQL =
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME2 +
"(" +
TABLE2_COLUMN_ID + " INTEGER PRIMARY KEY," +
TABLE2_COLUMN_PID + " INTEGER REFERENCES " + TABLE_NAME1 + "(" +
TABLE1_COLUMN_ID +
") ON DELETE CASCADE ON UPDATE CASCADE, " +
TABLE2_COLUMN_OUTLETNAME + " TEXT," +
TABLE2_COLUMN_STATUS + " INTEGER" +
")";
db.execSQL(createProjectsTableSQL);
db.execSQL(createOutletsTableSQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public long addProject(String projectName) {
ContentValues cv = new ContentValues();
cv.put(TABLE1_COLUMN_PROJECTNAME,projectName);
return this.getWritableDatabase().insert(TABLE_NAME1,null,cv);
}
public long addOutlet(String outletName, long projectId, boolean status) {
ContentValues cv = new ContentValues();
cv.put(TABLE2_COLUMN_OUTLETNAME,outletName);
cv.put(TABLE2_COLUMN_PID,projectId);
cv.put(TABLE2_COLUMN_STATUS,status);
return this.getWritableDatabase().insert(TABLE_NAME2,null,cv);
}
public Cursor getProjectsWithOutletsWithStatusOn() {
return this.getWritableDatabase().query(
TABLE_NAME1 + " INNER JOIN "
+ TABLE_NAME2 + " ON " +
TABLE_NAME1 + "." + TABLE1_COLUMN_ID + "=" +
TABLE_NAME2 + "." + TABLE2_COLUMN_PID,
new String[]{
TABLE_NAME1 + "." + TABLE1_COLUMN_ID,
TABLE2_COLUMN_PID,
TABLE1_COLUMN_PROJECTNAME,
TABLE2_COLUMN_OUTLETNAME,
TABLE2_COLUMN_STATUS //? optional as will always be 1
},
TABLE2_COLUMN_STATUS + "=1",
null,null,null,null
);
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
SQLiteHelper mHelper;
Cursor mCursor;
SimpleCursorAdapter mAdapter;
ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = this.findViewById(R.id.listview);
mHelper = new SQLiteHelper(this);
addSomeDataIfNone();
manageListView();
}
@Override
protected void onDestroy() {
super.onDestroy();
mCursor.close(); // close the Cursor (more important for invoked activities)
}
@Override
protected void onResume() {
super.onResume();
manageListView(); // refresh Listview when resuming activity as data may have changed
}
private void addSomeDataIfNone() {
if (DatabaseUtils.queryNumEntries(mHelper.getWritableDatabase(),SQLiteHelper.TABLE_NAME1) < 1) {
long idp1 = mHelper.addProject("Project001");
mHelper.addOutlet("OutletP1001",idp1,false);
mHelper.addOutlet("OutletP1002",idp1,true);
mHelper.addOutlet("Outletp1003",idp1,true);
long idp2 = mHelper.addProject("Prject002");
mHelper.addOutlet("OutletP2001",idp2,true);
mHelper.addOutlet("OutletP2002",idp2,false);
}
}
private void manageListView() {
mCursor = mHelper.getProjectsWithOutletsWithStatusOn();
if (mAdapter == null) {
mAdapter = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_2,
mCursor,
new String[]{
SQLiteHelper.TABLE1_COLUMN_PROJECTNAME,
SQLiteHelper.TABLE2_COLUMN_OUTLETNAME
},
new int[]{
android.R.id.text1,
android.R.id.text2
},
0
);
mListView.setAdapter(mAdapter);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(view.getContext(),
"You clicked " +
"Project = " + mCursor.getString(mCursor.getColumnIndex(SQLiteHelper.TABLE1_COLUMN_PROJECTNAME)) +
" Outlet = " + mCursor.getString(mCursor.getColumnIndex(SQLiteHelper.TABLE2_COLUMN_OUTLETNAME)) +
" Project ID = " + mCursor.getString(mCursor.getColumnIndex(SQLiteHelper.TABLE1_COLUMN_ID)) +
" Outlet Status = " + (mCursor.getInt(mCursor.getColumnIndex(SQLiteHelper.TABLE2_COLUMN_STATUS))> 0)
,
Toast.LENGTH_SHORT).show();
}
});
} else {
mAdapter.swapCursor(mCursor);
}
}
}