How to get multiple counts from room database? - android

I want to return multiple counts from Room Select query in android.
My query is like
Select Count(CASE WHEN x = '0' or x = '2'), Count(Case when a = '33' or a = '23') FROM my_table WHERE id=10
I want above query to return something as list which will contain values of both the above Count() function. This can be easily done using SQLite but I want to use it in room.

You can give names to the counts and return them as a class, like:
data class MyCounts(
#ColumnInfo(name = "first_count")
val firstCount: Int,
#ColumnInfo(name = "second_count")
val secondCount: Int
)
#Dao
interface MyDao {
#Query("SELECT COUNT(*) as first_count, COUNT(*) as second_count from my_table")
suspend fun getMyCounts(): MyCounts
}

Related

Room DB SQLite query to get counts of one-to-many relationships from different tables

I'm trying to get a count of a one-to-many ralationship in my query.
My data class:
data class CustomerWithCounts(
#Embedded val customer: Customer,
#Embedded val address: Address,
val orderCount: Int,
val paymentCount: Int
)
I'm struggling to figure out how I can get the counts.
My current Query:
SELECT *,
COUNT(SELECT * FROM tblOrder WHERE customerId = c.id) AS 'orderCount',
COUNT(SELECT * FROM tblPayment WHERE customerId = c.id) AS 'paymentCount'
FROM tblCustomer c
LEFT JOIN tblAddress a ON c.customerBillingAddressId = a.addressId
ORDER BY c.customerFirstName, c.customerLastName
How do I achieve this?

RoomDB - JOIN query for which the result is a list of data types with a field whose value is a multiplication of 2 tables fields

Let's say I have the following entities for my RoomDB:
enum class Type {
FOO,
BAR
}
#Entity(
tableName = "amount",
primaryKeys = ["id", "type"]
)
data class Amount(
val id: Long,
val type: Type,
val amount: Int
)
#Entity(
tableName = "value",
primaryKeys = ["id", "valueType"]
)
data class Value(
val id: Long,
val valueType: Type,
val value: Int
)
What I want to do is somehow, with a SQL query (or RoomDB annotations ideally...) query the amount table, and join each amount with the corresponding value row(s) from the value table (using the type from amount to cross reference the valueType field on the value table) by multiplying the amount field with the value field to end up with an object like this:
data class ValueOfAmount(
val type: Type,
val valueOfAmount: Int // amount * value
)
I can think of a way to do this, but it requires doing some of the JOIN "logic" in my repo code layer, when I'd prefer to do this at the query layer instead (if at all possible).
Create a joined data class like so:
data class AmountWithValue(
#Embedded
val amount: Amount,
#Relation(
parentColumn = "type",
entityColumn = "valueType"
)
val value: Value
)
Expose a function from my dao to retrieve the joined data:
#Query("SELECT * from amount")
suspend fun getAmountsWithValues() : List<AmountWithValue>
Consume this function, and map the results to ValueOfAmount instances like so:
val valueOfAmounts = dao.getAmountsWithValues().map { amountWithValue ->
ValueOfAmount(
amountWithValue.amount.type,
amountWithValue.amount.amount * amountWithValue.value.value
)
}
// Do stuff with value of amounts
What I'd like to know is if there is some way to encode that mapping code into the QUERY itself (either via SQL or, even better, if RoomDB has some annotations that support this kind of complex query as annotations on my data types - similar to how it let's me define relationships for simple JOIN operations).
I believe that the following may be what you want:-
First a Class to be joined :-
data class AmountWithCalculatedValue(
#Embedded
val amount: Amount,
val calculatedValue: Int
)
And then a Dao :-
#Query("SELECT *,(amount * value) AS calculatedValue FROM amount JOIN value ON amount.type = valueType")
fun getAmountWithCalculatedValue(): List<AmountWithCalculatedValue>
if you wanted the Value as well it's a little more complicated due to duplicate/ambiguous columns but you could use:-
data class AmountWithCalculatedValue(
#Embedded
val amount: Amount,
val calculatedValue: Int,
#Embedded(prefix = "_value_")
val value: Value
)
With :-
#Query("SELECT amount.id, amount.amount, amount.type,value.id AS _value_id, value.id AS _value_value, value.valueType AS _value_valueType,(amount * value) AS calculatedValue FROM amount JOIN value ON amount.type = valueType")
fun getAmountWithCalculatedValue(): List<AmountWithCalculatedValue>
or :-
#Query("SELECT amount.*,value.id AS _value_id, value.id AS _value_value, value.valueType AS _value_valueType,(amount * value) AS calculatedValue FROM amount JOIN value ON amount.type = valueType")
fun getAmountWithCalculatedValue(): List<AmountWithCalculatedValue>
that is using the #Embedded's prefix parameter is saying that the values will be prefixed with the prefixed with the prefix so you have to us AS clauses to disambiguate the respective columns. As no prefix is used on the amount columns then amount.* can be used (2nd dao).
I'd say that for 1-1 relationships using #Relationship (as opposed to #Embedded) is perhaps a little less efficient than using a JOIN. As the way Room works it gets the Parent's from the given query and then retrieves the #Relation from the Parent via a separate query and hence why it recommends #Transaction.

Search query using Android room relation

In Android room relation, is it possible to use search query using the property of the related table. Below is my table structure. In this i am relating transaction with payment and lines(transaction items). I have an search field in my UI where the user could search using payment amount which is inside payment table. How to form a query to access the properties of payment table.
class TransactionWithPaymentAndLines(
#Embedded
var transactions: Transactions? = null,
#Relation(
parentColumn = "id",
entityColumn = "transactionId",
entity = Payment::class
)
var payments: List<Payment> = listOf(),
#Relation(
parentColumn = "id",
entityColumn = "transactionId",
entity = TransactionLines::class
)
var transactionLines: List<TransactionLines> = listOf()
)
Ideal way is to query multiple related tables is to create a View. A view combines data from two or more tables using join.
In Android, using Room Persistance library, you can create such a view, and then you can query the fields of view. This is how you can do it:
Suppose, you have tables:
User: id, name, departmentId
Department: id, name
Create a View:
#DatabaseView("SELECT user.id, user.name, user.departmentId," +
"department.name AS departmentName FROM user " +
"INNER JOIN department ON user.departmentId = department.id")
data class UserDetail(
val id: Long,
val name: String?,
val departmentId: Long,
val departmentName: String?
)
Add View to Database:
#Database(entities = arrayOf(User::class),
views = arrayOf(UserDetail::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDetailDao(): UserDetailDao
}
Create a DAO:
#Dao
interface UserDetailDao {
#Query("SELECT * FROM UserDetail")
fun loadAllUserDetails(): Array<UserDetail>
}
Now, you can query a View using this DAO.
Absolutely possible, you can use #Query in your DAO class please read Room Database Documentation
Examples of #Query
#Query("SELECT * FROM user")
List<User> getAll();
#Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
#Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
use #DB or #Query.
That should perfectly work...
#Query("SELECT * FROM TABLE_NAME")
List<Identifier> getAll();

Android Room Allow Dao #Query to populate #Ignore columns

I'm wanting my Dao to populate #Ignore columns in my Entity class. For example:
Entity
#Entity(tableName = "example")
data class Example(
#PrimaryKey
val id: Long,
val value: Int
) {
#Ignore
var nextId: Long = 0L
}
Dao
#Dao
interface ExampleDao {
#Query(value = "SELECT *, (id + 1) AS nextId FROM example")
fun getAllExamples(): List<Example>
}
However, when the application gets built, the following warning gets produced:
The query returns some columns [nextId] which are not used by com.example.app.Example and it doesn't populate nextId.
Is it possible to include #Ignore columns in a #Query (if so, how)? If not, what are some strategies that can be employed to populate columns that are not present in my tables into my Entity class.
Note: I'm fully aware with the example provided that I can simply do something like:
#Ignore
val nextId: Long = id + 1
But is not the point of the question I am asking.
Based on the information that #CommonsWare has given me, the solution that I went with is
data class ExampleWithNextId(
#Embedded
val example: Example) {
var nextId: Long = 0L
}
Then use it in Dao like so
#Dao
interface ExampleDao {
#Query(value = "SELECT *, (id + 1) AS nextId FROM example")
fun getAllExamplesWithNextId(): List<ExampleWithNextId>
}

Room privilege Relation with query

I have two tables.
Table Decks:
Decks:
-id
-name
Table Cards:
Cards:
-id
-name
-deckId
I created a data class for this query:
data class DeckWithDueDatedCards(
#Embedded
var deckEntity: DeckEntity,
var list: List<CardEntity>
)
I removed the #Relation top of list, because it's queried all cards.
I want to add a condition. So I don't need all card, from the deck.
Let see:
I created the next Query:
#Query("SELECT *, (SELECT * FROM cards WHERE deckId = D.id AND dueDate < date('now')) as list FROM decks D WHERE D.id = :deckId")
fun getDeckWithDueDatedCards(deckId: Long): Flowable<DeckWithDueDatedCards>
But I got an error, because the inner select is bad. It can't inflate the list.
There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (sub-select returns 8 columns - expected 1)
public abstract io.reactivex.Flowable<....database.entity.DeckWithDueDatedCards> getDeckWithDueDatedCards(long deckId);
I see the issue is the Inner select have 8 item, but it's except only 1. But I have a list there. How to inflate it?
Anyone have idea how to fix this issue? It's possible with query?
Room Dao's allow non abstract method.
If you want to getDeckWithDueDatedCards you can write a method that gonna load all CardEntity first, and then load linked DeckEntity to build your DeckWithDueDatedCards
Java code :
#Query("SELECT * FROM decks WHERE id = :deckId")
public abstract DeckEntity loadDeck(int deckId);
#Query("SELECT * FROM cards WHERE deckId = D.id AND dueDate < :date")
public abstract Flowable<List<CardEntity>> loadCardEntity(String date);
#Transaction
public Flowable<DeckWithDueDatedCards> getDeckWithDueDatedCards(int deckId, String date) {
return loadCardEntity(date)
.map(cardEntityList -> {
List<DeckWithDueDatedCards> res = new ArrayList<>();
for(CardEntity cardEntity : cardEntityList) {
res.add(new DeckWithDueDatedCards(cardEntity, loadDeck(deckId)));
}
return res;
});
}
note: be aware that modification on CardEntity gonna trigger onNext on your subscriber, but modifications on DeckEntity won't ...
edit: if you need to be notify on DeckEntity changes update loadCardEntity query
#Query("SELECT cards.* FROM cards INNER JOIN decks ON cards.deckId = decks.id WHERE deckId = D.id AND dueDate < :date")
public abstract Flowable<List<CardEntity>> loadCardEntity(String date);

Categories

Resources