我的客户可以拥有一个或多个帐户。我正在调用 2 个函数 (get_customer_balance 和 get_account_balance)来计算余额。
正如您从下面的测试案例中看到的,我在每个查询中调用函数两次。我想知道这些场景是否适合虚拟领域?如果可以的话,如何实现?
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';
CREATE TABLE CUSTOMERS (
CUSTOMER_ID, FIRST_NAME, LAST_NAME,IS_ACTIVE) AS SELECT 'L382059', 'Leo', 'Langford','Y' FROM DUAL UNION ALL
SELECT 'P382319', 'Tom', 'Micelli','Y' FROM DUAL UNION ALL
SELECT 'E379466', 'Bonnie', 'Winterbottom','Y' FROM DUAL UNION ALL
SELECT 'X060162', 'Lisa','Saladino','Y' FROM DUAL UNION ALL
SELECT 'Y331964', 'Sandy', 'Herring','Y' FROM DUAL UNION ALL
SELECT 'Z888555', 'Barbara', 'Broadwater','Y' FROM DUAL;
CREATE TABLE vendors AS
SELECT level AS vendor_id,
'Vendor ' || level AS vendor_name
FROM dual
CONNECT BY level <= 3;
CREATE TABLE CUSTOMER_ACCOUNTS (
ACCOUNT_NUMBER,
CUSTOMER_ID, VENDOR_ID,
IS_ACTIVE) AS
SELECT 'Z17ARWYYZRCU2Q2', 'P382319', 1, 'Y' FROM DUAL
UNION ALL
SELECT '0T81Z07CS6LXQ7Z', 'P382319', 3, 'Y' FROM DUAL
UNION ALL
SELECT 'YWYXC3Q5N9XZ7S', 'L382059', 1, 'Y' FROM DUAL UNION ALL
SELECT '612ZKAQ66VA3W3', 'Y331964', 3, 'Y' FROM DUAL UNION ALL
SELECT 'BCHD9TW78W67S1D', 'Z888555', 3, 'Y' FROM DUAL UNION ALL
SELECT '0HLS87LDR1TE8WB',
'X060162', 3, 'Y' FROM DUAL UNION ALL
SELECT 'Z69AG7DKS37UYU',
'X060162', 3, 'Y' FROM DUAL UNION ALL
SELECT 'B17ARWYYZRCU2Q2',
'X060162', 3, 'Y' FROM DUAL UNION ALL
SELECT 'THVQD6M9LR7AVK', 'E379466', 1, 'Y' FROM DUAL UNION ALL
SELECT '0Z76WT5NTLRZPTW',
'E379466', 1, 'Y' FROM DUAL;
create table transactions (
transaction_id NUMBER GENERATED BY DEFAULT AS IDENTITY,
account_number VARCHAR2(15),
transaction_type varchar2(1) DEFAULT 'C',
transaction_amount NUMBER(10,2),
transaction_date DATE DEFAULT SYSDATE
);
insert into transactions(
account_number, transaction_type, transaction_amount, transaction_date)
SELECT 'Z17ARWYYZRCU2Q2', 'D', (LEVEL * 1250.50), date '2023-05-14' + level * interval '5 15:13' day to minute from dual
connect by level <= 7
union all
SELECT 'Z17ARWYYZRCU2Q2', 'C', (LEVEL * 1175.75), date '2023-07-04' + level * interval '1 21:23' day to minute from dual
connect by level <= 5
union all
SELECT '0T81Z07CS6LXQ7Z', 'D', (LEVEL * 1250.50), date '2023-02-14' + level * interval '3 15:13' day to minute from dual
connect by level <= 17
union all
SELECT '0T81Z07CS6LXQ7Z', 'C', (LEVEL * 75.75), date '2023-02-04' + level * interval '2 21:23' day to minute from dual
connect by level <= 11
union all
select '612ZKAQ66VA3W3', 'D', 555.25 * LEVEL, (DATE '2023-07-13' + 13/24) + (level * 2) from dual
connect by level <= 25
UNION ALL
select '612ZKAQ66VA3W3', 'C', 555.25 * LEVEL, (DATE '2023-07-23' + 13/24) + (level * 2) from dual
connect by level <= 20
UNION ALL
SELECT '0HLS87LDR1TE8WB', 'D', (LEVEL * 1250.50), date '2023-05-04' + level * interval '1 15:13' day to minute from dual
connect by level <= 7
union all
SELECT '0HLS87LDR1TE8WB', 'C', (LEVEL * 1175.75), date '2023-05-04' + level * interval '1 15:13' day to minute from dual
connect by level <= 5
union all
SELECT 'Z69AG7DKS37UYU', 'D', ((LEVEL * 5) * 1750), date '2023-06-01' + level * interval '1 18:43:35' day to second from dual
connect by level <= 15
union all
SELECT 'Z69AG7DKS37UYU', 'C', ((LEVEL * 5) * 1750), date '2023-06-11' + level * interval '1 15:23:49' day to second from dual
connect by level <= 13;
CREATE OR REPLACE FUNCTION get_customer_balance
( i_customer_id IN customers.customer_id%TYPE
)
RETURN transactions.transaction_amount%TYPE
IS
v_balance transactions.transaction_amount%TYPE;
BEGIN
SELECT SUM (
CASE t.transaction_type
WHEN 'C'
THEN -t.transaction_amount
ELSE t.transaction_amount
END
)
INTO v_balance
FROM customer_accounts ca
JOIN transactions t ON t.account_number = ca.account_number
WHERE ca.customer_id = i_customer_id -- one customer
OR ca.customer_id IS NULL; -- all customers
RETURN v_balance;
END get_customer_balance;
/
CREATE OR REPLACE FUNCTION get_account_balance(
i_account_number IN TRANSACTIONS.ACCOUNT_NUMBER%TYPE
) RETURN TRANSACTIONS.TRANSACTION_AMOUNT%TYPE
IS
v_balance TRANSACTIONS.TRANSACTION_AMOUNT%TYPE;
BEGIN
SELECT SUM(
CASE transaction_type WHEN 'C' THEN -1 ELSE 1 END
* transaction_amount
)
INTO v_balance
FROM transactions
WHERE account_number = i_account_number -- one account
OR i_account_number IS NULL; -- all accounts
RETURN v_balance;
END;
/
SELECT
CA.ACCOUNT_NUMBER,
C.CUSTOMER_ID,
C.FIRST_NAME,
C.LAST_NAME,
get_account_balance(ca.account_number) AS balance
FROM CUSTOMER_ACCOUNTS CA
INNER JOIN customers c ON ca.customer_id = c.customer_id
WHERE get_account_balance(ca.account_number) > 200000;
ACCOUNT_NUMBER CUSTOMER_ID
FIRST_NAME LAST_NAME BALANCE
Z69AG7DKS37UYU X060162 Lisa Saladino 253750
SELECT C.CUSTOMER_ID,
C.FIRST_NAME,
C.LAST_NAME,
get_customer_balance(C.CUSTOMER_ID) AS balance
FROM customers c
WHERE get_customer_balance(C.CUSTOMER_ID) > 200000;
CUSTOMER_ID FIRST_NAME
LAST_NAME. BALANCE
P382319 Tom Micelli
203704.75
X060162 Lisa Saladino
271127.75
我建议只使用普通的 SQL 而不是函数。函数的使用需要对每个客户进行单独的查找和排序,如果您针对非常大的客户群运行此函数,则可能会陷入困境。试试这个,两者之间的唯一区别是额外的
GROUP BY
列:
-- customer balance
SELECT C.CUSTOMER_ID,
C.FIRST_NAME,
C.LAST_NAME,
SUM(CASE transaction_type WHEN 'C' THEN -1 ELSE 1 END * transaction_amount) customer_balance
FROM customers c
JOIN customer_accounts ca ON ca.customer_id = c.customer_id
JOIN transactions t ON t.account_number = ca.account_number
GROUP BY C.CUSTOMER_ID,
C.FIRST_NAME,
C.LAST_NAME
HAVING SUM(CASE transaction_type WHEN 'C' THEN -1 ELSE 1 END * transaction_amount) > 200000;
-- account balance
SELECT ca.account_number,
C.CUSTOMER_ID,
C.FIRST_NAME,
C.LAST_NAME,
SUM(CASE transaction_type WHEN 'C' THEN -1 ELSE 1 END * transaction_amount) account_balance
FROM customers c
JOIN customer_accounts ca ON ca.customer_id = c.customer_id
JOIN transactions t ON t.account_number = ca.account_number
GROUP BY ca.account_number,
C.CUSTOMER_ID,
C.FIRST_NAME,
C.LAST_NAME
HAVING SUM(CASE transaction_type WHEN 'C' THEN -1 ELSE 1 END * transaction_amount) > 200000;