PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Friday, March 11, 2022

[FIXED] Query Builder: Subquery with multiple SUMs on associated data

 March 11, 2022     cakephp, cakephp-4.x     No comments   

Issue

I'd like to request advice for an approach to use the Query Builder to get multiple sums from associated models.

There are three tables:

invoices    invoice_items                               payment_receipts
--------    -------------                               -------------
id | name   id| invoice_id  | invoice_qty   unit_price  id| invoice_id  | receipt_amount
===|======  ==========================================  ================================    
 1 |INV01   1| 1            | 1300          |12.00      1 | 1           | 1000
 2 |INV02   2| 1            | 2600          |9.00       2 | 1           | 2000
 3 |INV03   3| 2            | 1100          |15.00      3 | 3           | 900
            4| 3            | 900           |12:00

For each invoice, I want the sum of the items' total amount (qty * price), and also the sum of payment receipts.

This query (with subqueries) correctly gets the result I'm looking for:

SELECT Invoices.id, Invoices.invoice_name, InvoiceItemSum.SumOfAmount, PaymentSum.SumOfPaymentAmount
  FROM Invoices
  INNER JOIN (
    SELECT invoice_id, SUM(Invoice_items.invoice_qty * Invoice_items.unit_price) AS SumOfAmount
      FROM Invoice_items
      GROUP BY Invoice_id
  ) InvoiceItemSum ON InvoiceItemSum.Invoice_id = Invoices.id
  LEFT JOIN (
    SELECT Invoice_id, SUM(Payment_receipts.receipt_amount) AS SumOfPaymentAmount
    FROM Payment_receipts
    GROUP BY Invoice_id
  ) PaymentSum ON PaymentSum.Invoice_id = Invoices.id
WHERE Invoices.invoice_id = 33

I can execute this query directly in my CakePhp app and get the results I need, so it works that way.

However I'd like advice on a more elegant CakePHP way to do this via the Query Builder.

I have tried this:

        $query = $this->Invoices->find()->contain(['InvoiceItems', 'PaymentReceipts']);
        $query->select([
                'Invoices.id',
                'Invoices.invoice_name',
            ]);
        $query->select([
            'total_inv_amt' => $query->func()->sum('InvoiceItems.invoice_qty * InvoiceItems.unit_price'),
            'total_paid_amt' => $query->func()->sum('PaymentReceipts.receipt_amount')
                ])
                ->innerJoinWith('InvoiceItems')
                ->leftJoinWith('PaymentReceipts')
                ->group(['Invoices.id']);
        $query->where(['Invoices.id' => 33]);

But this results in doubling the two sums via creating this query:

SELECT 
  Invoices.id AS Invoices__id, 
  Invoices.invoice_name AS Invoices__invoice_name, 
  (
    SUM(
      InvoiceItems.invoice_qty * InvoiceItems.unit_price
    )
  ) AS total_inv_amt, 
  (
    SUM(PaymentReceipts.receipt_amount)
  ) AS total_paid_amt 
FROM 
  invoices Invoices 
  INNER JOIN invoice_items InvoiceItems ON Invoices.id = (InvoiceItems.invoice_id) 
  LEFT JOIN payment_receipts PaymentReceipts ON Invoices.id = (PaymentReceipts.invoice_id) 
WHERE 
  Invoices.id = 33 
GROUP BY 
  Invoices.id

I've tried subqueries following the documentation with myriad unsuccessful results. I've also played with joins but still no dice.

My question is: what is a good approach to write this query using the Query Builder?

Thanks in advance for any advice!


Solution

OK, finally after reviewing Alimon's answer and several other questions on subqueries such as this and this, I've arrived at the correct Query Builder solution for this. Here it is:

        $subquery_a = $this->Invoices->InvoiceItems->find('all');
        $subquery_a
            ->select(['totalinvoiceamt' => $subquery_a->func()->sum('invoice_qty * unit_price') ])
            ->where([
                'InvoiceItems.invoice_id = Invoices.id'
            ]);
    
        $subquery_b = $this->Invoices->PaymentReceipts->find('all');
        $subquery_b
            ->select(['totalpaymentamt' => $subquery_b->func()->sum('receipt_amount') ])
            ->where([
                'PaymentReceipts.invoice_id = Invoices.id'
            ]);

        $query = $this->Invoices->find('all')
            ->select([
            'Invoices.id',
            'Invoices.invoice_name',
            'InvoiceItems__total_invoice_amount' => $subquery_a,
            'PaymentReceipts__total_payments_amount' => $subquery_b
            ])
            ->join([
                [
                    'table'     => 'invoice_items',
                    'alias'     => 'InvoiceItems',
                    'type'      => 'INNER',
                    'conditions'=> [
                        'Invoices.id = InvoiceItems.invoice_id'
                    ]
                ]
            ])
            ->join([
                [
                    'table'     => 'payment_receipts',
                    'alias'     => 'PaymentReceipts',
                    'type'      => 'LEFT',
                    'conditions'=> [
                        'Invoices.id = PaymentReceipts.invoice_id'
                    ]
                ]
            ])
            ->group('InvoiceItems.invoice_id');
            $query->where(['Invoices.id' => 33]);

The results are the same as the direct query, though the SQL looks a bit different from the manual one, but the results are identical.

Thanks Alimon et al for the assistance.



Answered By - dividedbyzero
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing