如何使用带有 sqlite 的 sqlstatement 在数据库中随机检索多个元素?

How can I retrieve multiple elements by random in a database using sqlstatement with sqlite?

我正在尝试使用包含两个 table 的数据库创建一个测验应用程序,一个用于主题,一个用于问题。 topicsID 是我的问题 table 中的外键。我想从每个 topicID 中检索 2 个随机问题,共有 7 个主题,到目前为止,我只尝试检索一个主题,但不知道如何在同一函数中检索其余主题。

有趣的 getAllQuestions(): ArrayList {

    val qList = ArrayList<MathQuestions>()
    val db: SQLiteDatabase = this.readableDatabase
    var sqlStatement = "SELECT * FROM $QuestionTableName WHERE $QTopicColumn_ID = '1' ORDER BY RAND() LIMIT 2"
    val cursor: Cursor = db.rawQuery(sqlStatement, null)

    if (cursor.moveToFirst())
        do {
            val id: Int = cursor.getInt(0)
            val tId: Int = cursor.getInt(1)
            val question: String = cursor.getString(2)
            val option1: String = cursor.getString(3)
            val option2: String = cursor.getString(4)
            val option3: String = cursor.getString(5)
            val optionR: String = cursor.getString(6)


            val p = MathQuestions(id, tId, question, option1, option2, option3, optionR)
            qList.add(p)
        } while (cursor.moveToNext())

    cursor.close()
    db.close()

    return qList
}

一种方法是递归 CTE,它将循环遍历您的主题,然后可以使用循环中的每个主题在 union all 中执行 ORDER BY RAND() LIMIT 2 查询。

CTE - Common Table Expressions

您可以使用 UNION 组合多个 SELECTS。

  • 但是,ORDER BY 和 LIMIT 遵循 UNION(即 SELECT 整体)按照 :-

    • In a compound SELECT, all the constituent SELECTs must return the same number of result columns. As the components of a compound SELECT must be simple SELECT statements, they may not contain ORDER BY or LIMIT clauses. ORDER BY and LIMIT clauses may only occur at the end of the entire compound SELECT, and then only if the final element of the compound is not a VALUES clause.

  • 因此您需要嵌入子查询中的 ORDER 和 LIMIT,它们为 SELECT/UNION 中的 IN 子句提供值。每个 SELECT/UNION select 2 个随机问题的 rowid 列的子查询驱动 select 由 SELECT/UNION 因此允许每个 select.

    的 ORDER BY AND LIMIT

以下是我认为您想要的,另外还按主题订购了整个集合:-

val SQLStatment = "SELECT * FROM $QuestionTableName WHERE rowid IN (SELECT rowid FROM $QuestionTableName WHERE $QTopicColumn_ID = '1'  ORDER BY random() LIMIT 2)\n" +
                "UNION SELECT * FROM $QuestionTableName WHERE rowid IN (SELECT rowid FROM $QuestionTableName WHERE $QTopicColumn_ID = '2' ORDER BY random() LIMIT 2)\n" +
                "UNION SELECT * FROM $QuestionTableName WHERE rowid IN (SELECT rowid FROM $QuestionTableName WHERE $QTopicColumn_ID = '3' ORDER BY random() LIMIT 2)\n" +
                "UNION SELECT * FROM $QuestionTableName WHERE rowid IN (SELECT rowid FROM $QuestionTableName WHERE $QTopicColumn_ID = '4' ORDER BY random() LIMIT 2)\n" +
                "UNION SELECT * FROM $QuestionTableName WHERE rowid IN (SELECT rowid FROM $QuestionTableName WHERE $QTopicColumn_ID = '5' ORDER BY random() LIMIT 2)\n" +
                "UNION SELECT * FROM $QuestionTableName WHERE rowid IN (SELECT rowid FROM $QuestionTableName WHERE $QTopicColumn_ID = '6' ORDER BY random() LIMIT 2)\n" +
                "UNION SELECT * FROM $QuestionTableName WHERE rowid IN (SELECT rowid FROM $QuestionTableName WHERE $QTopicColumn_ID = '7' ORDER BY random() LIMIT 2)\n" +
                "ORDER BY $QTopicColumn_ID /* OPTIONAL if you want the rows sorted according to topic */ ;" +
                ";"

额外

以下是对以上内容的改编,适用于任意数量的主题(显然 0 个主题将导致 0 个 rows/questions)。添加和删​​除主题不需要更改代码:-

@SuppressLint("Range")
fun getAllQuestions(orderBy: String?): ArrayList<MathQuestions> {

    val qList = ArrayList<MathQuestions>()
    val db: SQLiteDatabase = this.readableDatabase
    var sb: StringBuilder = StringBuilder() // Going to use a StringBuilder to build the SELECT/UNION statements
    /* extract unique topic ID's i.e. the number of topics */
    var csr = db.rawQuery("SELECT DISTINCT $T_IDColumn FROM $TopicTableName",null)
    var afterFirst = false // first topic will not have UNION keyword
    var currentTopicId: Int // variable for the current topic
    // Loop through extracted topic ID's (no need to moveToFirst Do While as moveToNext will return false if move not possible)
    while (csr.moveToNext()) {
        // If not the first row then add the UNION keyword
        if (afterFirst) {
            sb.append("UNION ")
        }
        afterFirst = true // from now on not the first row
        currentTopicId = csr.getInt(csr.getColumnIndex(QTopicColumn_ID)) // get the topic id
        // Add the appropriate SELECT for the current topic
        sb.append("SELECT * FROM $QuestionTableName WHERE rowid IN (SELECT rowid FROM $QuestionTableName WHERE $QTopicColumn_ID = '$currentTopicId'  ORDER BY random() LIMIT 2) ")
    }
    /* If provided add the ORDER BY clause with the specified order column */
    if (orderBy != null && orderBy.isNotEmpty()) {
        sb.append(" ORDER BY $orderBy" )
    }
    sb.append(";") /* add end of statement (not required) */
    /* execute the built query */
    csr = db.rawQuery(sb.toString(),null)
    while (csr.moveToNext()) {
        /* add to the results directly to the arraylist */
        qList.add(
            MathQuestions(
                csr.getInt(csr.getColumnIndex(QIDColumn)), /* rather than hard code column index, get them according to the extracted column names */
                csr.getInt(csr.getColumnIndex(QTopicColumn_ID)),
                csr.getString(csr.getColumnIndex(QQuestionColumn)),
                csr.getString(csr.getColumnIndex(QOption1Column)),
                csr.getString(csr.getColumnIndex(QOption2Column)),
                csr.getString(csr.getColumnIndex(QOption3Column)),
                csr.getString(csr.getColumnIndex(QOptionRightColumn))
            )
        )
    }
    csr.close() // Close the cursor. No need to close the database (inefficient to open and close the database)
    return qList
}