已验证的现有列上的 SQLite 数据库“没有此类列”错误

Posted

技术标签:

【中文标题】已验证的现有列上的 SQLite 数据库“没有此类列”错误【英文标题】:SQLite database "No such column" error on verified existing column 【发布时间】:2021-12-01 10:20:21 【问题描述】:

我遇到了“SQLiteException:没有这样的列:”错误。我正在尝试编写一些代码来检查数据库中是否存在一个项目,然后再存储它,我不知道这是否是最好的方法,但它可以完成这项工作。或者不是真的,当我将所有数字用于我正在搜索的特定列中的数据时,它工作正常。但是,如果列数据中有任何字母,它就会崩溃。

主活动

private ConversationsDatabaseHelper db;
//onCreate stuff here
db = new ConversationsDatabaseHelper(this);
String threadId = "886";
Log.d(TAG, "dbTest: EXISTS; " + db.conversationExists(threadId));

databaseHelper 类中是 converstionExists 函数

public boolean conversationExists(String threadId) 
// Select All Query
String selectQuery = "SELECT  * FROM " + Conversations.TABLE_NAME + " WHERE " +
        Conversations.COLUMN_THREAD_ID + " LIKE " + threadId;

SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
boolean returnValue = false;
// Check if this message exists
if (cursor.moveToFirst()) 
    returnValue = true;

// close db connection
db.close();
return returnValue;

因此,如果我使用例如“886”作为 threadId 值,那么一切都很好。如果我创建一个匹配 threadId 的行,那么它返回 true。在这种情况下,我没有那么错。 堆栈跟踪...

dbTest:存在;假的

但使用 a886 会导致

原因:android.database.sqlite.SQLiteException:没有这样的列:a886(代码 1 SQLITE_ERROR):,编译时:SELECT * FROM conversations WHERE thread_id LIKE a886

而 88a6 导致了这个

 Caused by: android.database.sqlite.SQLiteException: unrecognized token: "88a6" (code 1 SQLITE_ERROR): , while compiling: SELECT  * FROM conversations WHERE thread_id LIKE 88a6

看起来混合字母和数字可能是这里的部​​分原因,但不应该是因为该列是为保存 TEXT 数据类型而创建的。这是数据库创建表查询字符串。

    public static final String TABLE_NAME = "conversations";

public static final String COLUMN_ID = "id";
public static final String COLUMN__ID = "_id";
public static final String COLUMN_GROUP_ID = "group_id";
public static final String COLUMN_LAST_MESSAGE_ID = "last_message_id";
public static final String COLUMN_THREAD_ID = "thread_id";
public static final String COLUMN_ADDRESS = "address";
public static final String COLUMN_CONTACT = "contact";
public static final String COLUMN_BODY = "body";
public static final String COLUMN_DATE = "date";
public static final String COLUMN_TYPE = "type";
public static final String COLUMN_STATE = "state";
public static final String COLUMN_READ = "read";
public static final String COLUMN_STATUS = "status";
public static final String COLUMN_CT = "ct";    
// Create table SQL query
public static final String CREATE_TABLE =
        "CREATE TABLE " + TABLE_NAME + "("
                + COLUMN_ID + " INTEGER PRIMARY KEY ,"
                + COLUMN_LAST_MESSAGE_ID + " TEXT,"
                + COLUMN__ID + " TEXT,"
                + COLUMN_GROUP_ID + " TEXT,"
                + COLUMN_THREAD_ID + " TEXT,"
                + COLUMN_ADDRESS + " TEXT,"
                + COLUMN_CONTACT + " TEXT,"
                + COLUMN_BODY + " TEXT,"
                + COLUMN_DATE + " TEXT,"
                + COLUMN_TYPE + " TEXT,"
                + COLUMN_STATE + " TEXT,"
                + COLUMN_READ + " TEXT,"
                + COLUMN_STATUS + " TEXT,"
                + COLUMN_CT + " TEXT"
                + ")";

我对此感到“大吃一惊”,我们将不胜感激。

【问题讨论】:

我建议您了解 Room API。这使得使用 SQLite 数据库比编写原始 html 更容易。 看看this article关于查询表是否存在。 【参考方案1】:

值 a886 应该是文本/字符串文字而不是数字文字。因此,它应该用单引号括起来。错误是因为:-

a886 失败,未找到任何列,因为它被视为列名,因为它不是文字。

虽然 88a6 首先不是有效的文字(由于 a),因此是列名,但随后是无效的列(不能以数字开头,除非包含在内)名称,因此不是已知的标记。

见Literal Values (Constants)

您可以使用(将 threadId 括在单引号中)来解决此问题:-

String selectQuery = "SELECT  * FROM " + Conversations.TABLE_NAME + " WHERE " +
    Conversations.COLUMN_THREAD_ID + " LIKE '" + threadId + "'";

但是,建议使用绑定参数来防止 SQL 注入。

因此建议使用:-

String selectQuery = "SELECT  * FROM " + Conversations.TABLE_NAME + " WHERE " +
        Conversations.COLUMN_THREAD_ID + " LIKE ?";

还有:-

Cursor cursor = db.rawQuery(selectQuery, new String[]threadId);
即这 ?替换为正确包含的 threadId 值。

您可能希望考虑使用方便的query 方法而不是 rawQuery,这将是:-

Cursor cursor = db.query(Conversations.TABLE_NAME,null,Conversations.COLUMN_THREAD_ID + " LIKE ?", new String[]theadId,null,null, null);
SQL 专为您构建。

看起来混合字母和数字可能是这里的部​​分原因,但不应该是因为该列是为保存 TEXT 数据类型而创建的。

SQlite 以任何类型存储任何值都没有问题。类型本身几乎可以是任何东西(规则用于确定结果类型),它只是将要存储的值的指示。唯一的例外是 rowid 或 rowid 的别名必须是整数值(您的 id 列是 rowid 列的别名)

【讨论】:

注释强调:使用绑定参数,而不是字符串连接,以避免SQL注入攻击。 感谢这解决了问题。此外,我不相信我会受到任何可能的注入攻击,因为没有一个变量是用户可访问的。线程 id 是从 SMS thead_id 列中获取的,据我所知,用户无法编辑该列。同时,你永远不能太安全。再次感谢,这真的帮助了我。

以上是关于已验证的现有列上的 SQLite 数据库“没有此类列”错误的主要内容,如果未能解决你的问题,请参考以下文章

在生产数据库中减少列大小并修剪数据,处理同一列上的约束/依赖关系

postgresql中多个列上的多个索引与单个索引

Azure 数据库上的 Sqlite 已锁定

如何向现有 SQLite 表添加外键?

SQLite进阶-10.约束

Pandas:如何在现有 DataFrame 的列上设置索引?