当我尝试下面的代码时,我发现res.getType(i)
会为空(但存在现有表)抛出错误:
private HashMap<String,ColumnTypeEnum> getTableColumns(String tableName){
String query = "SELECT * FROM " + tableName + " WHERE 1=2";
Cursor res = db.rawQuery(query, null);
res.moveToFirst();//I removed this part but I got no result too
int columnCount = res.getColumnCount();
String[] columNames = res.getColumnNames();
HashMap<String, ColumnTypeEnum> columns = new HashMap<>();
for (int i = 0; i < columnCount; i++) {
columns.put(columNames[i], ColumnTypeEnum.getEnum(res.getType(i)));
}
res.close();
}
控制台输出为:
W/System.err: android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0
W/System.err: at android.database.AbstractCursor.checkPosition(AbstractCursor.java:426)
W/System.err: at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
at android.database.AbstractWindowedCursor.getType(AbstractWindowedCursor.java:130)
W/System.err: at backend.Database.DB.getTableColumns(DB.java:198)
W/System.err: at backend.Database.DB.insert(DB.java:151)
W/System.err: at backend.BaseActivity.BaseActivity.insert(BaseActivity.java:264)
W/System.err: at backend.WebView.Controller.SimpleController.InsertToDB(SimpleController.java:47)
at com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
W/System.err: at com.android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:24)
W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102)
W/System.err: at android.os.Looper.loop(Looper.java:146)
at android.os.HandlerThread.run(HandlerThread.java:61)
我已经读过android cursor guide,但是我的问题没有答案。我编写此代码的目的是根据给定的表名获取列信息。
答案 0 :(得分:2)
sqlite具有dynamic typing,列类型取决于数据值。试图推断没有数据的列类型将不起作用。
您可以删除列类型以仅包含列名,或查询sqlite_master
以获得创建表时使用的SQL。
答案 1 :(得分:1)
@laalto的精彩回答。
之后,您只能从sqlite_master
表中获取创建表查询
使用
SELECT sql FROM sqlite_master where type = 'table' and tbl_name = 'table_name'
其中 table_name 是您的表名。
我想到的另一个显而易见的解决方案是在当前查询中使用 limit 1 。
String query = "SELECT * FROM " + tableName + " limit 1";
此查询的光标指向某些数据,然后您可以找到列数据类型,但显然空值除外。
答案 2 :(得分:1)
您的问题是,当没有要读取的行时,您正在尝试读取第0行(第一行)。
这是因为您没有检查是否有要读取的行。您可以通过使用Cursor的getCount()
方法或通过检查Cursor的move????
方法的结果进行检查。
即如果无法进行移动,大多数move????
方法将返回false。例如您可以使用if (res.moveToFirst()) { ..... do your stuff } else { .... handle no rows if needed }
。
我们的目的是获取有关列的公正信息,因此我们不会 需要数据。因此,最好where子句为1 = 2 false(对于具有任何列的任何表)
然后,您可以使用table_info
PRAGMA语句来确定列类型,该语句不需要从可从sqlite_master获得的SQL中提取列类型。
您可以使用通用/通用方法,例如:-
public Cursor getTableInfo(String table) {
return this.getWritableDatabase().rawQuery("PRAGMA table_info(" + table + ")",null);
}
然后使用例如:-
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());
}
以上可能导致:-
07-24 22:27:19.842 1255-1255/ga.gamesapp D/TABLE INFO: Columns for Table gameinfo Column Name=_id Column Type=INTEGER Column Name=name Column Type=TEXT Column Name=category Column Type=TEXT Column Name=games Column Type=TEXT Column Name=weird Column Type=rumplestiltskin
从上面的结果列中可以看到,奇怪的列类型为 rumplestiltskin 。这意味着几乎没有什么意义,除非您查看3.1. Determination Of Column Affinity,否则列亲和力为 NUMERIC (由于没有其他规则适用,因此应用了规则 5 < / em>)。
您可以使用例程来确定列的类型相似性,例如:-
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
但是,即使如此,这仍然没什么用,因为再次引用Datatypes In SQLite Version 3时,可以找到这些小宝石:-
SQLite使用更通用的动态类型系统。在SQLite中, 值的数据类型与值本身而不是与其值相关联 容器。
但是,SQLite中的动态类型允许它执行以下操作: 在传统的严格类型数据库中是不可能的。
SQLite版本3数据库中的任何列,但INTEGER PRIMARY除外 KEY列,可用于存储任何存储类的值。
因此,在上面使用的gameinfo表中,除了 _id 列(它是rowid列的别名(因此只能存储INTEGER))之外,任何类型的值可以存储在任何类型的列中,因此,如果该列类型的重要性很大程度上不相关,则定义。
例如,以下是一个有效的表的示例,尽管该表可能无用,但该表显示了存储在列中的不同数据类型(数据类型使用颜色编码):
我的意思是直接从Sqlite查询是获取列的唯一方法 信息吗?
您可以从一个空的游标中获取列名,但无法获取列类型,因为这需要访问行中的列。
例如以下将起作用:-
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)为空,则删除注释掉的行将导致android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0
异常。
因此,除非表中没有数据,否则您无法直接使用SQLite来确定列类型