【问题标题】:Trouble with SQLite foreign key syntax using listviews to retrieve data使用列表视图检索数据的 SQLite 外键语法问题
【发布时间】:2019-04-04 13:02:29
【问题描述】:

这是上下文。我有一个数据库助手,可以在一个数据库中创建 3 个表。其中两个表有外键。一切正常,一切都很好,但考虑到我想做的事情,我需要使用列表视图而不是文本视图来显示我的数据并能够编辑、删除等。出于某种原因,列表视图在 Android 上与其他编程语言不同。我想出了适配器和所有的废话。我的新问题是我的应用程序不会显示数据,当我检查我的 logcat 时,我收到了这条消息

 Caused by: java.lang.IllegalArgumentException: column '_id' does not exist. Available columns: [id, termName, termStart, termEnd]

这是针对一个名为“term_table”的表。所以我将 id 从 'id' 更改为 '_id' 就成功了。

所以一切都很好,对吧?不,因为其他两个表有自己的 ID 和外键。当我对名为“assess_table”的不同表执行相同的查询时,我得到了同样的错误,所以我将“id”更改为“_id”。问题是“courses_table”(第三个表)使用“_id”作为外键来引用名为“term_table”的表。所以我收到一条错误消息,告诉我我正在创建重复的列 - 主键 '_id' 和外键 '_id' 来引用 term_table。

我将“courses_table”的 id 从“_id”重命名为“courses_id”。现在我回到第一方得到同样的错误:

 Caused by: java.lang.IllegalArgumentException: column '_id' does not exist. Available columns: [assess_id, assessName, assessDueDate, assessType, courses_id]

所以基本上如果所有三个表都必须命名为 _id 如果外键与引用表的主键具有相同的名称,我应该在创建外键时使用什么语法,所以我总是会得到创建重复列错误。我读到这个 ​​_id 命名方案只是游标适配器的事情。什么语法适合使用?

这是我的数据库助手中的 3 个表

//terms table
   public static final String TERM_TABLE_NAME = "term_table";
   public static final String COL_TERM1 = "_id";
   public static final String COL_TERM2 = "termName";
   public static final String COL_TERM3 = "termStart";
   public static final String COL_TERM4 = "termEnd";
   public static final String TERM_DATABASE_NAME = "term.db";
   //courses table
   public static final String COURSES_TABLE_NAME = "courses_table";
   public static final String COL_COURSE1 = "courses_id";
   public static final String COL_COURSE2 = "courseTitle";
   public static final String COL_COURSE3 = "courseStartDate";
   public static final String COL_COURSE4 = "courseEndDate";
   public static final String COL_COURSE5 = "courseStatus";
   public static final String COL_COURSE6 = "optionalNote";
   public static final String COL_COURSE7 = "mentorName";
   public static final String COL_COURSE8 = "mentorPhone";
   public static final String COL_COURSE9 = "mentorEmail";
   public static final String COL_COURSE_TERMID = "_id";
   //assess table
   public static final String ASSESS_TABLE_NAME = "assess_table";
   public static final String COL_ASSESS1 = "assess_id";
   public static final String COL_ASSESS2 = "assessName";
   public static final String COL_ASSESS3 = "assessDueDate";
   public static final String COL_ASSESS4 = "assessType";
   public static final String COL_ASSESS_COURSEID = "courses_id";

这是创建语法

public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TERM_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, termName TEXT, termStart TEXT, termEnd TEXT)");
        db.execSQL("CREATE TABLE " + COURSES_TABLE_NAME + " (courses_id INTEGER PRIMARY KEY AUTOINCREMENT, courseTitle TEXT, courseStartDate TEXT, courseEndDate TEXT, courseStatus INTEGER, optionalNote TEXT, mentorName TEXT, mentorPhone TEXT, mentorEmail TEXT, _id INTEGER, FOREIGN KEY (_id) REFERENCES term_table(_id))");
        db.execSQL("CREATE TABLE " + ASSESS_TABLE_NAME + " (assess_id INTEGER PRIMARY KEY AUTOINCREMENT, assessName TEXT, assessDueDate TEXT, assessType INTEGER, courses_id INTEGER, FOREIGN KEY (courses_id) REFERENCES courses_table(courses_id))");
    }

【问题讨论】:

  • FK 是引用列列表的列列表。第 n 个引用列不必与第 n 个引用列同名。 PS请在代码问题中给出minimal reproducible example--剪切&粘贴&可运行代码加上所需的输出加上清晰的规范和解释。给出导致原始错误的代码——这就是你给出的代码的作用吗?

标签: java android sqlite foreign-keys


【解决方案1】:

像这样重命名主键和外键:

public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TERM_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, termName TEXT, termStart TEXT, termEnd TEXT)");
        db.execSQL("CREATE TABLE " + COURSES_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, courseTitle TEXT, courseStartDate TEXT, courseEndDate TEXT, courseStatus INTEGER, optionalNote TEXT, mentorName TEXT, mentorPhone TEXT, mentorEmail TEXT, for_cors_term INTEGER, FOREIGN KEY (for_cors_term) REFERENCES term_table(_id))");
        db.execSQL("CREATE TABLE " + ASSESS_TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, assessName TEXT, assessDueDate TEXT, assessType INTEGER, for_ases_cors INTEGER, FOREIGN KEY (for_ases_cors) REFERENCES courses_table(_id))");
    }

   public static final String TERM_DATABASE_NAME = "term.db";

   //terms table
   public static final String TERM_TABLE_NAME = "term_table";
   public static final String COL_TERM1 = "_id"; //primary key
   public static final String COL_TERM2 = "termName";
   public static final String COL_TERM3 = "termStart";
   public static final String COL_TERM4 = "termEnd";

   //courses table
   public static final String COURSES_TABLE_NAME = "courses_table";
   public static final String COL_COURSE1 = "_id"; //primary key
   public static final String COL_COURSE2 = "courseTitle";
   public static final String COL_COURSE3 = "courseStartDate";
   public static final String COL_COURSE4 = "courseEndDate";
   public static final String COL_COURSE5 = "courseStatus";
   public static final String COL_COURSE6 = "optionalNote";
   public static final String COL_COURSE7 = "mentorName";
   public static final String COL_COURSE8 = "mentorPhone";
   public static final String COL_COURSE9 = "mentorEmail";
   public static final String COL_COURSE_TERMID = "for_cors_term"; //foreign key

   //assess table
   public static final String ASSESS_TABLE_NAME = "assess_table";
   public static final String COL_ASSESS1 = "_id"; //primary key
   public static final String COL_ASSESS2 = "assessName";
   public static final String COL_ASSESS3 = "assessDueDate";
   public static final String COL_ASSESS4 = "assessType";
   public static final String COL_ASSESS_COURSEID = "for_ases_cors"; //foreign key

或者如果您不想使用_id 字段作为主键,您可以使用RecyclerView 而不是ListView

【讨论】:

    【解决方案2】:

    所以基本上如果所有三个表都必须命名为 _id 我是什么语法 如果外键有,则应该在创建外键时使用 与引用表的主键同名,所以我将 总是得到创建重复列错误。

    它们不必像您可以使用 AS _id 来动态生成具有别名的列(注意,由于 * 您有 id _id 列)例如

    SELECT *, id AS _id
    
    • 您只需要每个 CursorAdapter 有 1 个 _id 列。

    至于定义外键使用

    INTEGER PRIMARY KEY _id,....., FOREIGN KEY(_id) REFERENCES parent_table(column_in_parent_table)
    

    它可能不会持续/良好/有用地工作。

    您实际上是在说 _id 列必须是父表中的值。因此,您将关系限制为 1-1 关系,因为 INTEGER PRIMARY KEY 是隐式唯一的,因为它是通常隐藏的 rowid 列的别名,该值,因为该列是特殊的,也必须是整数。

    例如如果课程表的第一行引用值为 1 的列,那么很好(比如 TERM1),那么您不能在该表中再次引用 TERM1,因为 _id 不得为 1。

    • 因此您遇到的唯一约束(重复)。

    简而言之,您应该将 _id 列引用为外键的父表列(在您的情况下),但不要在子表中使用 _id 列作为引用父表的列,然后您将父表限制为该值只有一个子表,即 1-1 关系(这实际上意味着这两个表是多余的)。

    相反,子表中的列应该是主要用于存储引用/关系/关联(可能是具有其他用途的值)的列。

    因此你想要类似的东西:-

    public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE " + TERM_TABLE_NAME + " (_id INTEGER PRIMARY KEY, termName TEXT, termStart TEXT, termEnd TEXT)");
            db.execSQL("CREATE TABLE " + COURSES_TABLE_NAME + " (_id INTEGER PRIMARY KEY, courseTitle TEXT, courseStartDate TEXT, courseEndDate TEXT, courseStatus INTEGER, optionalNote TEXT, mentorName TEXT, mentorPhone TEXT, mentorEmail TEXT, course_term_reference INTEGER REFERENCES term_table(_id))");
            db.execSQL("CREATE TABLE " + ASSESS_TABLE_NAME + " (_id INTEGER PRIMARY KEY, assessName TEXT, assessDueDate TEXT, assessType INTEGER, assess_course_reference INTEGER REFERENCES courses_table(_id))");
    }
    
    • 注意 AUTOINCREMENT 已被删除,您不需要它,也不需要它产生的开销 SQLite Autoincrement 根据:-
    • AUTOINCREMENT 关键字强加了额外的 CPU、内存、磁盘空间和 磁盘 I/O 开销,如果不是严格需要,应该避免。这是 通常不需要。
    • 使用了更简单且可能更容易理解的列外键定义(即外键是在列定义级别而不是表定义级别定义的)

    但是,我建议将标识符(列名、表名等)编码为常量,然后始终引用这些常量。

    因此,我建议以上内容会更好:-

    public static final String TERM_TABLE_NAME = "term_table";
    public static final String COL_TERM1 = "_id";
    public static final String COL_TERM2 = "termName";
    public static final String COL_TERM3 = "termStart";
    public static final String COL_TERM4 = "termEnd";
    public static final String TERM_DATABASE_NAME = "term.db";
    public static final int DBVERSION = 1;
    //courses table
    public static final String COURSES_TABLE_NAME = "courses_table";
    public static final String COL_COURSE1 = "_id";
    public static final String COL_COURSE2 = "courseTitle";
    public static final String COL_COURSE3 = "courseStartDate";
    public static final String COL_COURSE4 = "courseEndDate";
    public static final String COL_COURSE5 = "courseStatus";
    public static final String COL_COURSE6 = "optionalNote";
    public static final String COL_COURSE7 = "mentorName";
    public static final String COL_COURSE8 = "mentorPhone";
    public static final String COL_COURSE9 = "mentorEmail";
    public static final String COL_COURSE_TERM_REFERENCE = "term_reference";
    //assess table
    public static final String ASSESS_TABLE_NAME = "assess_table";
    public static final String COL_ASSESS1 = "_id";
    public static final String COL_ASSESS2 = "assessName";
    public static final String COL_ASSESS3 = "assessDueDate";
    public static final String COL_ASSESS4 = "assessType";
    public static final String COL_ASSESS_COURSE_REFERENCE = "course_reference";
    
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(
                "CREATE TABLE " + TERM_TABLE_NAME + "(" +
                        COL_TERM1 + " INTEGER PRIMARY KEY," +
                        COL_TERM2 + " TEXT, " +
                        COL_TERM3 + " TEXT, " +
                        COL_TERM4 + " TEXT" +
                        ")"
        );
        db.execSQL(
                "CREATE TABLE " + COURSES_TABLE_NAME + "(" +
                        COL_COURSE1 + " INTEGER PRIMARY KEY, " +
                        COL_COURSE2 + " TEXT, " +
                        COL_COURSE3 + " TEXT, " +
                        COL_COURSE4 + " TEXT, " +
                        COL_COURSE5 + " INTEGER, " +
                        COL_COURSE6 + " TEXT, " +
                        COL_COURSE7 + " TEXT, " +
                        COL_COURSE8 + " TEXT, " +
                        COL_COURSE9 + " TEXT, " +
                        COL_COURSE_TERM_REFERENCE + "INTEGER REFERENCES " + TERM_TABLE_NAME + "(" + COL_TERM1 + ")" +
                        ")"
        );
    
        db.execSQL(
                "CREATE TABLE " + ASSESS_TABLE_NAME + "(" +
                        COL_ASSESS1 + " INTEGER PRIMARY KEY, " +
                        COL_ASSESS2 + " TEXT, " +
                        COL_ASSESS3 + " TEXT, " +
                        COL_ASSESS4 + " INTEGER, " +
                        COL_ASSESS_COURSE_REFERENCE + " INTEGER REFERENCES " + COURSES_TABLE_NAME + "(" + COL_COURSE1 + ")" +
                        ")"
        );
    }
    

    上述结果导致 SQL(对于每个表)为:-

    CREATE TABLE term_table(_id INTEGER PRIMARY KEY,termName TEXT, termStart TEXT, termEnd TEXT)
    CREATE TABLE courses_table(courses_id INTEGER PRIMARY KEY, courseTitle TEXT, courseStartDate TEXT, courseEndDate TEXT, courseStatus INTEGER, optionalNote TEXT, mentorName TEXT, mentorPhone TEXT, mentorEmail TEXT, term_reference INTEGER REFERENCES term_table(_id))
    CREATE TABLE assess_table(assess_id INTEGER PRIMARY KEY, assessName TEXT, assessDueDate TEXT, assessType INTEGER, course_reference INTEGER REFERENCES courses_table(courses_id))
    

    附加

    要使用外键支持,您需要打开它。因此,您可以覆盖数据库助手的 onConfigure 方法以启用支持,例如:-

    @Override
    public void onConfigure(SQLiteDatabase db) {
        super.onConfigure(db);
        db.setForeignKeyConstraintsEnabled(true);
    }
    

    【讨论】:

      猜你喜欢
      • 2011-09-23
      • 1970-01-01
      • 2016-09-05
      • 2019-08-15
      • 2015-12-15
      • 2015-06-05
      • 1970-01-01
      • 1970-01-01
      • 2017-12-07
      相关资源
      最近更新 更多