SelectArgs 不替换?与 SQLITE Android 中 CTE 的实际值

问题描述 投票:0回答:1

我在哪里使用 CTE?需要替换为查询中的实际值:

val itemCursor: Cursor
                val rdb = openHelper.writableDatabase
                itemCursor = rdb.rawQuery(
                    menuItemsQuery(selection, sortOrder, projection),
                    selectionArgs
                )
                itemCursor.setNotificationUri(context.contentResolver, uri)
                return itemCursor

其中 menuItemsQuery 有我的 SQL 查询,其中包含 CTE(公用表表达式)

WITH cteI AS ( SELECT " +
                buildProjection(projection, "item.") +
                ",b." + InventoryContract.MenuItem.NAME + " AS " + Item.MENU_NAME +
                " FROM " + Item.CONTENT_DIRECTORY + " INNER JOIN " +
                InventoryContract.MenuItem.CONTENT_DIRECTORY + " AS b ON " +
                Item.CONTENT_DIRECTORY + "." + Item.UUID + " = b." +
                InventoryContract.MenuItem.ITEM_UUID + " WHERE ( " + selection + " ) )

上面的查询只是整个查询的CTE部分,其中selection包含

我尝试更换?与@但没有运气

android sqlite common-table-expression
1个回答
0
投票

选择包含?

这很可能取决于选择还包含什么。

如果

?
被解释为绑定的占位符,则另一个依赖项将是 “运气不佳”。它不区分非预期结果和异常等。

当然,如果放置正确,

?
占位符可以在 CTE 中使用。下面演示了它的成功使用:-

示范

首先考虑以下内容(满足所有待确定的可变因素(您的问题使大多数问题未得到解决)):-

const val DATABASE_NAME = "the_database.db"
const val DATABASE_VERSION = 1
const val TABLE_A = "a"
const val COLUMN_A_ID = "${TABLE_A}_${BaseColumns._ID}"
const val COLUMN_A_C1 = "${TABLE_A}_c1"
const val COLUMN_A_C2 = "${TABLE_A}_c2"
const val TABLE_B = "b"
const val COLUMN_B_ID = "${TABLE_B}_${BaseColumns._ID}"
const val COLUMN_B_C1 = "${TABLE_B}_c1"
const val COLUMN_B_C2 = "${TABLE_B}_c2"
const val COLUMN_B_MAPTO_A_ID = "${TABLE_B}_mapto_$COLUMN_A_ID"

class OpenHelper: SQLiteOpenHelper {
    override fun onCreate(db: SQLiteDatabase?) {
        db!!.execSQL(
            "CREATE TABLE IF NOT EXISTS $TABLE_A (" +
                "${COLUMN_A_ID} INTEGER PRIMARY KEY" +
                ",${COLUMN_A_C1} TEXT " +
                ",${COLUMN_A_C2} TEXT " +
                ");"
        )
        db!!.execSQL(
            "CREATE TABLE IF NOT EXISTS ${TABLE_B} (" +
                    "${COLUMN_B_ID} INTEGER PRIMARY KEY" +
                    ",${COLUMN_B_C1} TEXT" +
                    ",${COLUMN_B_C2} TEXT" +
                    ",${COLUMN_B_MAPTO_A_ID} INTEGER" +
                    ");"
        )
    }

    override fun onUpgrade(db: SQLiteDatabase?, fromVersion: Int, toVersion: Int) {
    }

    constructor(context: Context) : super(context, DATABASE_NAME, null, DATABASE_VERSION) {
    }

    fun getItemCursor(stringToUseInWhereClause: String): Cursor {
        val selection = "?"
        val query = "WITH cte1 AS (SELECT " +
                "* FROM ${TABLE_A} " +
                "JOIN ${TABLE_B} ON ${TABLE_B}.${COLUMN_B_MAPTO_A_ID} = ${TABLE_A}.${COLUMN_A_ID} " +
                "WHERE ${COLUMN_B_C1} LIKE " + selection +
                "||'%'" +
                ")" +
                "SELECT * FROM cte1;"
        Log.d("DBINFO","GIC Query passed to SQLite is:-\n\t$query")
        return this.writableDatabase.rawQuery(query, arrayOf(stringToUseInWhereClause))
    }

    fun getItemCursorOther(stringToUseInWhereClause: String): Cursor {
        val query = "WITH cte1 AS (SELECT " +
                "* FROM ${TABLE_A} " +
                "JOIN ${TABLE_B} ON ${TABLE_B}.${COLUMN_B_MAPTO_A_ID} = ${TABLE_A}.${COLUMN_A_ID} " +
                "WHERE ${COLUMN_B_C1} LIKE ?||'%'" +
                ")" +
                "SELECT * FROM cte1;"
        Log.d("DBINFO","GICO Query passed to SQLite is:-\n\t$query")
        return this.writableDatabase.rawQuery(query, arrayOf(stringToUseInWhereClause))
    }
    fun insertA(id:Long?,c1: String, c2: String): Long {
        val cv = ContentValues()
        if (id != null) {
            cv.put(COLUMN_A_ID,id)
        }
        cv.put(COLUMN_A_C1,c1)
        cv.put(COLUMN_A_C2,c2)
        return this.writableDatabase.insert(TABLE_A,null,cv)
    }
    fun insertB(id: Long?, c1: String, c2: String, mapToAId: Long): Long {
        val cv = ContentValues()
        if (id != null) {
            cv.put(COLUMN_B_ID,id)
        }
        cv.put(COLUMN_B_C1,c1)
        cv.put(COLUMN_B_C2,c2)
        cv.put(COLUMN_B_MAPTO_A_ID,mapToAId)
        return this.writableDatabase.insert(TABLE_B,null,cv)
    }
}
  • getItemCursor
    和等效的
    getItemCursorOther
    函数有 1 个细微差别;第一个使用变量 selection,其中包含占位符 (
    ?
    ),而第二个则对占位符进行硬编码。

    • 两者都在 CTE 的 WHERE 子句内。
  • 要绑定的实际术语/值(替换占位符)通过

    stringToUseInWhereClause
    传递给函数。

  • 其余代码,只是让数据库实际使用并进行演示。

显然代码本身并不能证明什么。因此,为了实际演示占位符被替换为一些活动代码:-

  1. 添加一些数据
  2. 通过 CTE 提取一些数据 .1 用于潜在调试传递给 SQLite 的完整查询也被输出
  3. 显示已提取的数据

活动代码:-

class MainActivity : AppCompatActivity() {
    lateinit var openHelper: OpenHelper
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        openHelper = OpenHelper(this)
        val aid = openHelper.insertA(null,"c1blah","c2blah")
        openHelper.insertB(100,"something","something",aid)
        openHelper.insertB(null,"some thing but not really","something but not really",aid)
        openHelper.insertB(null,"somethingelse","somethingelse",aid)
        openHelper.insertB(null,"somethingelse","somethingelse",999)
        val c1 = openHelper.getItemCursor("something")
        DatabaseUtils.dumpCursor(c1)
        val c2 = openHelper.getItemCursorOther("something")
        DatabaseUtils.dumpCursor(c2)
        val c3 = openHelper.getItemCursor("")
        DatabaseUtils.dumpCursor(c3)
        val c4 = openHelper.getItemCursorOther("")
        DatabaseUtils.dumpCursor(c4)
        c1.close()
        c2.close()
        c3.close()
        c4.close()
    }
}
  • c1 和 c2 将仅包含表 b 中的 c1 列以某些内容开头的行,而 c3 和 c4 将根据连接拥有所有行,因为条件实际上是“%”(任何内容)。

日志的最终输出为:-

2024-01-17 09:45:29.080 D/DBINFO: GIC Query passed to SQLite is:-
        WITH cte1 AS (SELECT * FROM a JOIN b ON b.b_mapto_a__id = a.a__id WHERE b_c1 LIKE ?||'%')SELECT * FROM cte1;
2024-01-17 09:45:29.080 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@8962cfb
2024-01-17 09:45:29.081 I/System.out: 0 {
2024-01-17 09:45:29.081 I/System.out:    a__id=1
2024-01-17 09:45:29.082 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.082 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.082 I/System.out:    b__id=100
2024-01-17 09:45:29.082 I/System.out:    b_c1=something
2024-01-17 09:45:29.082 I/System.out:    b_c2=something
2024-01-17 09:45:29.082 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.082 I/System.out: }
2024-01-17 09:45:29.082 I/System.out: 1 {
2024-01-17 09:45:29.082 I/System.out:    a__id=1
2024-01-17 09:45:29.082 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.082 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.082 I/System.out:    b__id=102
2024-01-17 09:45:29.082 I/System.out:    b_c1=somethingelse
2024-01-17 09:45:29.082 I/System.out:    b_c2=somethingelse
2024-01-17 09:45:29.082 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.082 I/System.out: }
2024-01-17 09:45:29.082 I/System.out: <<<<<


2024-01-17 09:45:29.082 D/DBINFO: GICO Query passed to SQLite is:-
        WITH cte1 AS (SELECT * FROM a JOIN b ON b.b_mapto_a__id = a.a__id WHERE b_c1 LIKE ?||'%')SELECT * FROM cte1;
2024-01-17 09:45:29.083 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@ec7d118
2024-01-17 09:45:29.084 I/System.out: 0 {
2024-01-17 09:45:29.084 I/System.out:    a__id=1
2024-01-17 09:45:29.084 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.084 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.084 I/System.out:    b__id=100
2024-01-17 09:45:29.084 I/System.out:    b_c1=something
2024-01-17 09:45:29.084 I/System.out:    b_c2=something
2024-01-17 09:45:29.084 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.084 I/System.out: }
2024-01-17 09:45:29.085 I/System.out: 1 {
2024-01-17 09:45:29.085 I/System.out:    a__id=1
2024-01-17 09:45:29.085 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.085 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.086 I/System.out:    b__id=102
2024-01-17 09:45:29.086 I/System.out:    b_c1=somethingelse
2024-01-17 09:45:29.086 I/System.out:    b_c2=somethingelse
2024-01-17 09:45:29.086 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.086 I/System.out: }
2024-01-17 09:45:29.086 I/System.out: <<<<<


2024-01-17 09:45:29.086 D/DBINFO: GIC Query passed to SQLite is:-
        WITH cte1 AS (SELECT * FROM a JOIN b ON b.b_mapto_a__id = a.a__id WHERE b_c1 LIKE ?||'%')SELECT * FROM cte1;
2024-01-17 09:45:29.086 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@d598771
2024-01-17 09:45:29.087 I/System.out: 0 {
2024-01-17 09:45:29.088 I/System.out:    a__id=1
2024-01-17 09:45:29.088 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.088 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.088 I/System.out:    b__id=100
2024-01-17 09:45:29.088 I/System.out:    b_c1=something
2024-01-17 09:45:29.088 I/System.out:    b_c2=something
2024-01-17 09:45:29.088 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.088 I/System.out: }
2024-01-17 09:45:29.088 I/System.out: 1 {
2024-01-17 09:45:29.088 I/System.out:    a__id=1
2024-01-17 09:45:29.088 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.089 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.089 I/System.out:    b__id=101
2024-01-17 09:45:29.089 I/System.out:    b_c1=some thing but not really
2024-01-17 09:45:29.091 I/System.out:    b_c2=something but not really
2024-01-17 09:45:29.091 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.091 I/System.out: }
2024-01-17 09:45:29.091 I/System.out: 2 {
2024-01-17 09:45:29.092 I/System.out:    a__id=1
2024-01-17 09:45:29.092 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.092 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.092 I/System.out:    b__id=102
2024-01-17 09:45:29.092 I/System.out:    b_c1=somethingelse
2024-01-17 09:45:29.093 I/System.out:    b_c2=somethingelse
2024-01-17 09:45:29.093 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.093 I/System.out: }
2024-01-17 09:45:29.093 I/System.out: <<<<<


2024-01-17 09:45:29.093 D/DBINFO: GICO Query passed to SQLite is:-
        WITH cte1 AS (SELECT * FROM a JOIN b ON b.b_mapto_a__id = a.a__id WHERE b_c1 LIKE ?||'%')SELECT * FROM cte1;
2024-01-17 09:45:29.094 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@8c94956
2024-01-17 09:45:29.095 I/System.out: 0 {
2024-01-17 09:45:29.095 I/System.out:    a__id=1
2024-01-17 09:45:29.095 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.095 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.095 I/System.out:    b__id=100
2024-01-17 09:45:29.096 I/System.out:    b_c1=something
2024-01-17 09:45:29.096 I/System.out:    b_c2=something
2024-01-17 09:45:29.096 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.096 I/System.out: }
2024-01-17 09:45:29.096 I/System.out: 1 {
2024-01-17 09:45:29.096 I/System.out:    a__id=1
2024-01-17 09:45:29.096 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.096 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.096 I/System.out:    b__id=101
2024-01-17 09:45:29.096 I/System.out:    b_c1=some thing but not really
2024-01-17 09:45:29.096 I/System.out:    b_c2=something but not really
2024-01-17 09:45:29.096 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.096 I/System.out: }
2024-01-17 09:45:29.096 I/System.out: 2 {
2024-01-17 09:45:29.097 I/System.out:    a__id=1
2024-01-17 09:45:29.097 I/System.out:    a_c1=c1blah
2024-01-17 09:45:29.097 I/System.out:    a_c2=c2blah
2024-01-17 09:45:29.097 I/System.out:    b__id=102
2024-01-17 09:45:29.097 I/System.out:    b_c1=somethingelse
2024-01-17 09:45:29.097 I/System.out:    b_c2=somethingelse
2024-01-17 09:45:29.097 I/System.out:    b_mapto_a__id=1
2024-01-17 09:45:29.097 I/System.out: }
2024-01-17 09:45:29.097 I/System.out: <<<<<
  • 即正如预期的那样,前两个仅提取 2 行,其中 b_c1 以 something 开头,而后两个都提取与 JOIN 匹配的所有 3 行。

因此

?
占位符必须替换为selectionArgs参数中的值,因此您遇到的问题不是由于?无法用选择参数中的值替换,而是在其他地方(由于问题的模糊性实际上无法确定)。但是,演示确实包含一些调试代码,在尝试确定实际问题时可能会有用。

© www.soinside.com 2019 - 2024. All rights reserved.