代码之家  ›  专栏  ›  技术社区  ›  Yashar Aliabbasi

游标.getType空结果引发错误

  •  2
  • Yashar Aliabbasi  · 技术社区  · 7 年前

    当我尝试下面的代码时,我发现 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 但我的问题没有答案。我编写这段代码的目的是基于给定的表名获取列信息。

    3 回复  |  直到 7 年前
        1
  •  2
  •   laalto    7 年前

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

    您可以删除列类型以只使用列名或查询 sqlite_master

        2
  •  1
  •   Man    7 年前

    来自@laalto的精彩回答。
    接下来,只能从 sqlite_master 桌子 使用

    SELECT sql FROM sqlite_master where type = 'table' and tbl_name = 'table_name'
    

    表格名称 是您的表名。

    我想到的另一个明显的解决办法是 限制1 使用当前查询。

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

    这个查询的光标指向一些数据,然后您可以找到列数据类型,除了空值。

        3
  •  1
  •   MikeT    7 年前

    getCount() 方法或通过检查游标的 move???? 方法。

    i、 e.大多数 移动???? 如果无法进行移动,则方法将返回false。e、 你可以用 if (res.moveToFirst()) { ..... do your stuff } else { .... handle no rows if needed } .

    然而,考虑到你的评论:-

    我们的目的是获取有关列的信息 需要数据。因此,最佳where子句是1=2,它总是

    然后,可以使用 table_info PRAGMA statment,它不需要从SQL中提取列类型,可以从sqlite\u master获得。

    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
    

    但是这有什么用吗?

    奇怪的 列类型为 黑精灵 3.1. Determination Of Column Affinity ,这将导致列亲和力为 数字 ( 规则 5 ).

    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版本3数据库中的任何列,但整数主列除外

    因此,在上面使用的gameinfo表中,除了 _身份证

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

    enter image description here

    补充评论:-

    信息?

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

        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();
    

    但是删除注释掉的行将导致 android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0 异常,如果表(因此游标)为空。

    • 0,因为理论上光标已移到第一行。
    • 如果没有csr2.moveToFirst(),那么索引将是-1
      • (即光标位于第一行之前)