【问题标题】:How to join a sum from another table如何连接另一个表的总和
【发布时间】:2019-09-22 21:47:18
【问题描述】:

我有一个包含帐户的表格和另一个包含交易的表格:

@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

我怎么能意识到这一点?

【问题讨论】:

    标签: android sqlite android-sqlite android-room


    【解决方案1】:

    您可以通过不使用联接而是通过访问各个对象以及查询来获取某个时间点的余额来实现这一点(假设您想要一个运行平衡)。

    所以你可以有以下道的

    @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);
    

    第一个返回帐户的交易。 第二个根据它的 id 返回一个帐户。 第三个将返回所提供日期的所有交易的总和以及所有以前的交易(因此是运行余额)。

    • 注释名称可能不同,但以上内容基于您的代码。

    示例

    您可以使用类似以下的内容(不是在 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)。
    • 这还包括在子查询中使用 sum 函数的列。请注意,这将始终返回与硬编码日期的选择标准相同的值(为了简单/演示)。

    最后加法

    上一个查询似乎就是您所要求的(尽管所有帐户的交易都会被求和)。但是,您可能需要一个流动的平衡。

    TransactionWithAccountAndDerivedBalance 无需更改即可使用。

    对于流动余额(因此以前的方法不关心帐户),您需要能够区分子查询的列名(使用 transaction_date &lt;= transaction_date 将返回每笔交易,因为值始终是相同(就像检查 accountId 一样))。

    这可以通过设置主查询的名称并使用该名称作为前缀来引用列来完成。

    Dao 可以是(使用 ma​​in 作为主查询的名称):-

    @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
    

    【讨论】:

    • 感谢您的输入,但我实际上想在查询中实现这一点。
    【解决方案2】:

    我是使用 AccountWithBalance POJO 实现的:

    @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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-05
      • 1970-01-01
      • 2021-03-22
      • 1970-01-01
      • 2011-06-18
      • 2017-12-12
      • 1970-01-01
      相关资源
      最近更新 更多