问题
I have a table that keeps track of changes in customer profiles. Here's a simplified version:
CREATE TABLE HISTORY (
CUSTOMER_ID NUMBER(9,0),
DATE_CHANGED DATE,
ACCOUNT_TYPE VARCHAR2(20),
CONSTRAINT HISTORY_PK PRIMARY KEY (CUSTOMER_ID, DATE_CHANGED)
);
INSERT INTO HISTORY (CUSTOMER_ID, DATE_CHANGED, ACCOUNT_TYPE) VALUES (200, TO_DATE('05/01/2013 00:00:00','DD/MM/RRRR HH24:MI:SS'), 'Premium');
INSERT INTO HISTORY (CUSTOMER_ID, DATE_CHANGED, ACCOUNT_TYPE) VALUES (300, TO_DATE('17/02/2013 00:00:00','DD/MM/RRRR HH24:MI:SS'), 'Free');
INSERT INTO HISTORY (CUSTOMER_ID, DATE_CHANGED, ACCOUNT_TYPE) VALUES (100, TO_DATE('05/03/2013 00:00:00','DD/MM/RRRR HH24:MI:SS'), 'Free');
INSERT INTO HISTORY (CUSTOMER_ID, DATE_CHANGED, ACCOUNT_TYPE) VALUES (100, TO_DATE('12/03/2013 00:00:00','DD/MM/RRRR HH24:MI:SS'), 'Standard');
INSERT INTO HISTORY (CUSTOMER_ID, DATE_CHANGED, ACCOUNT_TYPE) VALUES (200, TO_DATE('22/03/2013 00:00:00','DD/MM/RRRR HH24:MI:SS'), 'Standard');
INSERT INTO HISTORY (CUSTOMER_ID, DATE_CHANGED, ACCOUNT_TYPE) VALUES (100, TO_DATE('29/03/2013 00:00:00','DD/MM/RRRR HH24:MI:SS'), 'Premium');
That data is maintained by a third-party. My ultimate goal is to obtain a sum of customers per account type and month for a given timespan but, by now, I'd like to start with something simpler—display the latest account type for each month/customer combination where there are changes recorded:
YEAR MONTH CUSTOMER_ID ACCOUNT_TYPE
==== ===== =========== ============
2013 1 200 Premium
2013 2 300 Free
2013 3 100 Premium
2013 3 200 Standard
Here, customer 100 has made three changes on March; we display "Premium" because it has the latest date within March.
The query to obtain all rows would be this:
SELECT EXTRACT(YEAR FROM DATE_CHANGED) AS YEAR,
EXTRACT(MONTH FROM DATE_CHANGED) AS MONTH,
CUSTOMER_ID, ACCOUNT_TYPE
FROM HISTORY
ORDER BY YEAR, MONTH, CUSTOMER_ID, DATE_CHANGED
Is it possible to filter out unwanted rows using aggregate functions? Does it make more sense to use analytic functions?
(And, in either case, what would be the adequate function?)
Edit: I've been asked for an example of unwanted rows. There're 3 rows for customer 100 on March:
'05/03/2013 00:00:00', 'Free'
'12/03/2013 00:00:00', 'Standard'
'29/03/2013 00:00:00', 'Premium'
Unwanted rows are 'Free'
and 'Standard'
because they aren't the latest in the month.
回答1:
SELECT YEAR
,MONTH
,customer_id
,max(ACCOUNT_TYPE) keep(dense_rank FIRST ORDER BY date_changed DESC) LAST_ACC
FROM (
SELECT EXTRACT(YEAR FROM DATE_CHANGED) AS YEAR,
EXTRACT(MONTH FROM DATE_CHANGED) AS MONTH,
CUSTOMER_ID,
date_changed,
account_type
FROM HISTORY
)
GROUP BY YEAR, MONTH, customer_id
ORDER BY YEAR, MONTH, CUSTOMER_ID
| YEAR | MONTH | CUSTOMER_ID | LAST_ACC |
-----------------------------------------
| 2013 | 1 | 200 | Premium |
| 2013 | 2 | 300 | Free |
| 2013 | 3 | 100 | Premium |
| 2013 | 3 | 200 | Standard |
http://sqlfiddle.com/#!4/e493a/15
回答2:
SELECT DISTINCT
CUSTOMER_ID,
EXTRACT(YEAR FROM DATE_CHANGED) AS YEAR,
EXTRACT(MONTH FROM DATE_CHANGED) AS MONTH,
LAST_VALUE(ACCOUNT_TYPE)
OVER(PARTITION BY CUSTOMER_ID,TO_CHAR(DATE_CHANGED,'YYYY-MM') ORDER BY DATE_CHANGED ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS ACCOUNT_TYPE
FROM HISTORY
CUSTOMER_ID YEAR MONTH ACCOUNT_TYPE
200 2013 1 Premium
300 2013 2 Free
100 2013 3 Premium
200 2013 3 Standard
http://www.sqlfiddle.com/#!4/fab60/13
回答3:
SELECT YEAR, MONTH, CUSTOMER_ID, ACCOUNT_TYPE
FROM
(
SELECT EXTRACT(YEAR FROM DATE_CHANGED) AS YEAR,
EXTRACT(MONTH FROM DATE_CHANGED) AS MONTH,
CUSTOMER_ID,
ACCOUNT_TYPE,
ROW_NUMBER() OVER (PARTITION BY CUSTOMER_ID,
EXTRACT(YEAR FROM DATE_CHANGED),
EXTRACT(MONTH FROM DATE_CHANGED)
ORDER BY EXTRACT(YEAR FROM DATE_CHANGED) DESC,
EXTRACT(MONTH FROM DATE_CHANGED) DESC,
DATE_CHANGED DESC) RN
FROM HISTORY
)
WHERE RN = 1
ORDER BY YEAR, MONTH, CUSTOMER_ID
- SQLFiddle Demo
OUTPUT
╔══════╦═══════╦═════════════╦══════════════╗
║ YEAR ║ MONTH ║ CUSTOMER_ID ║ ACCOUNT_TYPE ║
╠══════╬═══════╬═════════════╬══════════════╣
║ 2013 ║ 1 ║ 200 ║ Premium ║
║ 2013 ║ 2 ║ 300 ║ Free ║
║ 2013 ║ 3 ║ 100 ║ Premium ║
║ 2013 ║ 3 ║ 200 ║ Standard ║
╚══════╩═══════╩═════════════╩══════════════╝
来源:https://stackoverflow.com/questions/16436516/row-with-latest-value-by-customer-and-month