【问题标题】:Failed rename column while join query连接查询时重命名列失败
【发布时间】:2021-07-06 09:10:10
【问题描述】:

我有一个问题:

@Query("SELECT entries.*, myProfile.myProfileId as my_profile_id FROM Entry as entries LEFT JOIN MyProfile ON entries.user_public_profile_userId = myProfile.myProfileId")

我只需要从第二个(右)表中获取值并将其放入实体的字段 - 第一个表中的“my_profile_id”。但没有任何效果。 这就是第一个表(左)实体中的字段的样子 -

 @ColumnInfo(name = "my_profile_id")
val myUserProfileId: Int? = null,

这就是第二个表中这个字段的样子(右)

 @PrimaryKey
val myProfileId: Int,

结果始终为空,但 ON 后的条件有效,因为我试图从 LEFT JOIN 更改为 INNER JOIN 广告得到了结果,所以这里唯一的问题是将“myProfileId”正确映射到“my_profile_id” 我做错了什么?

【问题讨论】:

  • 请编辑您的问题以包含完整的 EntryMyProfile 实体以及您用于查询结果的 POJO(如果您不'没有一个那么这可能是你的问题)。还提供两个表中存在的示例数据。最后还要提供预期的结果。

标签: android sql join android-room


【解决方案1】:

不考虑 JOIN 类型,使用 :-

SELECT entries.*, myProfile.myProfileId as my_profile_id FROM Entry as entries LEFT JOIN MyProfile ON entries.user_public_profile_userId = myProfile.myProfileId

将导致 my_profile_id 与存储在 user_public_profile_userId 列中的值相同。从这个意义上说,JOIN 是浪费时间。

我怀疑您可能想从 MyProfile 表中获取另一个有用的值。

假设您有一个 Entry 实体,它类似于:-

@Entity
data class Entry(
    @PrimaryKey
    val id: Long? = 0,
    val name: String,
    @ColumnInfo(name = "my_profile_id")
    val myUserProfileId: Long? = null,
    val user_public_profile_userId:  Long
)

还有一个 MyProfile 实体,类似于:-

@Entity
data class MyProfile(
    @PrimaryKey
    val myProfileId: Long? = null,
    val otherdata: String
)

如果您想从 otherdata 列中获取值,那么您需要一个额外的 POJO 来组合数据。

因此考虑这样的 POJO EntryPlus :-

data class EntryPlus(
    @Embedded
    val entry: Entry,
    val profileIdFromMyProfile: Long,
    val otherdataFromMyProfile: String
)
  • @Embedded 和下面一行是说你想要条目表中的所有列/字段
  • 其他两列将来自其他地方(满足查询)

所以你可以有一个查询,例如:-

 @Query("SELECT entries.*, myProfile.myProfileId AS profileIdFromMyProfile, myProfile.otherdata AS otherdataFromMyProfile  FROM Entry as entries LEFT JOIN MyProfile ON entries.user_public_profile_userId = myProfile.myProfileId")
    fun getMyOtherData(): List<EntryPlus>
  • 即查询略有不同,但重要的是使用 AS 来相应地命名输出列以适合 EntryPlus POJO 中的字段名称。
  • 同样重要的是,结果是 POJO (EntryPlus) 的列表。

示例

考虑以下代码:

  1. 插入一些数据(3 个 MyProfile 行和 2 个 Entry 行),然后
  2. 使用 SELECT * FROM entry 提取所有没有 JOIN 的 Entry 行,然后
  3. 使用您的原始查询进行提取,然后
  4. 通过 POJO 提取

代码是:-

    db = TheDatabase.getInstance(this) // Get database instance
    dao = db.getAllDao() // get the Dao
    dao.deleteAllMyProfile() // Clear the MyProfile table
    dao.deleteAllEntry() // Clear the Entry table
    // Add some profile rows
    dao.insert(MyProfile(1,"myprofile1"))
    dao.insert(MyProfile(2,"myprofile2"))
    dao.insert(MyProfile(3,"myprofile3"))
    // Add some Entry rows (both link to profile1 in this case)
    dao.insert(Entry(100,"Entry1",0,1))
    dao.insert(Entry(200,"Entry2",0,1))

    // Extract 1 All as Entry List (no join)
    for(e: Entry in dao.getAll()) {
        Log.d("ENTRYINFO(1)","Entry Name is ${e.name} EntryID is ${e.id} MapToMyProfile is ${e.user_public_profile_userId} Value is ${e.myUserProfileId}" )
    }
    // Extract 2 All from original query
    for(e: Entry in dao.getMyData()) {
        Log.d("ENTRYINFO(2)","Entry Name is ${e.name} EntryID is ${e.id} MapToMyProfile is ${e.user_public_profile_userId} Value is ${e.myUserProfileId}" )
    }
    // Extract 3 getting useful data from the 2nd (JOINED) table
    for(ep: EntryPlus in dao.getMyOtherData()) {
        Log.d("ENTRYINFO(3)",
            "Entry Name is ${ep.entry.name} EntryID is ${ep.entry.id} MapToMyProfile is ${ep.entry.user_public_profile_userId} Myuserprofile(From Entry) ${ep.entry.myUserProfileId}" +
                    " MyProfileId (From MyProfile) is ${ep.profileIdFromMyProfile} OtherData (From MyProfile) is ${ep.otherdataFromMyProfile}" )
    }

日志的输出是:-

2021-07-07 09:44:12.665 D/ENTRYINFO(1): Entry Name is Entry1 EntryID is 100 MapToMyProfile is 1 Value is 0
2021-07-07 09:44:12.665 D/ENTRYINFO(1): Entry Name is Entry2 EntryID is 200 MapToMyProfile is 1 Value is 0

2021-07-07 09:44:12.666 D/ENTRYINFO(2): Entry Name is Entry1 EntryID is 100 MapToMyProfile is 1 Value is 1
2021-07-07 09:44:12.666 D/ENTRYINFO(2): Entry Name is Entry2 EntryID is 200 MapToMyProfile is 1 Value is 1

2021-07-07 09:44:12.667 D/ENTRYINFO(3): Entry Name is Entry1 EntryID is 100 MapToMyProfile is 1 Myuserprofile(From Entry) 0 MyProfileId (From MyProfile) is 1 OtherData (From MyProfile) is myprofile1
2021-07-07 09:44:12.668 D/ENTRYINFO(3): Entry Name is Entry2 EntryID is 200 MapToMyProfile is 1 Myuserprofile(From Entry) 0 MyProfileId (From MyProfile) is 1 OtherData (From MyProfile) is myprofile1

输出说明

前两行注意 MyProfileId 值(即 Value =)在插入时为 0。

第二两行使用您的原始查询显示 MyProfileId (Value =) 现在与 MyProfile 行的链接/引用/关联/关系(即user_public_profile_userId 列)的值相同。

第三个表示已从 MyProfile 表(otherdata 列)中提取值。

但是,您似乎还描述了 LEFT JOIN 与仅 JOIN 相比的性质。

如果在提取之前添加了以下行(另一个 Entry 行但引用了不存在的 MyProfile 行):-

dao.insert(Entry(300,"Entry3",999,10 /* 10 = No such myprofile row */))

那么结果中的变化将是显着的,因为第二次提取会根据以下内容为 my_profile_id 检索 null:-

D/ENTRYINFO(2): Entry Name is Entry3 EntryID is 300 MapToMyProfile is 10 Value is null

更改为仅 JOIN(而不是 LEFT JOIN)并省略引用不存在的 MyProfile 的行。这是记录在案的影响:-

如果连接运算符是“LEFT JOIN”或“LEFT OUTER JOIN”,则在应用 ON 或 USING 过滤子句后,将在原始左侧的每一行的输出中添加一个额外的行输入数据集对应于复合数据集中的任何行(如果有)。添加的行在通常包含从右侧输入数据集复制的值的列中包含 NULL 值。

在空值的情况下,如果这是您的问题,那么您需要决定要做什么。

  • 例如,您可以使用 SQLite COALESCE 函数来更改空值。例如SELECT entries.*, coalesce(myProfile.myProfileId,9999) as my_profile_id FROM Entry ....

  • 也许您需要确保保持参照完整性,在这种情况下,您可以利用 FOREIGN KEYS 来强制执行参照完整性。或许可以参考Foreign Key

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-07
    • 2011-09-12
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 2019-07-16
    相关资源
    最近更新 更多