【问题标题】:Trouble using INNER JOIN in SQLite in Android在 Android 的 SQLite 中使用 INNER JOIN 时遇到问题
【发布时间】:2015-04-02 16:51:10
【问题描述】:

我在本地服务器上有一个 MySQL 数据库。我通过 HTTP 使用 PHP 和 JSON 从 MySQL 数据库中获取数据。在 Android 应用程序中,使用 JSON 结果填充 SQLite 数据库。我已经验证了 SQLite 数据库实际上是通过运行类似于

的测试查询来填充的
Cursor cursor = database.rawQuery("SELECT recipe_id, ingredient_id, name FROM mm_ingredient_in_recipe_groups", null);

然后迭代生成的游标以查看数据是否存在。存在的数据存储在三个表中:

  1. mm_ingredients,包含食谱的成分。
  2. mm_recipes,包含食谱数据。
  3. mm_ingredient_in_recipe,包含用于连接mm_recipesmm_ingredients中数据的数据(即解析多对多关系)。

我想做的是运行一个查询,为参数配方名称选择成分。这意味着我必须将 mm_ingredientsmm_ingredient_in_recipe 以及 mm_recipes 进行 INNER JOIN。

我已尝试运行以下查询:

database.rawQuery("SELECT ingredient.name, ingredient.base_unit FROM mm_ingredients AS ingredient INNER JOIN mm_ingredient_in_recipe AS ir ON ir.ingredient_id = ingredient._id INNER JOIN mm_recipes AS recipe ON ir.recipe_id = recipe._id WHERE recipe.name = ?", new String[] {"Spicy tomatsuppe"});

这将返回一个空游标。然而,在我的 MySQL 数据库中,运行相应的查询

SELECT ingredient.name, ingredient.base_unit FROM mm_ingredients AS ingredient INNER JOIN mm_ingredient_in_recipe AS ir ON ir.ingredient_id = ingredient.id INNER JOIN mm_recipes AS recipe ON ir.recipe_id = recipe.id WHERE recipe.name = "Spicy tomatsuppe";

返回预期的数据。我在这里做错了什么?似乎我运行的每个查询都可以正常工作,直到我开始包含 INNER JOIN。 (据我所知,id 字段在 SQLite 中被命名为 _id,这应该是最佳实践。)

编辑:添加用于写入数据库的代码:

下面是我如何为表mm_recipes 填充数据库的示例。其他两个表也类似。我将JSONArray.optString() 用于SQLite 的TEXT 数据类型,JSONArray.optInt() 用于INTEGERJSONArray.optDouble() 用于REAL

public static final String TABLE_NAME                       = "mm_recipes";
public static final String TABLE_NAME_IN_JSON               = "mm_recipes";

public static final String COLUMN_ID                        = "_id";
public static final String COLUMN_NAME                      = "name";
public static final String COLUMN_IN_APP                    = "in_app";
public static final String COLUMN_DIFFICULTY                = "difficulty";
public static final String COLUMN_TIME_CONSUMPTION_UPPER    = "time_consumption_upper";
public static final String COLUMN_TIME_CONSUMPTION_LOWER    = "time_consumption_lower";
public static final String COLUMN_PREPARATION_TIME          = "preparation_time";
public static final String COLUMN_PORTIONS                  = "portions";
public static final String COLUMN_PORTIONS_UNIT             = "portions_unit";
public static final String COLUMN_DESCRIPTION               = "description";
public static final String COLUMN_TIPS                      = "tips";
public static final String COLUMN_USE_INGREDIENT_GROUPS     = "use_ingredient_groups";

public static final void populateDatabase(SQLiteDatabase database, JSONObject databaseData) throws JSONException{

    JSONArray table_data = databaseData.optJSONArray(TABLE_NAME_IN_JSON);
    ContentValues contentValues = new ContentValues();

    for (int i = 0; i < table_data.length(); i++){
        JSONObject data = table_data.getJSONObject(i);

        contentValues.clear();

        contentValues.put(COLUMN_NAME,                      data.optString(COLUMN_NAME));
        contentValues.put(COLUMN_IN_APP,                    data.optInt(COLUMN_IN_APP));
        contentValues.put(COLUMN_DIFFICULTY,                data.optString(COLUMN_DIFFICULTY));
        contentValues.put(COLUMN_TIME_CONSUMPTION_UPPER,    data.optInt(COLUMN_TIME_CONSUMPTION_UPPER));
        contentValues.put(COLUMN_TIME_CONSUMPTION_LOWER,    data.optInt(COLUMN_TIME_CONSUMPTION_LOWER));
        contentValues.put(COLUMN_PREPARATION_TIME,          data.optInt(COLUMN_PREPARATION_TIME));
        contentValues.put(COLUMN_PORTIONS,                  data.optInt(COLUMN_PORTIONS));
        contentValues.put(COLUMN_PORTIONS_UNIT,             data.optString(COLUMN_PORTIONS_UNIT));
        contentValues.put(COLUMN_DESCRIPTION,               data.optString(COLUMN_DESCRIPTION));
        contentValues.put(COLUMN_TIPS,                      data.optString(COLUMN_TIPS));
        contentValues.put(COLUMN_USE_INGREDIENT_GROUPS,     data.optInt(COLUMN_USE_INGREDIENT_GROUPS));

        database.insert(TABLE_NAME, null, contentValues);
    }

}

作为参考,这里还有一个如何创建mm_recipe 表的示例。在我的课堂上为mm_recipe

public static final String DATATYPE_ID                      = "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL";
public static final String DATATYPE_NAME                    = "TEXT NOT NULL";
public static final String DATATYPE_IN_APP                  = "INTEGER DEFAULT 0";
public static final String DATATYPE_DIFFICULTY              = "TEXT NOT NULL";
public static final String DATATYPE_TIME_CONSUMPTION_UPPER  = "INTEGER NOT NULL";
public static final String DATATYPE_TIME_CONSUMPTION_LOWER  = "INTEGER NOT NULL";
public static final String DATATYPE_PREPARATION_TIME        = "INTEGER";
public static final String DATATYPE_PORTIONS                = "INTEGER NOT NULL";
public static final String DATATYPE_PORTIONS_UNIT           = "TEXT NOT NULL";
public static final String DATATYPE_DESCRIPTION             = "TEXT";
public static final String DATATYPE_TIPS                    = "TEXT";
public static final String DATATYPE_USE_INGREDIENT_GROUPS   = "INT";

/**Create table statement.*/
public static final String CREATE_TABLE = 
    "CREATE TABLE"                  + " " + TABLE_NAME                      + " (" +
    COLUMN_ID                       + " " + DATATYPE_ID                     + ", " +
    COLUMN_NAME                     + " " + DATATYPE_NAME                   + ", " +
    COLUMN_IN_APP                   + " " + DATATYPE_IN_APP                 + ", " +
    COLUMN_DIFFICULTY               + " " + DATATYPE_DIFFICULTY             + ", " +
    COLUMN_TIME_CONSUMPTION_UPPER   + " " + DATATYPE_TIME_CONSUMPTION_UPPER + ", " +
    COLUMN_TIME_CONSUMPTION_LOWER   + " " + DATATYPE_TIME_CONSUMPTION_LOWER + ", " +
    COLUMN_PREPARATION_TIME         + " " + DATATYPE_PREPARATION_TIME       + ", " +
    COLUMN_PORTIONS                 + " " + DATATYPE_PORTIONS               + ", " +
    COLUMN_PORTIONS_UNIT            + " " + DATATYPE_PORTIONS_UNIT          + ", " +
    COLUMN_DESCRIPTION              + " " + DATATYPE_DESCRIPTION            + ", " +
    COLUMN_TIPS                     + " " + DATATYPE_TIPS                   + ", " +
    COLUMN_USE_INGREDIENT_GROUPS    + " " + DATATYPE_USE_INGREDIENT_GROUPS  + ");";

在我的SQLiteOpenHelper 课堂上:

@Override
public void onCreate(SQLiteDatabase database) {
    database.execSQL(Table_mm_recipes.CREATE_TABLE);
    database.execSQL(Table_mm_ingredient_in_recipe.CREATE_TABLE);
    database.execSQL(Table_mm_ingredients.CREATE_TABLE);
} 

【问题讨论】:

  • 在将数据写入数据库时​​,您可能使用了错误的数据类型。显示该代码。
  • 我使用JSONArrayoptString()optInt()optDouble() 方法从JSON 中获取数据。如果我没记错的话,这些应该对应于SQLiteTEXTINTEGERREAL。我编辑了这个问题,以包含一个我如何为mm_recipes 填充数据库的示例。其他两个表的操作方式相同。

标签: android sqlite android-sqlite


【解决方案1】:

这被证明是(正如预期的那样)一些愚蠢的错误。我将把它留在这里,以防其他人犯类似的错误。

由于我已经设置了代码以在每次创建时删除数据库上的所有内容,因此以这种方式:

database.execSQL("DELETE FROM " + Table_mm_recipes.TABLE_NAME);
database.execSQL("DELETE FROM " + Table_mm_ingredient_in_recipe.TABLE_NAME);
database.execSQL("DELETE FROM " + Table_mm_ingredients.TABLE_NAME);

数据以某种方式发生了一些不好的事情。这样做并没有什么意义,因为您只需创建数据库并在SQLiteOpenHelper.onCreate() 插入数据就可以了。如果您需要在设置任何版本控制之前更新数据,只需卸载并重新安装该应用程序。然后,如果愿意,您可以在SQLiteOpenHelper.onUpgrade() 销毁数据。我不确定如何数据是混乱的,但他们肯定是。

奇怪的是,我仍然可以通过简单的操作看到数据库中有数据

Cursor cursor = database.rawQuery("SELECT recipe_id, ingredient_id, name FROM mm_ingredient_in_recipe_groups", null);

并从之前迭代该光标。我直接在INNER JOIN 语句之前对所有表执行了此操作,但它们仍然不起作用。这可能与数据库的错误删除/创建有关。

除此之外,上面的所有代码都应该是有效的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-02
    • 2022-01-24
    • 1970-01-01
    • 2019-03-03
    • 2011-03-25
    • 1970-01-01
    相关资源
    最近更新 更多