Analytic Functions in CONNECT BY Queries
Can analytic functions be used in a CONNECT BY query? Are there limits?
This problem occurs in Oracle 11.1.0.6.0, 10.2 and 10.1.
Here is the presenting problem:
Starting with data like this:
CREATE TABLE enrollment
( name VARCHAR2 (10)
, coursenumber NUMBER
INSERT INTO enrollment (name, coursenumber) VALUES ('Ted', 101);
INSERT INTO enrollment (name, coursenumber) VALUES ('Ted', 102);
INSERT INTO enrollment (name, coursenumber) VALUES ('Ted', 103);
INSERT INTO enrollment (name, coursenumber) VALUES ('Mary', 102);
INSERT INTO enrollment (name, coursenumber) VALUES ('Mary', 104);
INSERT INTO enrollment (name, coursenumber) VALUES ('Hiro', 101);
INSERT INTO enrollment (name, coursenumber) VALUES ('Hiro', 104);
INSERT INTO enrollment (name, coursenumber) VALUES ('Hiro', 105);
COMMIT;I'm trying to get cross-tab output like this:
NAME TXT
Hiro 101 104 105
Mary 102 104
Ted 101 102 103without knowing before-hand what course numbers, or even how many course numbers, will be in the results.
My strategy was to use LPAD to make the course numbers always occupy 5 spaces.
If n "columns" needed to be left blank before the number, I wanted to add 5n extra spaces.
I tried this:
WITH universe AS
SELECT name
, coursenumber
, DENSE_RANK () OVER ( ORDER BY coursenumber) AS cnum
, ROW_NUMBER () OVER ( PARTITION BY name
ORDER BY coursenumber
) AS snum
FROM enrollment
SELECT name
, REPLACE ( SYS_CONNECT_BY_PATH ( LPAD ( TO_CHAR (coursenumber)
, 5 * (cnum - LAG (cnum, 1, 0)
OVER ( PARTITION BY name
ORDER BY coursenumber
) AS txt
FROM universe
WHERE CONNECT_BY_ISLEAF = 1
START WITH snum = 1
CONNECT BY snum = PRIOR snum + 1
AND name = PRIOR name
ORDER BY name
;but the txt column was always NULL.
I tried showing some of the intermediate calculations:
WITH universe AS
SELECT name
, coursenumber
, DENSE_RANK () OVER ( ORDER BY coursenumber) AS cnum
, ROW_NUMBER () OVER ( PARTITION BY name
ORDER BY coursenumber
) AS snum
FROM enrollment
SELECT name
, REPLACE ( SYS_CONNECT_BY_PATH ( LPAD ( TO_CHAR (coursenumber)
, 5 * (cnum - LAG (cnum, 1, 0)
OVER ( PARTITION BY name
ORDER BY coursenumber
) AS txt
, coursenumber
, cnum
, LAG (cnum, 1, 0) OVER ( PARTITION BY name
ORDER BY coursenumber
) AS lag_cnum
FROM universe
-- WHERE CONNECT_BY_ISLEAF = 1
START WITH snum = 1
CONNECT BY snum = PRIOR snum + 1
AND name = PRIOR name
ORDER BY name
;and they all seemed reasonable:
NAME TXT COURSENUMBER CNUM LAG_CNUM
Hiro 101 1 0
Hiro 104 4 1
Hiro 105 5 4
Mary 102 2 0
Mary 104 4 2
Ted 101 1 0
Ted 102 2 1
Ted 103 3 2but txt was still NULL.
I got around the problem by computing the LAG in a sub-query (see [this thread|http://forums.oracle.com/forums/message.jspa?messageID=3875566#3875566]), but I'd like to know why LAG didn't work in the CONNECT BY query, or at least within SYS_CONNECT_BY_PATH.
I've had other problems before trying to use analytic functions in CONNECT BY queries. Sometimes, the presence of an analytic function woudl cause CONNECT BY to never work, sometimes it would destroy the order of the output. (Sorry, I don't have those examples handy now.)
Are there limitations on using analytic functions in a CONNECT BY query?
is there a work-around other than computing the analytic functions in a sub-query?
Thanks.
how about
SQL> with temp as
2 (select name
3 , coursenumber
4 from enrollment
5 )
6 , courses as
7 (select distinct
8 coursenumber
9 from enrollment
10 )
11 select name
12 , replace (sys_connect_by_path (case when t_course is not null
13 then rpad (t_course, 8, ' ')
14 else rpad (' ', 8, ' ')
15 end, ';'), ';', ' ') scbp
16 from (
17 select t.name
18 , t.coursenumber t_course
19 , c.coursenumber c_course
20 , row_number() over (partition by t.name
21 order by c.coursenumber
22 ) rn
23 from temp t partition by (name)
24 right outer
25 join courses c
26 on c.coursenumber = t.coursenumber
27 )
28 where connect_by_isleaf = 1
29 start with rn = 1
30 connect by rn = prior rn + 1
31 and name = prior name;
NAME SCBP
Hiro 101 104 105
Mary 102 104
Ted 101 102 103
Similar Messages
-
Analytic function to retrieve a value one year ago
Hello,
I'm trying to find an analytic function to get a value on another row by looking on a date with Oracle 11gR2.
I have a table with a date_id (truncated date), a flag and a measure. For each date, I have at least one row (sometimes 2), so it is gapless.
I would like to find analytic functions to show for each date :
sum of the measure for that date
sum of the measure one week ago
sum of the measure one year ago
As it is gapless I managed to do it the week doing a group by date in a subquery and using a LAG with offset set to 7 on top of it (see below).
However I'm struggling on how to do that for the data one year ago as we might have leap years. I cannot simply set the offset to 365.
Is it possible to do it with a RANGE BETWEEN window clause? I can't manage to have it working with dates.
Week :LAG with offset 7
SQL Fiddle
or
create table daily_counts
date_id date,
internal_flag number,
measure1 number
insert into daily_counts values ('01-Jan-2013', 0, 8014);
insert into daily_counts values ('01-Jan-2013', 1, 2);
insert into daily_counts values ('02-Jan-2013', 0, 1300);
insert into daily_counts values ('02-Jan-2013', 1, 37);
insert into daily_counts values ('03-Jan-2013', 0, 19);
insert into daily_counts values ('03-Jan-2013', 1, 14);
insert into daily_counts values ('04-Jan-2013', 0, 3);
insert into daily_counts values ('05-Jan-2013', 0, 0);
insert into daily_counts values ('05-Jan-2013', 1, 1);
insert into daily_counts values ('06-Jan-2013', 0, 0);
insert into daily_counts values ('07-Jan-2013', 1, 3);
insert into daily_counts values ('08-Jan-2013', 0, 33);
insert into daily_counts values ('08-Jan-2013', 1, 9);
commit;
select
date_id,
total1,
LAG(total1, 7) OVER(ORDER BY date_id) total_one_week_ago
from
select
date_id,
SUM(measure1) total1
from daily_counts
group by date_id
order by 1;
Year : no idea?
I can't give a gapless example, would be too long but if there is a solution with the date directly :
SQL Fiddle
or add this to the schema above :
insert into daily_counts values ('07-Jan-2012', 0, 11);
insert into daily_counts values ('07-Jan-2012', 1, 1);
insert into daily_counts values ('08-Jan-2012', 1, 4);
Thank you for your help.
FloydHi,
Sorry, I;m not sure I understand the problem.
If you are certain that there is at least 1 row for every day, then you can be sure that the GROUP BY will produce exactly 1 row per day, and you can use LAG (total1, 365) just like you already use LAG (total1, 7).
Are you concerned about leap years? That is, when the day is March 1, 2016, do you want the total_one_year_ago column to reflect March 1, 2015, which was 366 days earlier? If that case, use
date_id - ADD_MONTHS (date_id, -12)
instead of 365.
LAG only works with an exact number, but you can use RANGE BETWEEN with other analytic functions, such as MIN or SUM:
SELECT DISTINCT
date_id
, SUM (measure1) OVER (PARTITION BY date_id) AS total1
, SUM (measure1) OVER ( ORDER BY date_id
RANGE BETWEEN 7 PRECEDING
AND 7 PRECEDING
) AS total1_one_week_ago
, SUM (measure1) OVER ( ORDER BY date_id
RANGE BETWEEN 365 PRECEDING
AND 365 PRECEDING
) AS total1_one_year_ago
FROM daily_counts
ORDER BY date_id
Again, use date arithmetic instead of the hard-coded 365, if that's an issue.
As Hoek said, it really helps to post the exact results you want from the given sample data. You're miles ahead of the people who don't even post the sample data, though.
You're right not to post hundreds of INSERT statements to get a year's data. Here's one way to generate sample data for lots of rows at the same time:
-- Put a 0 into the table for every day in 2012
INSERT INTO daily_counts (date_id, measure1)
SELECT DATE '2011-12-31' + LEVEL
, 0
FROM dual
CONNECT BY LEVEL <= 366 -
Analytic Functions - Need resultset only in one select
Hello Experts,
Problem Definition: Using Analytic Function, get Total sales for the Product P1 and Customer C1 [Total sales for the customer itself] in one line. I want to restrict the ResultSet of the query to Product P1, please look at the data below, queries and problems..
Data
Customer Product Qtr Sales
C1 P1 19991 100.00
C1 P1 19992 125.00
C1 P1 19993 175.00
C1 P1 19994 300.00
C1 P2 19991 100.00
C1 P2 19992 125.00
C1 P2 19993 175.00
C1 P2 19994 300.00
C2 P1 19991 100.00
C2 P1 19992 125.00
C2 P1 19993 175.00
C2 P1 19994 300.00
Problem, I want to display....
Customer Product ProdSales CustSales
C1 P1 700 1400
But Without using outer query, i.e. please look below for the query that returns this reult with two select, I want this result in one query only..
Select * From ----*** want to avoid this... ***----
(Select Customer,Product,
Sum(Sales) ProdSales,
Sum(Sum(Sales)) Over(Partition By Customer) CustSales
From t1
Where customer='C1')
Where
Product='P1' ;
Also, I want to avoid Hard coding of P1 in the select clause....
I mean, I can do it in one shot/select, but look at the query below, it uses P1 in the select clause, which is No No!! P1 is allowed only in Where or Having ..
Select Customer,Decode(Product, 'P1','P1','P1') Product,
Decode(Product,'P1',Sales,0) ProdSales,
Sum(Sum(Sales)) Over (Partition By Customer ) CustSales
From t1
Where customer='C1' ;
This will get me what I want, but as I said earlier, I want to avoid using P1 in the
Select clause..
Goal is to Avoid using
1-> Two Select/Outer Query/In Line Views
2-> Product 'P1' in the Select clause...No hard coded product name in the select clause and group by clause..
Thanks
-DhavalSelect * From ----*** want to avoid this... ***----
(Select Customer,Product,
Sum(Sales) ProdSales,
Sum(Sum(Sales)) Over(Partition By Customer)
CustSales
From t1
Where customer='C1')
Where
Product='P1' ;
Goal is to Avoid using
1-> Two Select/Outer Query/In Line ViewsWhy? -
Hi,
Please find below table structure and insert scritps. Requesting for vluable help.
create table temp2 (col1 number,col2 varchar2(10),col3 number,col4 varchar2(20));
insert into temp2 values (1,'a',100,'vvv');
insert into temp2 values (2,'b',200,'www');
insert into temp2 values (3,'c',300,'xxx');
insert into temp2 values (4,'d',400,'yyy');
insert into temp2 values (5,'e',500,'zzz');
insert into temp2 values (6,'f',600,'aaa');
insert into temp2 values (7,'g',700,'bbb');
insert into temp2 values (8,'h',800,'ccc');
I am trying to get same output, what we get from below UNION query with ANALYTICAL Function.
select * from temp2 where col1 in (1,2,3,4,5)
union
select * from temp2 where col1 in (1,2,5,6)
union
select * from temp2 where col1 in (1,2,7,8);
I am seeking help by this dummy example to understand the concept, how can we use analytical functional over UNION or OUTER JOINS.
In my exact query, I am using same table three times adding UNION clause. here also we scan temp2 three times, so for bulky tables using 'union' would be hampering query's performance
It means i go with three time scans of same table that is not performance oriented. With the help of above required concept, i will try to remove UNIONs from my exact query.
Thanks!!Thanks for your time BluShadow and sorry as i think i couldn't make my query clear.
I try it again. Below there are three queries, you may see all three queries are using same tables. Difference in all three queries are just few conditions, which makes all three queries diff with each other.
I know, u cant run below query in your database, but i think it will convey my doubt to you. I have mentioned no. of rows with each clause and total i am getting 67 rows as my output. (Reason may be first n third query's result set are the subset of Second Query dataset)
So i want to take all common rows as well as additional rows, if present in any of the query. This is getting easliy done with UNION clause but want to have it in other way as here my same is getting scanned again n again.
SELECT
START_TX.FX_TRAN_ID START_FX_TRAN_ID
,END_TX.FX_TRAN_ID END_FX_TRAN_ID
,START_TX.ENTERED_DT_TS
,USER
,START_TX.TRADE_DT
,START_TX.DEAL_NUMBER
,START_TX.FX_DEAL_TYPE
,START_TX.ORIENTATION_BUYSELL
,START_TX.BASE_CCY
,START_TX.BASE_CCY_AMT
,START_TX.SECONDARY_CCY
,START_TX.SECONDARY_CCY_AMT
,START_TX.MATURITY_DT
,START_TX.TRADE_RT
,START_TX.FORWARD_PTS
,START_TX.CORPORATE_PIPS
,START_TX.DEAL_OWNER_INITIALS
,START_TX.CORPORATE_DEALER
,START_TX.PROFIT_CENTER_CD
,START_TX.COUNTERPARTY_NM
,START_TX.COUNTERPARTY_NUMBER
FROM
(SELECT * FROM FX_TRANSACTIONS WHERE GMT_CONV_ENTERED_DT_TS >= TO_DATE('20-Nov-2013 4:00:01 AM','DD-Mon-YYYY HH:MI:SS AM')) START_TX
INNER JOIN
(SELECT * FROM FX_TRANSACTIONS WHERE GMT_CONV_ENTERED_DT_TS <= TO_DATE('20-Nov-2013 4:59:59 PM','DD-Mon-YYYY HH:MI:SS AM')) END_TX
ON START_TX.COUNTERPARTY_NM = END_TX.COUNTERPARTY_NM AND
START_TX.COUNTERPARTY_NUMBER = END_TX.COUNTERPARTY_NUMBER AND
START_TX.FX_DEAL_TYPE = END_TX.FX_DEAL_TYPE AND
START_TX.BASE_CCY = END_TX.BASE_CCY AND
START_TX.SECONDARY_CCY = END_TX.SECONDARY_CCY AND
NVL(START_TX.CORPORATE_DEALER,'nullX')=NVL(END_TX.CORPORATE_DEALER,'nullX') AND
START_TX.ORIENTATION_BUYSELL='B' AND
END_TX.ORIENTATION_BUYSELL='S' AND
START_TX.FX_TRAN_ID = 1850718 AND
(START_TX.BASE_CCY_AMT = END_TX.BASE_CCY_AMT
OR
START_TX.SECONDARY_CCY_AMT = END_TX.SECONDARY_CCY_AMT) -- 10 Rows
UNION
SELECT
START_TX.FX_TRAN_ID START_FX_TRAN_ID
,END_TX.FX_TRAN_ID END_FX_TRAN_ID
,START_TX.ENTERED_DT_TS
,USER
,START_TX.TRADE_DT
,START_TX.DEAL_NUMBER
,START_TX.FX_DEAL_TYPE
,START_TX.ORIENTATION_BUYSELL
,START_TX.BASE_CCY
,START_TX.BASE_CCY_AMT
,START_TX.SECONDARY_CCY
,START_TX.SECONDARY_CCY_AMT
,START_TX.MATURITY_DT
,START_TX.TRADE_RT
,START_TX.FORWARD_PTS
,START_TX.CORPORATE_PIPS
,START_TX.DEAL_OWNER_INITIALS
,START_TX.CORPORATE_DEALER
,START_TX.PROFIT_CENTER_CD
,START_TX.COUNTERPARTY_NM
,START_TX.COUNTERPARTY_NUMBER
FROM
(SELECT * FROM FX_TRANSACTIONS WHERE GMT_CONV_ENTERED_DT_TS >= TO_DATE('20-Nov-2013 4:00:01 AM','DD-Mon-YYYY HH:MI:SS AM')) START_TX
INNER JOIN
(SELECT * FROM FX_TRANSACTIONS WHERE GMT_CONV_ENTERED_DT_TS <= TO_DATE('20-Nov-2013 4:59:59 PM','DD-Mon-YYYY HH:MI:SS AM')) END_TX
ON START_TX.COUNTERPARTY_NM = END_TX.COUNTERPARTY_NM AND
START_TX.COUNTERPARTY_NUMBER = END_TX.COUNTERPARTY_NUMBER AND
START_TX.FX_DEAL_TYPE = END_TX.FX_DEAL_TYPE AND
START_TX.BASE_CCY = END_TX.BASE_CCY AND
START_TX.SECONDARY_CCY = END_TX.SECONDARY_CCY AND
NVL(START_TX.CORPORATE_DEALER,'nullX')=NVL(END_TX.CORPORATE_DEALER,'nullX') AND
START_TX.FX_TRAN_ID = 1850718 AND
START_TX.ORIENTATION_BUYSELL='B' AND
END_TX.ORIENTATION_BUYSELL='S' -- 67 Rows
UNION
SELECT
START_TX.FX_TRAN_ID START_FX_TRAN_ID
,END_TX.FX_TRAN_ID END_FX_TRAN_ID
,START_TX.ENTERED_DT_TS
,USER
,START_TX.TRADE_DT
,START_TX.DEAL_NUMBER
,START_TX.FX_DEAL_TYPE
,START_TX.ORIENTATION_BUYSELL
,START_TX.BASE_CCY
,START_TX.BASE_CCY_AMT
,START_TX.SECONDARY_CCY
,START_TX.SECONDARY_CCY_AMT
,START_TX.MATURITY_DT
,START_TX.TRADE_RT
,START_TX.FORWARD_PTS
,START_TX.CORPORATE_PIPS
,START_TX.DEAL_OWNER_INITIALS
,START_TX.CORPORATE_DEALER
,START_TX.PROFIT_CENTER_CD
,START_TX.COUNTERPARTY_NM
,START_TX.COUNTERPARTY_NUMBER
FROM
(SELECT * FROM FX_TRANSACTIONS WHERE GMT_CONV_ENTERED_DT_TS >= TO_DATE('20-Nov-2013 4:00:01 AM','DD-Mon-YYYY HH:MI:SS AM')) START_TX
INNER JOIN
(SELECT * FROM FX_TRANSACTIONS WHERE GMT_CONV_ENTERED_DT_TS <= TO_DATE('20-Nov-2013 4:59:59 PM','DD-Mon-YYYY HH:MI:SS AM')) END_TX
ON START_TX.COUNTERPARTY_NM = END_TX.COUNTERPARTY_NM AND
START_TX.COUNTERPARTY_NUMBER = END_TX.COUNTERPARTY_NUMBER AND
START_TX.FX_DEAL_TYPE = END_TX.FX_DEAL_TYPE AND
START_TX.BASE_CCY = END_TX.BASE_CCY AND
START_TX.SECONDARY_CCY = END_TX.SECONDARY_CCY AND
NVL(START_TX.CORPORATE_DEALER,'nullX')=NVL(END_TX.CORPORATE_DEALER,'nullX') AND
START_TX.ORIENTATION_BUYSELL='B' AND
END_TX.ORIENTATION_BUYSELL='S' AND
START_TX.FX_TRAN_ID = 1850718 AND
END_TX.BASE_CCY_AMT BETWEEN (START_TX.BASE_CCY_AMT - (START_TX.BASE_CCY_AMT * :PERC_DEV/100)) AND (START_TX.BASE_CCY_AMT + (START_TX.BASE_CCY_AMT * :PERC_DEV/100))
OR
END_TX.SECONDARY_CCY_AMT BETWEEN (START_TX.SECONDARY_CCY_AMT - (START_TX.SECONDARY_CCY_AMT*:PERC_DEV/100) ) AND (START_TX.SECONDARY_CCY_AMT + (START_TX.SECONDARY_CCY_AMT*:PERC_DEV/100))
); --- 10 Rows -
Restrict Query Resultset which uses Analytic Function
Gents,
Problem Definition: Using Analytic Function, get Total sales for the Product P1
and Customer C1 [Total sales for the customer itself] in one line.
I want to restrict the ResultSet of the query to Product P1,
please look at the data below, queries and problems..
Data
Customer Product Qtr Sales
C1 P1 19991 100.00
C1 P1 19992 125.00
C1 P1 19993 175.00
C1 P1 19994 300.00
C1 P2 19991 100.00
C1 P2 19992 125.00
C1 P2 19993 175.00
C1 P2 19994 300.00
C2 P1 19991 100.00
C2 P1 19992 125.00
C2 P1 19993 175.00
C2 P1 19994 300.00
Problem, I want to display....
Customer Product ProdSales CustSales
C1 P1 700 1400
But Without using outer query, i.e. please look below for the query that
returns this reult with two select, I want this result in one query only..
Select * From ----*** want to avoid this... ***----
(Select Customer,Product,
Sum(Sales) ProdSales,
Sum(Sum(Sales)) Over(Partition By Customer) CustSales
From t1
Where customer='C1')
Where
Product='P1' ;
Also, I want to avoid Hard coding of P1 in the select clause....
I mean, I can do it in one shot/select, but look at the query below, it uses
P1 in the select clause, which is No No!! P1 is allowed only in Where or Having ..
Select Customer,Decode(Product, 'P1','P1','P1') Product,
Decode(Product,'P1',Sales,0) ProdSales,
Sum(Sum(Sales)) Over (Partition By Customer ) CustSales
From t1
Where customer='C1' ;
This will get me what I want, but as I said earlier, I want to avoid using P1 in the
Select clause..
Goal is to Avoid using
1-> Two Select/Outer Query/In Line Views
2-> Product 'P1' in the Select clause...
Thanks
-Dhaval RasaniaI don't understand goal number 1 of not using an inline view.
What is the harm? -
My first real analytic function... any unexpected results?
Hello all. I have a table that contains transactions from bank accounts. The columns I am concerned with (I think) are the account number and the status date.
The status date has the date that the transaction cleared through the bank. I would like a query that returns all rows for an account that have cleared since the last reconciliation of that account. (the reconciliation will occur monthly)
This will produce some test data that replicates what we'll have in this table.
DROP TABLE dave_test;
DROP TABLE dave_test succeeded.
CREATE TABLE dave_test AS
SELECT level id, ROUND(TO_NUMBER(level), -1) account, TO_DATE('2007-08-01','YYYY-MM-DD') test_date
FROM DUAL
CONNECT BY LEVEL < 20 UNION ALL
SELECT 21, 10, TO_DATE('2007-07-01','YYYY-MM-DD') FROM DUAL UNION ALL
SELECT 22, 10, TO_DATE('2007-06-01','YYYY-MM-DD') FROM DUAL UNION ALL
SELECT 23, 0, TO_DATE('2007-09-01', 'YYYY-MM-DD') FROM DUAL;
CREATE TABLE succeeded.
SELECT * FROM dave_test ORDER BY id;
ID ACCOUNT TEST_DATE
1 0 01-AUG-07
2 0 01-AUG-07
3 0 01-AUG-07
4 0 01-AUG-07
5 10 01-AUG-07
6 10 01-AUG-07
7 10 01-AUG-07
8 10 01-AUG-07
9 10 01-AUG-07
10 10 01-AUG-07
11 10 01-AUG-07
12 10 01-AUG-07
13 10 01-AUG-07
14 10 01-AUG-07
15 20 01-AUG-07
16 20 01-AUG-07
17 20 01-AUG-07
18 20 01-AUG-07
19 20 01-AUG-07
21 10 01-JUL-07
22 10 01-JUN-07
23 0 01-SEP-07
22 rows selected
I have developed a query that returns accurate results for my test data. My request is this:
Will you look over this query and see if there is a better way of doing things? This is my first real attempt with an analytic function, so I would appreciate some input on anything that looks like it could be improved. Also, perhaps some test cases that might produce results I haven't thought of.
Thank you for your time.
SELECT
id
,account
,test_date
,max(date_sort)
FROM
SELECT
id id
,account account
,test_date test_date
,CASE DENSE_RANK() OVER(PARTITION BY account ORDER BY TRUNC(test_date, 'DD') DESC)
WHEN 1 THEN TO_DATE('1', 'J')
WHEN 2 THEN test_date
ELSE NULL
END date_sort
FROM
dave_test
WHERE
account = &account_number
HAVING
test_date > MAX(date_sort)
GROUP BY
id
,account
,test_date
ORDER BY
idRun with 0 as account number:
ID ACCOUNT TEST_DATE MAX(DATE_SORT)
23 0 01-SEP-07 01-JAN-13
1 rows selectedRun with 10 as account number
ID ACCOUNT TEST_DATE MAX(DATE_SORT)
5 10 01-AUG-07 01-JAN-13
6 10 01-AUG-07 01-JAN-13
7 10 01-AUG-07 01-JAN-13
8 10 01-AUG-07 01-JAN-13
9 10 01-AUG-07 01-JAN-13
10 10 01-AUG-07 01-JAN-13
11 10 01-AUG-07 01-JAN-13
12 10 01-AUG-07 01-JAN-13
13 10 01-AUG-07 01-JAN-13
14 10 01-AUG-07 01-JAN-13
10 rows selectedRun with 20 as account_number
ID ACCOUNT TEST_DATE MAX(DATE_SORT)
15 20 01-AUG-07 01-JAN-13
16 20 01-AUG-07 01-JAN-13
17 20 01-AUG-07 01-JAN-13
18 20 01-AUG-07 01-JAN-13
19 20 01-AUG-07 01-JAN-13
5 rows selectedLet me know if I need to clarify anything.Sorry, Volder, for being unclear.
Here is the table the query is based on.
desc bank_account_transactions
Name Null Type
ID NOT NULL NUMBER(28)
BKA_ID NOT NULL NUMBER(28)
BKATC_ID NOT NULL NUMBER(28)
ST_TABLE_SHORT_NAME VARCHAR2(10)
KEY_VALUE NUMBER(28)
EDF_ID NUMBER(28)
GLFS_ID NOT NULL NUMBER(28)
GLTT_ID NUMBER(28)
AMOUNT NOT NULL NUMBER(11,2)
PAYMENT_NUMBER NUMBER(9)
BANK_SERIAL_NUMBER NUMBER(15)
PAYEE_NAME VARCHAR2(60)
STATUS NOT NULL VARCHAR2(1)
STATUS_DATE DATE
EFFECTIVE_DATE NOT NULL DATE
POSITIVE_PAY_DATE DATE
DATA_SOURCE NOT NULL VARCHAR2(1)
REPORTED_TO_ACCOUNT_OWNER NOT NULL VARCHAR2(1)
PAYEE_BANK_ACCOUNT_NUMBER NUMBER(30)
PAYEE_BANK_ABA_NUMBER NUMBER(9)
DESCRIPTION VARCHAR2(4000)
DATE_CREATED NOT NULL DATE
CREATED_BY NOT NULL VARCHAR2(30)
DATE_MODIFIED DATE
MODIFIED_BY VARCHAR2(30)
25 rows selectedThe bka_id is the account number, status is 'C' for cleared checks and the status_date is the date the check cleared.
When I reconcile, I set the status to 'C' and set the status_date to SYSDATE. So the "last reconciliation date" is stored in status_date.
Like so
ID Account_No status_date
1 10 05-04-07
2 10 05-04-07
3 10 05-04-07
4 20 05-04-07
5 20 05-04-07
6 10 06-03-07
7 10 06-03-07
8 20 06-03-07
9 10 07-05-07
10 10 07-05-07In this example, account 10 was reconciled on May 5, June 3, and July 5. So the previous reconciliation date would be 06-03-07, and my report would return the transactions from 07-05-07.
For account 20, it was reconciled on May 5 and June 3. The previous reconciliation date would be 05-04-07, and the transactions from 06-03-07 would be reported.
Does this help?
I appreciate your time. -
Problem with SUM () analytic function
Dear all,
Please have a look at my problem.
SELECT CURR, DT, AMT, RATE,
SUM(AMT) OVER (PARTITION BY CURR ORDER BY DT) SUMOVER,
sum( amt * rate) over (PARTITION BY CURR ORDER BY DT) / SUM(AMT) OVER (PARTITION BY CURR ORDER BY DT) avgrt
FROM
select 'CHF' CURR, ADD_MONTHS(TO_DATE('01-DEC-07'), LEVEL -1) DT, 100 * LEVEL AMT, 1 + ( 5* LEVEL/100) RATE
FROM DUAL CONNECT BY LEVEL < 10
SQL> /
CUR DT AMT RATE SUMOVER AVGRT
CHF 01-DEC-07 100 1.05 100 1.05
CHF 01-JAN-08 200 1.1 300 1.08333333
CHF 01-FEB-08 300 1.15 600 1.11666667
CHF 01-MAR-08 400 1.2 1000 1.15
CHF 01-APR-08 500 1.25 1500 1.18333333
CHF 01-MAY-08 600 1.3 2100 1.21666667
CHF 01-JUN-08 700 1.35 2800 1.25
CHF 01-JUL-08 800 1.4 3600 1.28333333
CHF 01-AUG-08 900 1.45 4500 1.31666667
Table Revaluation
select 'CHF' CURR1, '31-DEC-07' DT , 1.08 RATE FROM DUAL UNION ALL
select 'CHF' CURR1, '31-MAR-08' DT , 1.22 RATE FROM DUAL UNION ALL
select 'CHF' CURR1, '30-JUN-08' DT , 1.38 RATE FROM DUAL
CUR DT RATE
CHF 31-DEC-07 1.08
CHF 31-MAR-08 1.22
CHF 30-JUN-08 1.38.
Problem is with the calculation of average rate.
I want to consider the data in the revaluation table to be used in the calculation of
average rate.
So average rate for Jan-08 will be
(100 * 1.08(dec revaluation rate) + 200 * 1.1 ) / (300) = 1.093333333
for Feb-08
(100 * 1.08(dec revaluation rate) + 200 * 1.1 + 300 * 1.15) / (600) = 1.121666667
for mar-08
(100 * 1.08(dec revaluation rate) + 200 * 1.1 + 300 * 1.15 + 400 * 1.2) / (1000) = 1.153
for Apr-08
(1000 * 1.22(Apr revaluation rate) + 500 * 1.25) /1500 = 1.23
for May-08
(1000 * 1.22(Apr revaluation rate) + 500 * 1.25 + 600 * 1.30 ) /2100 = 1.25
and so on..
Kindly adviceHi,
The main thing in this problem is that for every dt you want to compute the cumulative total from previous rows using the formula
SUM (amt * rate)
But rate can be either the rate from the revaluation table or the rate from the main table. For evaluating prior dates, you wnat to use the most recent rate.
I'm not sure if you can do this using analytic functions. Like Damorgan said, you should use a self-join.
The query below gives you the results you requested:
WITH
revaluation AS
SELECT 'CHF' curr1, TO_DATE ('31-DEC-07', 'DD-MON-RR') dt, 1.08 rate FROM dual UNION ALL
SELECT 'CHF' curr1, TO_DATE ('31-MAR-08', 'DD-MON-RR') dt, 1.22 rate FROM dual UNION ALL
SELECT 'CHF' curr1, TO_DATE ('30-JUN-08', 'DD-MON-RR') dt, 1.38 rate FROM dual
original_data AS
select 'CHF' curr
, ADD_MONTHS(TO_DATE('01-DEC-07'), LEVEL -1) dt
, 100 * LEVEL amt
, 1 + ( 5* LEVEL/100) rate
FROM dual
CONNECT BY LEVEL < 10
two_rates AS
SELECT od.*
SELECT MAX (dt)
FROM revaluation
WHERE curr1 = od.curr
AND dt <= od.dt
) AS r_dt
SELECT AVG (rate) KEEP (DENSE_RANK LAST ORDER BY dt)
FROM revaluation
WHERE curr1 = od.curr
AND dt <= od.dt
) AS r_rate
FROM original_data od
SELECT c.curr
, c.dt
, c.amt
, c.rate
, SUM (p.amt) AS sumover
, SUM ( p.amt
* CASE
WHEN p.dt <= c.r_dt
THEN c.r_rate
ELSE p.rate
END
/ SUM (p.amt) AS avgrt
FROM two_rates c
JOIN original_data p ON c.curr = p.curr
AND c.dt >= p.dt
GROUP BY c.curr, c.dt, c.amt, c.rate
ORDER BY c.curr, c.dt
; -
SQL Analytical Functions in 9i
Hi
I am trying to determine of the SQL Analytical Functions in Oracle 9i like LAG, NTILE, PERCENT_RANK etc are part of Enterprise without OLAP installed.
They will be very useful to a set of queries I am trying to build but I don't want to add OLAP to the mix if I can help it as the customer won't be OLAP enabled
CheersHi
I am trying to determine of the SQL Analytical Functions in Oracle 9i like LAG, NTILE, PERCENT_RANK etc are part of Enterprise without OLAP installed.
They will be very useful to a set of queries I am trying to build but I don't want to add OLAP to the mix if I can help it as the customer won't be OLAP enabled
Cheers -
How can we write this in analytical function..
select a.employee_id,a.last_name,b.count from employees a, (select manager_id, count(manager_id) as count from employees group by manager_id) b where a.employee_id=b.manager_id;
As per my requirement I need each manager name and no of employees reporting to him... above query works.. Could anybody help to write the same using analytic function.... Hw this same can be written in effiect way??? (quick performance)
Please also share the link to download some doc to have good understanding of analytical function..
Thanks in advance....are you trying to do a hierarchical type of query?
select ename, count(ename) -1 numr_of_emps_under_this_mgr from emp
connect by empno =prior mgr
group by ename
order by count(ename) desc ;
ENAME NUMR_OF_EMPS_UNDER_THIS_MGR
KING 13
BLAKE 5
JONES 4
CLARK 1
FORD 1
SCOTT 1
ADAMS 0
TURNER 0
MARTIN 0
JAMES 0
SMITH 0
MILLER 0
ALLEN 0
WARD 0Here is the table structure I used (I think you can download it from oracle somewhere)
CREATE TABLE EMP
EMPNO NUMBER(4) NOT NULL,
ENAME VARCHAR2(10 BYTE),
JOB VARCHAR2(9 BYTE),
MGR NUMBER(4),
HIREDATE DATE,
SAL NUMBER(7,2),
COMM NUMBER(7,2),
DEPTNO NUMBER(2)
SET DEFINE OFF;
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO)
Values
(7369, 'SMITH', 'CLERK', 7902, TO_DATE('12/17/1980 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
800, 20);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
Values
(7499, 'ALLEN', 'SALESMAN', 7698, TO_DATE('02/20/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
1600, 300, 30);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
Values
(7521, 'WARD', 'SALESMAN', 7698, TO_DATE('02/22/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
1250, 500, 30);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO)
Values
(7566, 'JONES', 'MANAGER', 7839, TO_DATE('04/02/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
2975, 20);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
Values
(7654, 'MARTIN', 'SALESMAN', 7698, TO_DATE('09/28/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
1250, 1400, 30);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO)
Values
(7698, 'BLAKE', 'MANAGER', 7839, TO_DATE('05/01/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
2850, 30);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO)
Values
(7782, 'CLARK', 'MANAGER', 7839, TO_DATE('06/09/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
2450, 10);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO)
Values
(7788, 'SCOTT', 'ANALYST', 7566, TO_DATE('12/09/1982 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
3000, 20);
Insert into EMP
(EMPNO, ENAME, JOB, HIREDATE, SAL, DEPTNO)
Values
(7839, 'KING', 'PRESIDENT', TO_DATE('11/17/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
5000, 10);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
Values
(7844, 'TURNER', 'SALESMAN', 7698, TO_DATE('09/08/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
1500, 0, 30);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO)
Values
(7876, 'ADAMS', 'CLERK', 7788, TO_DATE('01/12/1983 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
1100, 20);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO)
Values
(7900, 'JAMES', 'CLERK', 7698, TO_DATE('12/03/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
950, 30);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO)
Values
(7902, 'FORD', 'ANALYST', 7566, TO_DATE('12/03/1981 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
3000, 20);
Insert into EMP
(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO)
Values
(7934, 'MILLER', 'CLERK', 7782, TO_DATE('01/23/1982 00:00:00', 'MM/DD/YYYY HH24:MI:SS'),
1300, 10);
COMMIT; -
Alternate for analytic functions
Hello All,
I'm trying to write a query without using analytic functions.
Using Analytic func,
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
"CORE 11.2.0.2.0 Production"
TNS for Linux: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production
SELECT id, sal, rank() OVER (PARTITION BY ID ORDER BY SAL) rnk FROM
(SELECT 10 AS id, 100 AS sal FROM DUAL
UNION ALL
SELECT 10, 300 FROM DUAL
UNION ALL
SELECT 10, 400 FROM DUAL
UNION ALL
SELECT 20, 200 FROM DUAL
UNION ALL
SELECT 20, 200 FROM DUAL
UNION ALL
SELECT 20, 300 FROM DUAL
UNION ALL
SELECT 30, 100 FROM DUAL
UNION ALL
SELECT 40, 100 FROM DUAL
UNION ALL
SELECT 40, 200 FROM DUAL
) Expected results. I want these results without analytic functions.
10 100 1
10 300 2
10 400 3
20 200 1
20 200 1
20 300 3
30 100 1
40 100 1
40 200 2Hi,
SamFisher wrote:
Thank You Frank. That was simple.
I was trying to get the reults without using analytical functions. Just trying to improve my SQL skills. Yes, I admit that practicising using the wrong tools can improve your SQL skills, but I think there's a lot to be said for practising using the right tools, too.
I tried all sort of things. I thought hierarchical query would do it but hard luck for me.Do you want to use a CONNECT BY query for this? Here's one way:
WITH got_max_level AS
SELECT id
, sal
, MAX (LEVEL) AS max_level
FROM table_x
CONNECT BY NOCYCLE id = PRIOR id
AND sal >= PRIOR sal
AND ( sal > PRIOR sal
OR ROWID > PRIOR ROWID
GROUP BY id
, sal
, got_cnt AS
SELECT id
, sal
, COUNT (*) AS cnt
FROM table_x
GROUP BY id
, sal
SELECT x.id
, x.sal
, l.max_level + 1 - c.cnt AS rnk
FROM table_x x
JOIN got_max_level l ON x.id = l.id
AND x.sal = l.sal
JOIN got_cnt c ON x.id = c.id
AND x.sal = c.sal
ORDER BY x.id
, x.sal
;This is even less efficient, as well as more complicated, than the scalar sub-query solution. -
Advantages and disadvantages of Analytical function
Plz list out the advantages and disadvantages of normal queries and queries using analytical function (Performance wise)
I'm not sure how you wish to compare?
Analytical functions give you functionality that cannot otherwise be achieved easily in a lot of cases. They can introduce some performance degredation to a query but you have to compare on a query by query basis to determine if analytical functions or otherwise are the best solution for the issue. If it were as simple as saying that analytical functions are always slower than doing it without analytical functions, then Oracle wouldn't bother introducing them into the language. -
Are analytic functions usefull only for data warehouses?
Hi,
I deal with reporting queries on Oracle databases but I don't work on Data Warehouses, thus I'd like to know if learning to use analytic functions (sql for anaylis such as rollup, cube, grouping, ...) might be usefull in helping me to develop better reports or if analytic functions are usually usefull only for data warehouses queries. I mean are rollup, cube, grouping, ... usefull also on operational database or do they make sense only on DWH?
Thanks!Mark1970 wrote:
thus does it worth learning them for improving report queries also not on DHW but on common operational databases?Why pigeonhole report queries as "+operational+" or "+data warehouse+"?
Do you tell a user/manager that "<i>No, this report cannot be done as it looks like a data warehouse report and we have an operational database!</i>"?
Data processing and data reporting requirements not not care what label you assign to your database.
Simple real world example of using analytical queries on a non warehouse. We supply data to an external system via XML. They require that we limit the number of parent entities per XML file we supply. E.g. 100 customer elements (together with all their child elements) per file. Analytical SQL enables this to be done by creating "buckets" that can only contain 100 parent elements at a time. Complete process is SQL driven - no slow-by-slow row by row processing in PL/SQL using nested cursor loops and silly approaches like that.
Analytical SQL is a tool in the developer toolbox. It would be unwise to remove it from the toolbox, thinking that it is not applicable and won't be needed for the work that's to be done. -
Need analytic function suggestion
Hi,
I need advice related to analytic ( I think ) function in Oracle 9.
create table testx ( id number, arr number, fore number, actual number, result_x number, is_first number);
insert into testx values ( 1, null, null, 12, null , 0 );
insert into testx values ( 2, null, null, 14 , null, 0 );
insert into testx values ( 3, 4, 5, 16, 16, 1 );
insert into testx values ( 4, 5, 5, 18, 16, 0 );
insert into testx values ( 5, 5, 5, 20, 16, 0 );
insert into testx values ( 6, 5, 5, 22, 16, 0 );
insert into testx values ( 7, 5, 5, 24, 16, 0 );
insert into testx values ( 8, 5, 5, 25, 16, 0 );
insert into testx values ( 9, 5, 8, 25, 13, 0 );
insert into testx values ( 10, 5, 8, 21, 10, 0 );
insert into testx values ( 11, 5, 8, 19, 7, 0 );
insert into testx values ( 12, 5, 8, 18, 4, 0 );
I need ONE level query ( no subqueries ) which will calculate value stored in RESULT_X column.
Rule for calculation is:
1. when arr and fore columns are available first time then result_x = actual ( row with id = 3)
2. in other case result_x = (previous value of result_x + arr - fore )
3. order of records is stored in id column
I have problem with calculating previous value of result_x since it should be available in next row calculation and dependents on other columns values.
Thanks for help,
Regards,
PiotrHi, Piotr,
This produces the results you requested:
SELECT testx.*
, SUM ( CASE
WHEN is_first = 1
THEN result_x
ELSE arr - fore
END
) OVER (ORDER BY id) AS computed_result_x
FROM testx
ORDER BY id
;This relies on the fact that there is only one row where is_first=1, and that all the earlier rows have NULL as arr or fore.
If that's not the case in your real data, then I don't think it's possible in SQL without sub-queries. Why can't you use a sub-query?
The problem is that rows up to the one with is_first=1 have to be treated differently from rows after that point, so ithe CASE expression might need to know if a given row is before or after the one with is_first=1. If you need an analytic function to determine that, then you need a sub-query, becuase analytic functions can not be nested.
You could use MODEL or a recursive WITH clause to get the results you want, but they require sub-querries. -
BO4 - Oracle Analytic Function within agg_aware measure
Hi,
We can use an analytic function within idt tool and when in a measures by itself it parses ok.
However when include in an agg_aware measure it gives an ORA-00937 not a single group group function.
When create queries with the agg_aware measure behaves ok, generates sql ok and runs ok - please advise.
ThanksNice, am I coming in here to read the English docs again?
-
From analytical function to regular query
Hi all,
I was reading a tutorial for analytical function and i found something like this
sum(princial) keep(dense_rank first order by d_date) over partition by (userid, alias, sec_id, flow, p_date)
can somebody translate this into simple queries / subquery? i am aware that analytical function are faster but i would like to know
how this can translate to regular query.
can someone help me writing a regular query that will be produce same result as query above?
. thanks
Edited by: Devx on Jun 10, 2010 11:16 AMHi,
WITH CUSTOMERS AS
SELECT 1 CUST_ID ,'NJ' STATE_CODE,1 TIMES_PURCHASED FROM DUAL UNION ALL
SELECT 1,'CT',1 FROM DUAL UNION ALL
SELECT 2,'NY',10 FROM DUAL UNION ALL
SELECT 2,'NY',10 FROM DUAL UNION ALL
SELECT 1,'CT',10 FROM DUAL UNION ALL
SELECT 3,'NJ',2 FROM DUAL UNION ALL
SELECT 4,'NY',4 FROM DUAL
SELECT SUM(TIMES_PURCHASED) KEEP(DENSE_RANK FIRST ORDER BY CUST_ID ASC) OVER (PARTITION BY STATE_CODE) SUM_TIMES_PURCHASED_WITH_MIN,
SUM(TIMES_PURCHASED) KEEP(DENSE_RANK LAST ORDER BY CUST_ID) OVER (PARTITION BY STATE_CODE) SUM_TIMES_PURCHASED_WITH_MAX,
C.*
FROM CUSTOMERS C;
SUM_TIMES_PURCHASED_WITH_MIN SUM_TIMES_PURCHASED_WITH_MAX CUST_ID STATE_CODE TIMES_PURCHASED
11 11 1 CT 10
11 11 1 CT 1
1 2 3 NJ 2
1 2 1 NJ 1
20 4 4 NY 4
20 4 2 NY 10
20 4 2 NY 10The above given example is self explanatory, execute the SQL, you'll notice that in the first column the sum of TIMES_PURCHASED partitioned by state code of FIRST cust_id will be repeated for the STATE_CODE partition, in the second column, the sum of TIMES_PURCHASED partitioned by state code of LAST cust_id will be repeated for the STATE_CODE partition.
HTH
*009*
Edited by: 009 on Jun 10, 2010 10:53 PM
Maybe you are looking for
-
hi there, i hope there is some help out there, since i have absolutely no idea what else i could try to make my printer work via airport, which has until now worked perfectly well via direct usb connect. via airport either way (ip or bonjour) print j
-
Unzip CS5 download. How?
Unzip CS5 download. How? Its in z7 format...
-
Positioning multiple video tracks in Prem.Elements10
Hi there, I'm new to video editing and to Adobe Premiere Elements 10 so apologies if this is a silly question... When I export my files into mpeg format, the size, shape and positioning of my video tracks does not match those on the elements screen.
-
Deleted photoshop cc by mistake, I cannot find my photoshop cc in my app list. However it seems that I can perform updates for my photshop cc even if I don't have it anymore. I have a mac.
-
In a php upload file I have inserted: if($_FILES['file']['type'] !="application/pdf"){ echo "Should be PDF";} exit; Even when trying to upload a PDF file the error message comes: Should be PDF. Earlier this worked fine. Maybe a bug in an update? Rega