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

我想渲染projectnameoutletname,其中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 列名称分别为 projectNameOutletName。尝试使用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连接表

在 SQLite 中连接来自不同表的两个字段

如何在我的 Android 应用程序中加入两个 SQLite 表?

使用内连接 SQLITE 从两个表中删除行

在 android 应用程序中从 Sqlite 数据库中读取数据。

Android studio 连接SQLite数据库 +创建数据库+创建数据库表