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数据库中的任何列,整数主列除外 键列,可用于存储任何存储类的值
- 注意:尽管类型定义在很大程度上是不相关的,但它可能与用于存储数据的类型(存储类型)以及通过typeof函数返回的结果相关。因此,可能需要很好地理解
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();