简体   繁体   中英

Take two different values from a single SQL query

Is there a way to simplify/optimise this mysql/mariadb query? Ultimately I need two individual pieces of data from a single query: the latest transaction name and sum of all transaction payment amounts.

This works but is ugly because it repeats the JOINS and WHERE clauses:

    SELECT
        SUM(btp.allocated_amount),
        (
        SELECT
            bt.name
        FROM
            `tabBank Transaction Payments` as btp
        LEFT JOIN
            `tabBank Transaction` bt ON bt.name=btp.parent
        WHERE
            btp.payment_document = 'Journal Entry'
        AND
            bt.docstatus = 1
        ORDER BY
            bt.date desc
        LIMIT 1
        ) AS name
        FROM
            `tabBank Transaction Payments` as btp
        LEFT JOIN
            `tabBank Transaction` bt ON bt.name=btp.parent
        WHERE
            btp.payment_document = 'Journal Entry'
        AND
            bt.docstatus = 1;

I thought something like this would work but it doesn't. The name given is arbitrary and not the first according to the ORDER BY:

        SELECT
        (SELECT SUM(allocated_amount)),
        (SELECT name LIMIT 1)
        FROM
        (
        SELECT
            btp.allocated_amount,
            bt.name
        FROM
            `tabBank Transaction Payments` as btp
        LEFT JOIN
            `tabBank Transaction` bt ON bt.name=btp.parent
        WHERE
            btp.payment_document = 'Journal Entry'
        AND
            bt.docstatus = 1
        ORDER BY
            bt.date desc
        ) AS temp;

Edit:

Example data (NOTE: example has been simplified):

tabBank Transaction Payments

| parent | payment_document | allocated_amount |
------------------------------------------------
| doc1   | Journal Entry    | 10.00            |
| doc1   | Journal Entry20  | 4000.00          |
| doc2   | Journal Entry    | 20.00            |
| doc2   | Journal Entry20  | 5000.00          |
| doc3   | Journal Entry    | 30.00            |
| doc3   | Journal Entry20  | 6000.00          |

tabBank Transaction

| name | date       | docstatus |
---------------------------------
| doc1 | 2022-01-01 | 1         |
| doc2 | 2022-02-01 | 1         |
| doc3 | 2022-03-01 | 1         |

Required result: (60.00, doc3)

mariadb version: 10.2.27

We can try to use ROW_NUMBER & SUM window functions to make it.

ROW_NUMBER get the lastest row of name by bt.date desc

OVER clause is essential to window functions, that perform calculations based on a set of records and PARTITION BY defines the groups into which the rows are divided.

More detail we can see Window Function Concepts and Syntax

SELECT total_allocated_amount,name
FROM (
    SELECT
        SUM(btp.allocated_amount) OVER() total_allocated_amount,
        ROW_NUMBER() OVER(ORDER BY bt.date desc) rn,
        bt.name
    FROM
        `tabBank Transaction Payments` as btp
    LEFT JOIN
        `tabBank Transaction` bt ON bt.name=btp.parent
    WHERE
        btp.payment_document = 'Journal Entry'
    AND
        bt.docstatus = 1
) t1
WHERE rn = 1

sqlfiddle

Thanks to D-Shih for the suggestion and intro to window functions which I didn't know existed. I simplified and ended up with this.

For other scenarios where you need more than one result row due to partitioning, the ROW_NUMBER() will be required.

SELECT *
FROM (
    SELECT
        SUM(btp.allocated_amount) OVER() total_allocated_amount,
        FIRST_VALUE(bt.name) OVER(ORDER BY bt.date desc) latest_name
    FROM
        `tabBank Transaction Payments` as btp
    LEFT JOIN
        `tabBank Transaction` bt ON bt.name=btp.parent
    WHERE
        btp.payment_document = 'Journal Entry'
    AND
        bt.docstatus = 1
) t1
LIMIT 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM