Android 空结果的Cursor.getType抛出错误

Android 空结果的Cursor.getType抛出错误,android,sqlite,android-studio,android-cursor,Android,Sqlite,Android Studio,Android Cursor,当我尝试下面的代码时,我发现res.getType(I)抛出一个空错误(但现有表除外): 我已经读过了,但我的问题没有答案。我编写这段代码的目的是根据给定的表名获取列信息。sqlite有,列类型取决于数据值。尝试在没有数据的情况下推断列类型是行不通的 您可以删除列类型以仅使用列名,也可以通过查询获取创建表时使用的SQL。来自@laalto的精彩答案。 接下来,您只能从sqlite\u mastertable中获取创建表查询 使用 其中table_name是您的表名 我想到的另一个明显的解决方案是

当我尝试下面的代码时,我发现
res.getType(I)
抛出一个空错误(但现有表除外):

我已经读过了,但我的问题没有答案。我编写这段代码的目的是根据给定的表名获取列信息。

sqlite有,列类型取决于数据值。尝试在没有数据的情况下推断列类型是行不通的


您可以删除列类型以仅使用列名,也可以通过查询获取创建表时使用的SQL。

来自@laalto的精彩答案。
接下来,您只能从
sqlite\u master
table中获取创建表查询 使用

其中table_name是您的表名

我想到的另一个明显的解决方案是在当前查询中使用限制1

String query = "SELECT * FROM " + tableName + " limit 1";

此查询的光标指向某些数据,然后您可以找到列数据类型,但空值除外。

您的问题是,当没有要读取的行时,您正在尝试读取第0行(第一行)

这是因为您没有检查是否有要读取的行。您可以使用光标的
getCount()
方法进行检查,也可以检查光标的
move???
方法的结果

i、 e.如果无法移动,大多数
move???
方法将返回false。e、 你可以使用
if(res.moveToFirst()){..完成你的工作}或者{..如果需要,不处理任何行}

然而,考虑到你的评论:- 我们的目的是获取关于专栏的信息,所以我们不需要 需要数据。出于这个原因,最好的where子句是1=2,它总是 false(对于包含任何列的任何表)

然后,您可以使用
table_info
PRAGMA语句确定列类型,该语句不需要从sqlite_master获取的SQL中提取列类型

您可以使用通用/通用方法,例如:-

public Cursor getTableInfo(String table) {
    return this.getWritableDatabase().rawQuery("PRAGMA table_info(" + table + ")",null);
}
private String determineColumnAffinity(String columntype) {
    String uc = columntype.toUpperCase();
    //rule 1
    if (uc.indexOf("INT") > -1) {
        return "INTEGER";
    }
    //rule 2
    if ((uc.indexOf("CHAR") > -1) || (uc.indexOf("CLOB") > -1) || (uc.indexOf("TEXT") > -1)) {
        return "TEXT";
    }
    //rule 3
    if ((uc.length() < 1) || (uc.indexOf("BLOB") > -1)) {
        return "BLOB";
    }
    if ((uc.indexOf("REAL") > -1) || (uc.indexOf("FLOA") > -1) || (uc.indexOf("DOUB") > -1)) {
        return "REAL";
    }
    return "NUMERIC";
}
然后调用这个,作为一个例子,使用如下内容:-

public class MainActivity extends AppCompatActivity {

    public static final String col_table_info_cid = "cid";
    public static final String col_table_info_name = "name";
    public static final String col_table_info_type = "type";
    public static final String col_table_info_notnull = "notnull";
    public static final String col_table_info_default_value = "dflt_value";
    public static final String col_table_info_primary_key = "pk";

    DBHelper mDBHlpr;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDBHlpr = new DBHelper(this);
        String table_to_look_at = "gameinfo"; //<<<<< The table to look at
        Cursor csr = mDBHlpr.getTableInfo(table_to_look_at);
        StringBuilder sb = new StringBuilder("Columns for Table " + table_to_look_at);
        while (csr.moveToNext()) {
            sb.append("\n\tColumn Name=")
                    .append(csr.getString(csr.getColumnIndex(col_table_info_name)))
                    .append(" Column Type=")
                    .append(csr.getString(csr.getColumnIndex(col_table_info_type)))
            ;
        }
        Log.d("TABLE INFO",sb.toString());
}
但是这有什么用呢? 从上面的结果中可以看出,怪异列的列类型为rumplestiltskin。除非您查看,否则这意味着很少,这将导致列关联性为数值(应用规则5,其他规则不适用)

您可以使用一个例程来确定列的类型关联,例如:-

public Cursor getTableInfo(String table) {
    return this.getWritableDatabase().rawQuery("PRAGMA table_info(" + table + ")",null);
}
private String determineColumnAffinity(String columntype) {
    String uc = columntype.toUpperCase();
    //rule 1
    if (uc.indexOf("INT") > -1) {
        return "INTEGER";
    }
    //rule 2
    if ((uc.indexOf("CHAR") > -1) || (uc.indexOf("CLOB") > -1) || (uc.indexOf("TEXT") > -1)) {
        return "TEXT";
    }
    //rule 3
    if ((uc.length() < 1) || (uc.indexOf("BLOB") > -1)) {
        return "BLOB";
    }
    if ((uc.indexOf("REAL") > -1) || (uc.indexOf("FLOA") > -1) || (uc.indexOf("DOUB") > -1)) {
        return "REAL";
    }
    return "NUMERIC";
}
您将得到以下结果:-

然而,即使如此,这可能也没有什么用处,因为当提到这些小宝石时,可以找到:-

  • SQLite使用更通用的动态类型系统。在SQLite中 值的数据类型与值本身关联,而不是与值的 容器

  • 但是,SQLite中的动态类型允许它执行 在传统的严格类型化数据库中是不可能的

  • SQLite版本3数据库中的任何列,整数主列除外 键列,可用于存储任何存储类的值

因此,在上面使用的gameinfo表中,除了\u id列(它是rowid列的别名,因此只能存储整数)之外,任何类型的值都可以存储在任何类型的列中,因此列类型重要性的定义在很大程度上是无关的

例如,下面是一个有效(尽管可能没有用处)表的示例,该表显示了存储在列中的不同数据类型(数据类型是彩色编码的):-

  • 注意:尽管类型定义在很大程度上是不相关的,但它可能与用于存储数据的类型(存储类型)以及通过typeof函数返回的结果相关。因此,可能需要很好地理解
补充意见:- 我的意思是直接从Sqlite查询是获取列的唯一方法 信息

可以从空游标获取列名,但无法获取列类型,因为这需要访问行中的列

e、 g.以下措施将起作用:-

    Cursor csr2 = mDBHlpr.getAllRows(); // get rows from empty table
    csr2.moveToFirst();
    sb = new StringBuilder("Columns for Table");
    for(int i=0; i < (csr2.getColumnCount());i++) {
        int ctype = 100; //<<<<<<<<<< NOT A VALID COLUMN TYPE
        //ctype = csr2.getType(i); //<<<<<<<<<< cannot get the type unless there is a row
        String type = "unknown";
        switch (ctype) {
            case Cursor.FIELD_TYPE_FLOAT:
                type= "REAL";
                break;
             case Cursor.FIELD_TYPE_NULL:
                 type = "NULL";
                 break;
             case Cursor.FIELD_TYPE_INTEGER:
                 type = "INTEGER";
                 break;
             case Cursor.FIELD_TYPE_STRING:
                 type = "TEXT";
                 break;
            case Cursor.FIELD_TYPE_BLOB:
                type = "BLOB";
                break;
        }
        sb.append("\n\tColumn Name=").append(csr2.getColumnName(i))
                .append(" Column Type=")
                .append(type)
                .append(" Column Affinity=")
                .append(determineColumnAffinity(type));
    }
    Log.d("TABLE INFO 2",sb.toString());
    csr2.close();
Cursor csr2=mDBHlpr.getAllRows();//从空表中获取行
csr2.moveToFirst();
sb=新的StringBuilder(“表的列”);
对于(int i=0;i<(csr2.getColumnCount());i++){

int ctype=100;//在您的SQL语句中,我看到
其中1=2
1
是一个列的名称?这是为了不从DB获取任何数据,仅此而已,是完全正常的。谢谢您的解释。我认为是错误,因为它们没有行,错误表示
大小为0
,索引为0
。您需要检查
moveToFirst()
返回true以执行您的语句,否则它们会出现错误。添加if语句。感谢您的回复。您能分享一些代码吗?谢谢您的精彩回答。
getTableInfo
在任何android版本中都能工作吗?我的意思是直接从
Sqlite
查询是获取列信息的唯一方法?它肯定可以追溯到最早的版本API 16(上面是在16日测试的)。它应该涵盖SQLite3的所有版本,因为它是在2001-10-09年引入的(SQLIte 2.0.2)。不保证它会按照特定的pragma语句保留。在SQLite的未来版本中,可能会删除和添加其他语句。不保证向后兼容。但是,可能不太可能删除它(由于保持向后兼容)。我的意思是,直接从Sqlite查询是获取列信息的唯一方法?Drat将检查并忘记这一点。我们将对此进行研究。您的第二个解决方案对空表不起作用。
private String determineColumnAffinity(String columntype) {
    String uc = columntype.toUpperCase();
    //rule 1
    if (uc.indexOf("INT") > -1) {
        return "INTEGER";
    }
    //rule 2
    if ((uc.indexOf("CHAR") > -1) || (uc.indexOf("CLOB") > -1) || (uc.indexOf("TEXT") > -1)) {
        return "TEXT";
    }
    //rule 3
    if ((uc.length() < 1) || (uc.indexOf("BLOB") > -1)) {
        return "BLOB";
    }
    if ((uc.indexOf("REAL") > -1) || (uc.indexOf("FLOA") > -1) || (uc.indexOf("DOUB") > -1)) {
        return "REAL";
    }
    return "NUMERIC";
}
        sb.append("\n\tColumn Name=")
                .append(csr.getString(csr.getColumnIndex(col_table_info_name)))
                .append(" Column Type=")
                .append(csr.getString(csr.getColumnIndex(col_table_info_type)))
                .append(" Column Affinity=")
                .append(determineColumnAffinity(csr.getString(csr.getColumnIndex(col_table_info_type))))
        ;
07-24 22:59:57.770 1408-1408/ga.gamesapp D/TABLE INFO: Columns for Table gameinfo
      Column Name=_id Column Type=INTEGER Column Affinity=INTEGER
      Column Name=name Column Type=TEXT Column Affinity=TEXT
      Column Name=category Column Type=TEXT Column Affinity=TEXT
      Column Name=games Column Type=TEXT Column Affinity=TEXT
      Column Name=weird Column Type=rumplestiltskin Column Affinity=NUMERIC
    Cursor csr2 = mDBHlpr.getAllRows(); // get rows from empty table
    csr2.moveToFirst();
    sb = new StringBuilder("Columns for Table");
    for(int i=0; i < (csr2.getColumnCount());i++) {
        int ctype = 100; //<<<<<<<<<< NOT A VALID COLUMN TYPE
        //ctype = csr2.getType(i); //<<<<<<<<<< cannot get the type unless there is a row
        String type = "unknown";
        switch (ctype) {
            case Cursor.FIELD_TYPE_FLOAT:
                type= "REAL";
                break;
             case Cursor.FIELD_TYPE_NULL:
                 type = "NULL";
                 break;
             case Cursor.FIELD_TYPE_INTEGER:
                 type = "INTEGER";
                 break;
             case Cursor.FIELD_TYPE_STRING:
                 type = "TEXT";
                 break;
            case Cursor.FIELD_TYPE_BLOB:
                type = "BLOB";
                break;
        }
        sb.append("\n\tColumn Name=").append(csr2.getColumnName(i))
                .append(" Column Type=")
                .append(type)
                .append(" Column Affinity=")
                .append(determineColumnAffinity(type));
    }
    Log.d("TABLE INFO 2",sb.toString());
    csr2.close();