Search code examples
kotlinandroid-roomandroid-livedata

How to Query a minimum value from a table in a room database?


I am building a simple weight tracking app in Kotlin, using room, and am having a tough time finding the resource to figure out how to query and return just the one value from the database without it returning my entire data class.

I realize I am returning a list in my Dao, but can someone explain to me how to return just the minimum weight without the list?

I am still fairly new to room so any help is appreciated

my Dao

@Dao
interface WeightDao {

    @Query("SELECT * FROM weight")
    fun getAllWeightEntries():Flow<List<Weight>>

    This does returns the lowest weight entry but includes everything else from my data class
    @Query("SELECT * FROM weight WHERE weight_entry = (SELECT MIN(weight_entry) FROM weight)")
    fun getLowestWeightEntry():Flow<List<Weight>>

     My dataclass


    @Entity(tableName = "weight")
    data class Weight(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "weight_entry")
    val weightEntry: Double,
    @ColumnInfo(name = "weight_entry_notes")
    val weightEntryNotes: String
    )

    Snippet from my viewmodel


      val lowestWeightEntry: LiveData<List<Weight>> = weightDao.getLowestWeightEntry().asLiveData()



    snippet from my u.i. fragment


      viewModel.lowestWeightEntry.observe(this.viewLifecycleOwner) { item ->
            item.let {
                binding.tvLowestWeightTest.text = item.toString()
            }
        }


Solution

  • I believe that you could use the following:-

    @Query("SELECT min(weight_entry) FROM weight")
    fun getAllWeightEntries():Flow<Double>
    
    • Could also be String instead of Double (even Int or Long .... but you'd then reduce it to an integer value)

    That is, if the output is a single column you can use the appropriate type for that value (as long as it's one of the types that Room supports directly for a column (i.e. no TypeConverter required)).

    And obviously the viewmodel should expect to retrieve that type of value.

    A Little more on the subject (POJO's for non @Entity types)

    For anything other than a single value, other than for an @Entity annotated class that is defined in the entries parameter of the @Database annotation (which tells Room that the class is a table) then you need a POJO whose field names match the output field names.

    e.g. say you had

    @Query("SELECT min(weight_entry) AS min, max(weight_entry) AS max FROM weight")
    fun getAllWeightEntries():Flow<weightMinAndMax>
    

    The you could have a POJO such as:-

    data class weightMinAndMax(
        val min: Double,
        val max: Int /* although should be Double */
    )
    
    • note AS gives the output column an alias (min and max respectively) to suit the POJO fields (else output columns would be min(weight_entry) and max(weight_entry) respectively).
    • you can also use the name parameter of the @ColumnInfo to override the expected output column name

    e.g. you could have

    @Query("SELECT min(weight_entry) AS mymin, max(weight_entry) AS mymax FROM weight")
    fun getAllWeightEntries():Flow<weightMinAndMax>
    

    The you could have a POJO such as:-

    data class weightMinAndMax(
        @ColumnInfo(name = "mymin")
        val min: Double,
        @ColumnInfo(name = "mymax")
        val max: Int /* although should be Double */
    )
    

    In Short; for a single value output from a Query Room does not need to match output columns with field names. For multiple value outputs from a Query then Room needs to be able to associate the output values with the respective fields in the recipient object.