Android SQLite - 连接两个表并将结果呈现为列表
Posted
技术标签:
【中文标题】Android SQLite - 连接两个表并将结果呈现为列表【英文标题】:Android SQLite - Joining two tables and render the result as list 【发布时间】:2020-04-01 14:00:33 【问题描述】:我在 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();
【问题讨论】:
像 ProjectName_Array.add(cursor.getString(0) 一样,你必须小心选择列序列 【参考方案1】:我在渲染上述内容时需要帮助。如何将光标返回的值直接呈现为列表,而不是引用表中的实际列。
简而言之,您不能使用标准 API(如果使用替代方案而不是 Cursor 您将拥有一个结果集,似乎不会更困难)。所以游标就是你从中得到结果的东西。
查看您的代码,您可能会因为试图从光标中获取不存在的值而感到困惑。
通过使用SELECT projectName, OutletName FROM .....
,您会得到一个类似(2 列)的光标:-
您是说您希望光标中有 2 列名称分别为 projectName 和 OutletName。尝试使用cursor.getColumnIndex(SQLiteHelper.Table1_Column_ID)
将导致返回-1(在光标中找不到列名)并且光标中没有偏移量为-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 有一个错误,即它不依赖于大小写。因此,列名应与查询中使用的列名完全相同。
检查从任何 SQLiteDatabase 方法返回的游标是否为空是没有用的。将始终返回有效的光标。如果没有行,则光标的计数将为 0,并且任何试图 移动 到第一行 (-1) 之前 以外的位置的任何尝试都将返回 false(因此如何while 循环有效)。
通常建议使用 query 便捷方法,而不是 rawQuery 方法(但在上述情况下都可以使用)。
注意以上代码为in-principle代码;它尚未运行或测试,因此可能包含一些错误。
然而因为您似乎想要驱动 ListView,那么如果您使用 CursorAdapter 的子类,大部分工作都可以由适配器代表您完成。
SimpleCursorAdapter,这样的适配器,非常灵活,允许布局显示多列。 CursorAdapters 的显着优势之一是 ItemSelection (Click LongClick) 事件将 id 传递给方法(对于非 CursorAdapters,传递的值是作为 long 的位置)。为此,游标 必须 有一列特别是名称 _id (Base.Columns._ID
) 并且该列应该是 rowid 的别名(即表中的列已定义使用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);
结果
【讨论】:
感谢您的详细回答......它确实有助于澄清我不知道的领域。我仍然想知道为什么您的代码版本没有select
选项。我会努力回来的。
@Apricot 我仍在制定答案,因为您可能很喜欢 CursorAdapter(例如 SimpleCursorAdapter)的想法。该版本假定光标已根据 SELECT 进行设置,如下所述约 1/3,即cursor = sqLiteDatabase.rawQuery("select projects.id,pid,projectName, OutletName from projects inner join outlets on outlets.pid = projects.id where outlets.Status = 1"+"", null).
@Apricot 答案包括一个基于您的代码使用 SimpleCursorAdapter 的示例。
我对您的详细信息感到惊讶,非常感谢您....为您所做的所有努力....非常感谢...我会检查并回复您。
再次非常感谢...我正在尝试简单的光标适配器解决方案,但我无法让它工作...目前我不知道我在哪里犯了错误,因为我刚刚开始学习 java 和android...但是您的showProjectStatus
函数的第一个解决方案有效。以上是关于Android SQLite - 连接两个表并将结果呈现为列表的主要内容,如果未能解决你的问题,请参考以下文章
如何在我的 Android 应用程序中加入两个 SQLite 表?