Как присоединить сумму из другого стола - PullRequest
0 голосов
/ 23 сентября 2019

У меня есть таблица с учетными записями, а другая - с транзакциями:

@Parcelize
@Entity(tableName = Database.Accounts.TABLE_NAME)
data class Account(
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = Database.Accounts.COL_ID) val id: Long = 0,
    @ColumnInfo(name = Database.Accounts.COL_NAME) val name: String = "",
    @ColumnInfo(name = Database.Accounts.COL_BALANCE) val balance: Double = 0.0
) : Model

Каждая транзакция принадлежит учетной записи и, следовательно, содержит account_id.

data class TransactionEntity(
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = Database.Transactions.COL_ID) val id: Long = 0L,
    @ColumnInfo(name = Database.Transactions.COL_TITLE) val title: String = "",
    @ColumnInfo(name = Database.Transactions.COL_DATE) val date: LocalDate = LocalDate.MIN,
    @ColumnInfo(name = Database.Transactions.COL_VALUE) val value: Double = 0.0,
    @ColumnInfo(name = Database.Transactions.COL_NOTES) val notes: String = "",
    @ColumnInfo(name = Database.Transactions.COL_TYPE) @TransactionType val type: Int = TransactionType.EARNING,
    @ColumnInfo(name = Database.Transactions.COL_ACCOUNT_ID) val accountId: Long = Account.DEFAULT_ACCOUNT_ID,
    @ColumnInfo(name = Database.Transactions.COL_BUDGET_ID) val budgetId: Long? = null
) : Model

При запросах учетных записейbalance должно быть суммой value всех транзакций, принадлежащих счету.

Как я могу это понять?

Ответы [ 2 ]

1 голос
/ 23 сентября 2019

Этого можно достичь (при условии, что вам нужен текущий баланс), не используя объединение, а получая доступ к соответствующим объектам вместе с запросом, чтобы получить баланс в определенный момент времени.

Таким образом, вы можете иметьследующий Dao's

@Query("SELECT * FROM transactions WHERE transaction_account_id = :accountID")
List<TransactionEntity> getTransactionsForAnAccount(long accountID);

@Query("SELECT * FROM accounts WHERE account_id = :accountId LIMIT 1")
Accounts getAccountById(long accountId);

@Query("SELECT sum(transaction_value) FROM transactions WHERE transaction_date <= :transactionDate AND transaction_account_id = :accountId ORDER BY transaction_date ASC")
Double getBalanaceAtADate(long accountId, String transactionDate);

Первый возвращает транзакции для учетной записи.Второй возвращает аккаунт в соответствии с его идентификатором.Третий вернет СУММУ всех транзакций за предоставленную дату вместе со всеми предыдущими транзакциями (отсюда и текущее сальдо).

  • Имена примечаний могут отличаться, но приведенное выше основано на вашем коде.

Пример

Вы можете использовать что-то вроде следующего (не на Java, а на Kotlin): -

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mDB = Room.databaseBuilder(this,Database.class,Database.DBNAME)
            .allowMainThreadQueries()
            .build();
    mDBDao = mDB.getAADao();

    mDBDao.addAccount(new Accounts("Account1",0));
    mDBDao.addAccount(new Accounts("Account2"));

    mDBDao.addTransaction(new TransactionEntity("Buy my first thing","2019-09-20 10:20",-25.46,"blah",0,1,0));
    mDBDao.addTransaction(new TransactionEntity("Buy my second thing","2019-09-20 11:30",-65.30,"blah",0,1,0));
    mDBDao.addTransaction(new TransactionEntity("Deposit","2019-09-21 12:00",100,"blah",1,1,0));

    mDBDao.addTransaction(new TransactionEntity("Deposit 1","2019-09-22 13:00",50,"blah",1,2,0));
    mDBDao.addTransaction(new TransactionEntity("Deposit 2","2019-09-22 14:00",150,"blah",1,2,0));

    //<<<<<<<<< previous code is initialisation and setting up some testing data >>>>>>>
    List<TransactionEntity> tlaccount1 = mDBDao.getTransactionsForAnAccount(1); //<<<<<<<< get the transactions
    logTransactions(tlaccount1); //<<<<<<<<<< invokes the logTransactions method as below

}

private void logTransactions(List<TransactionEntity> tl) {
    final String TAG = "TRANSLOG";

    // If no transactions the return after logging message
    if (tl.size() < 1) {
        Log.d("TRANSLOG","No transactions to process");
        return;
    }

    // First get the Account from the first Transaction
    Accounts currentAccount = mDBDao.getAccountById(tl.get(0).getAccountId());
    // Iterate through the transactions
    for (TransactionEntity tle: tl) {
        double running_balance = mDBDao.getBalanaceAtADate(currentAccount.accountid,tle.getDate()); //<<<<<<<<<< GET the running balance
        // Output the current transaction with the account name and balance
        Log.d(TAG,
                "Account Name: " + currentAccount.getName() +
                        " Title: " + tle.getTitle() +
                        " Value: " + tle.getValue().toString() +
                        " Balance: " + running_balance
        );
    }
}

Результат

2019-09-23 10:59:19.225  D/TRANSLOG: Account Name: Account1 Title: Buy my first thing Value: -25.46 Balance: -25.46
2019-09-23 10:59:19.228  D/TRANSLOG: Account Name: Account1 Title: Buy my second thing Value: -65.3 Balance: -90.75999999999999
2019-09-23 10:59:19.230  D/TRANSLOG: Account Name: Account1 Title: Deposit Value: 100.0 Balance: 9.240000000000009

Итакбез остатка 25,46 снимается, затем 65,30 снимается (-90,76) и, наконец, 100 депонируется, таким образом, последний остаток равен -90,76 + 100 = 9,24.

  • Обратите внимание, что это, вероятно, не самый эффективный способ, но его относительно легко кодировать и понимать.

Дополнительно

Чтобы на самом деле объединять таблицы и получать данные из других таблиц, вам нужен другой класс, чтобы вы могли иметьобъект с необходимыми переменными-членами.

Например, чтобы объединить обе таблицы со связанными данными, у вас может быть класс, такой как: -

public class TransactionWithAccountAndDerivedBalance {

    long transactionId;
    String transactionTitle;
    String transactionDate;
    double transactionValue;
    String transactionNotes;
    int transactionType;
    long accountId;
    long budgetId;
    String accountName;
    double balance;

    public long getTransactionId() {
        return transactionId;
    }

    public void setTransactionId(long transactionId) {
        this.transactionId = transactionId;
    }

    public String getTransactionTitle() {
        return transactionTitle;
    }

    public void setTransactionTitle(String transactionTitle) {
        this.transactionTitle = transactionTitle;
    }

    public String getTransactionDate() {
        return transactionDate;
    }

    public void setTransactionDate(String transactionDate) {
        this.transactionDate = transactionDate;
    }

    public double getTransactionValue() {
        return transactionValue;
    }

    public void setTransactionValue(double transactionValue) {
        this.transactionValue = transactionValue;
    }

    public String getTransactionNotes() {
        return transactionNotes;
    }

    public void setTransactionNotes(String transactionNotes) {
        this.transactionNotes = transactionNotes;
    }

    public int getTransactionType() {
        return transactionType;
    }

    public void setTransactionType(int transactionType) {
        this.transactionType = transactionType;
    }

    public long getAccountId() {
        return accountId;
    }

    public void setAccountId(long accountId) {
        this.accountId = accountId;
    }

    public long getBudgetId() {
        return budgetId;
    }

    public void setBudgetId(long budgetId) {
        this.budgetId = budgetId;
    }

    public String getAccountName() {
        return accountName;
    }

    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}

и Дао по линиям: -

@Query("SELECT " +
        "transaction_id AS transactionId, " +
        "transaction_title AS transactionTitle, " +
        "transaction_date AS transactionDate, " +
        "transaction_value AS transactionValue, " +
        "transaction_notes AS transactionNotes, " +
        "transaction_type AS transactionType, " +
        "transaction_account_id AS accountId, " +
        "transaction_budget_id AS budgetId, " +
        "account_name As accountName, " +
        "(SELECT sum(transaction_value) FROM  transactions WHERE transaction_date <= '2019-09-20 12:00') AS balance" +
        " FROM transactions JOIN accounts ON transaction_account_id = account_id " +
        " WHERE account_id = :accountId")
List<TransactionWithAccountAndDerivedBalance> getFullTransaction(long accountId); 
  • Примечания
  • Имена переменных, используемые в классе TransactionWithAccountAndDerivedBalance, отличаются от имен столбцов, и поэтому имя столбца в наборе результатов должно бытьуказано (т. е. table_column_name AS class_member_variable_name).
  • Сюда также входит столбец, который использует функцию суммы в подзапросе.Отмечая, что это всегда будет возвращать то же значение, что и критерии выбора, дата жестко закодирована (для простоты / демонстрации).

Окончательное добавление

Появится предыдущий запросбыть тем, о чем вы просите (хотя транзакции по всем счетам будут суммироваться).Однако вполне вероятно, что вам нужен текущий баланс.

TransactionWithAccountAndDerivedBalance можно использовать без изменений.

Для текущего баланса (следовательно, почему предыдущий метод не сделал 'Вы должны быть в состоянии различать имена столбцов для подзапроса (использование transaction_date <= transaction_date будет возвращать каждую транзакцию, поскольку значение всегда будет одинаковым (как при проверке accountId)).

Это можно сделать, установив имя основного запроса и указав ссылки на столбцы, используя имя в качестве префикса.

В этом случае Dao может быть (используя main в качестве имени основного запроса): -

@Query("SELECT " +
        "main.transaction_id AS transactionId, " +
        "main.transaction_title AS transactionTitle, " +
        "main.transaction_date AS transactionDate, " +
        "main.transaction_value AS transactionValue, " +
        "main.transaction_notes AS transactionNotes, " +
        "main.transaction_type AS transactionType, " +
        "main.transaction_account_id AS accountId, " +
        "main.transaction_budget_id AS budgetId, " +
        "account_name As accountName, " +
        /* The SubQuery to get the balanace for the current transaction */
        "(" +
        "SELECT sum(transaction_value) " +
        "FROM  transactions " +
        "WHERE transaction_date <= main.transaction_date " +
        "AND main.transaction_account_id = transaction_account_id" +
        ") AS balance" +
        /* Back to the Main Query */
        " FROM transactions AS main JOIN accounts ON transaction_account_id = account_id " +
        " WHERE account_id = :accountId")
List<TransactionWithAccountAndDerivedBalance> getFullTransaction(long accountId);

Это вернет тот же результат, что и начальная часть ответа.

Например, используя: -

logOtherWay(mDBDao.getFullTransaction(1));

, где logOtherWay метод: -

private void logOtherWay(List<TransactionWithAccountAndDerivedBalance> twaadbList) {
    for (TransactionWithAccountAndDerivedBalance twaadb: twaadbList) {
        Log.d("TRANSOTHER","Account Name :" + twaadb.getAccountName() +
                " Title: " + twaadb.getTransactionTitle() +
                " Value " + twaadb.getTransactionValue() +
                " Balance: " + twaadb.getBalance()
        );
    }
}

Окончательный результат

Результат (вместе с результатом первоначального пути) будет: -

09-23 15:18:00.868 4079-4079/? D/TRANSLOG: Account Name: Account1 Title: Buy my first thing Value: -25.46 Balance: -25.46
09-23 15:18:00.868 4079-4079/? D/TRANSLOG: Account Name: Account1 Title: Buy my second thing Value: -65.3 Balance: -90.75999999999999
09-23 15:18:00.868 4079-4079/? D/TRANSLOG: Account Name: Account1 Title: Deposit Value: 100.0 Balance: 9.240000000000009



09-23 15:18:00.868 4079-4079/? D/TRANSOTHER: Account Name :Account1 Title: Buy my first thing Value -25.46 Balance: -25.46
09-23 15:18:00.868 4079-4079/? D/TRANSOTHER: Account Name :Account1 Title: Buy my second thing Value -65.3 Balance: -90.75999999999999
09-23 15:18:00.868 4079-4079/? D/TRANSOTHER: Account Name :Account1 Title: Deposit Value 100.0 Balance: 9.240000000000009
0 голосов
/ 23 сентября 2019

Я понял это, используя POJO AccountWithBalance:

@Parcelize
@Entity(tableName = Database.Accounts.TABLE_NAME)
data class AccountWithBalance(
    @Embedded val account: Account = Account(),
    @ColumnInfo(name = Database.Accounts.COL_BALANCE) val balance: Double = 0.0
) : Model {

    val id: Long
        get() = account.id

    val name: String
        get() = account.name
}

И следующий запрос:

@Query(
    """
        SELECT accounts.*, 
        (SELECT SUM(CASE WHEN transactions.type = ${Transaction.TransactionType.EARNING} OR transactions.type = ${Transaction.TransactionType.CLAIM} THEN transactions.value 
        ELSE -transactions.value END) FROM transactions) AS balance
        FROM accounts
        LEFT JOIN transactions ON transactions.account_id = accounts.id
        GROUP BY accounts.id
    """
)
suspend fun getAccountsWithBalance(): List<AccountWithBalance>
...