I have a table-valued function f_customer_return_rate (that returns a table, not a number)
Customer_id end_date period RATE
1234 '2020-12-31' 12 0.3
and i want to run it many times to get return rate for many customers. How do i loop it, or embed it in a query?
Eg i want
select customer_id,
select RATE
from table(f_customer_return_rate (
customer_id, -- customer id
To_date('2020-12-31', 'YYYY-MM-DD'), --end_date IN DATE,
12 -- period: since how many months ago
)
)
where rownum = 1
as return_rate
FROM customer_table -- this table has 1000 customers
It throws err
[Code: 936, SQL State: 42000] ORA-00936: missing expression
Is it because this function is table-valued and can't be put into a column?
Any way i can loop (ideally embed in a query) and put the results for 1000 customers together?
No, it is not happening from the function you are calling.
You are missing a couple of parenthesis.
select customer_id,
--> here
(select RATE
from table(f_customer_return_rate (
customer_id, -- customer id
To_date('2020-12-31', 'YYYY-MM-DD'), --end_date IN DATE,
12 -- period: since how many months ago
)
)
where rownum = 1) --> here
as return_rate
FROM customer_table -- this table has 1000 customers
Related
select rownum into v_rownum
from waitlist
where p_callnum=callnum
order by sysdate;
tried doing this but gives too many values.
and if I do p_snum=snum, it will keep returning 1. I need it to return 2 if it's #2 on the waitlist.
select rn into v_rownum
from (select callnum,
row_number() over (order by sysdate) rn
from waitlist)
where p_snum=snum;
Almost got it to work. Running into issues in the first select. I believe I might have to use v_count instead. Also Ordering by Sysdate even if a second apart will order it correctly.
SNU CALLNUM TIME
--- ---------- ---------
101 10125 11-DEC-18
103 10125 11-DEC-18
BTW time is = date which I entered people into waitlist using sysdate. So I suppose ordering by time could work.
create table waitlist(
snum varchar2(3),
callnum number(8),
time date,
constraint fk_waitlist_snum foreign key(snum) references students(snum),
constraint fk_waitlist_callnum foreign key(callnum) references schclasses(callnum),
primary key(snum,callnum)
);
is the waitlist table.
I used Scott's DEPT table to create your WAITLIST; department numbers represent CALLNUM column:
SQL> select * From waitlist;
CALLNUM WAITER
---------- --------------------
10 ACCOUNTING
20 RESEARCH
30 SALES
40 OPERATIONS
How to fetch data you need?
using analytic function (ROW_NUMBER) which orders values by CALLNUMs, you'll know the order
that query will be used as an inline view for the main query that returns number in the waitlist for any CALLNUM
Here's how:
SQL> select rn
2 from (select callnum,
3 row_number() over (order by callnum) rn
4 from waitlist
5 )
6 where callnum = 30;
RN
----------
3
SQL>
rownum in oracle is a generated column, it does not refer to any specific row, it is just the nth row in a set.
With a select into it can only return one row (hence the two many rows error) so rownum will always be 1.
Without more details about your table structure, and how you are uniquely identifying records it is hard to give assist you further with a solution.
The standard PIVOT syntax uses a static FOR list:
SELECT *
FROM (
SELECT log_id, event_id, event_time
FROM patient_events
WHERE event_id IN (10,20,30,40,50)
) v
PIVOT (
max(event_time) event_time
FOR event_id IN( 10,20,30,40,50 )
)
Is there a way to make this dynamic?
I know the sub-select in the WHERE clause will work, but can I use one in the FOR?
SELECT *
FROM (
SELECT log_id, event_id, event_time
FROM patient_events
WHERE event_id IN ( sub-select to generate list of IDs )
) v
PIVOT (
max(event_time) event_time
FOR event_id IN( sub-select to generate list of IDs )
)
You can't in pure SQL, but I don't think quite because of the reason suggested - it's not that the IN clause needs to be ordered, it's that it has to be constant.
When given a query, the database needs to know the shape of the result set and the shape needs to be consistent across queries (assuming no other DDL operations have taken place that might affect it). For a PIVOT query, the shape of the result is defined by the IN clause - each entry becomes a column, with a data type corresponding to the aggregation clause.
Hypothetically if you were to allow a sub-select for the IN clause then you could alter the shape of the result set just by performing DML operations. Imagine your sub-select worked and got you a list of all event_ids known to the system - by inserting a new record into whatever drives that sub-select, your query returns a different number of columns even though no DDL has occurred.
Now we're stuck - any view built on that query is invalid because its shape wouldn't match that of the query, but Oracle couldn't know that it's invalid because none of the objects it depends on have been changed by DDL.
Depending on where you're consuming the result, dynamic SQL's your only option - either at the application level (build the IN list yourself) or via a ref cursor in a database function or procedure.
Interesting question.
On the face of it, it shouldn't work, since the list of values (which will become column names) must be ordered. This is not the case for an "IN" list in the WHERE clause. But perhaps it would work with an ORDER BY condition in the sub-SELECT?
Unfortunately, no. This is easy to test. Got the same error message with or without ORDER BY. (And the query works fine if the IN list is just 10, 20, 30, 40 - the actual department numbers from the DEPT table.) Using tables from the standard SCOTT schema.
SQL> select deptno from scott.dept;
DEPTNO
----------
10
20
30
40
4 rows selected.
SQL> select * from (
2 select sal, deptno
3 from scott.emp
4 )
5 pivot (sum(sal) as total_sal
6 for deptno in (10, 20, 30, 40))
7 ;
10_TOTAL_SAL 20_TOTAL_SAL 30_TOTAL_SAL 40_TOTAL_SAL
------------ ------------ ------------ ------------
8750 10875 9400
1 row selected.
SQL> select * from (
2 select sal, deptno
3 from scott.emp
4 )
5 pivot (sum(sal) as total_sal
6 for deptno in (select deptno from scott.dept order by deptno))
7 ;
for deptno in (select deptno from scott.dept order by deptno))
*
ERROR at line 6:
ORA-00936: missing expression
I'm working with PL/SQL Developer.
I'm trying to update values in a column(existing table).
The values used to populate rows should be auto incremented. The starting value is the maximum value that already exist in such field.
An example, I have the following table
ORDER_ID T_NAME T_PRICE
20 CAR 50
NULL VAN 100
NULL BIKE 10
NULL BOAT 300
After running the query I would expect the table to look like:
ORDER_ID T_NAME T_PRICE
20 CAR 50
21 VAN 100
22 BIKE 10
23 BOAT 300
The query I created so far is:
DECLARE
temp_order_id number;
BEGIN
:temp_order_id = SELECT ISNULL(MAX((ORDER_ID)),0) + 1 FROM SALES_ACC;
update SALES_ACC
set (ORDER_ID) = :temp_order_id , :temp_order_id = :temp_order_id + 1
where (ORDER_ID) is null;
END;
Oracle doesn't like assigning a value from select statement to the temp_order_id variable.
Does anyone has any idea how to fix it?
You don't need pl/sql for this - you can do it in a single update statement - eg:
create table test1 as
select 20 order_id, 'CAR' t_name, 50 t_price from dual union all
select null order_id, 'VAN' t_name, 100 t_price from dual union all
select null order_id, 'BIKE' t_name, 10 t_price from dual union all
select null order_id, 'BOAT' t_name, 300 t_price from dual;
update test1
set order_id = (select max(order_id) from test1) + rownum
where order_id is null;
commit;
select * from test1
order by 1;
ORDER_ID T_NAME T_PRICE
---------- ------ ----------
20 CAR 50
21 VAN 100
22 BIKE 10
23 BOAT 300
drop table test1;
As a side note, it sounds like order_id is something that should really be the primary key of the table - if you had that, then you wouldn't be allowed to add a row without a value. Plus, you would also need a sequence that you would then use when inserting data into the table - e.g.:
insert into test1 (order_id, t_name, t_price)
values (test1_seq.nextval, 'TRIKE', 30);
ORACLE's recomended way for this is either:
Create a sequence and a trigger on the table to assign order_id as soon as row is being inserted
or, for Oracle 12c, you can have an IDENTITY column
See How to create id with AUTO_INCREMENT on Oracle?, both approaches are described there.
Within the DECLARE ... BEGIN ... END; section you are in PL/SQL syntax. That is not equal to the SQL syntax. Within PL/SQL syntax you should make use of the so called select into statement.
SELECT ISNULL(MAX((ORDER_ID)),0) + 1
into :temp_order_id
FROM SALES_ACC
I need to fetch 200 rows a month per year randomly sampled for each month from this table in Oracle 10g:
Documents
--------------------------------------
Doc_ID Date
95687 25-AUG-12
99283 21-SEP-12
87654 10-AUG-12
97111 14-SEP-12
I've seen the SAMPLE() function but not sure how to do the other parts of the criteria.
Perhaps something like this:
select doc_id
, date
from ( select doc_id
, date
, row_number() over (partition by trunc(date, 'MM')
order by dbms_randon.value ) as rn
from your_table )
where rn <= 200
order by date, doc_id ;
The analytic ROW_NUMBER() function produces a number for each record grouped by month (achieved by truncating the date with the 'MM' mask). Ordering by DBMS_RANDOM will randomise the sort order. Remember to initialise DBMS_RANDOM before running the query to get a truly random sort order.
Find out more.
While researching Oracle Analytics, I came across this query:
select an_id,
a_date,
min(a_date) over (
partition by an_id, trunc(a_date)
order by a_date
range between (2.5/24) preceding and (2.5/24) following
) mn,
max(a_date) over (
partition by an_id, trunc(a_date)
order by a_date
range between (2.5/24) preceding and (2.5/24) following
) mx
from a_table
I believe this finds the min and max dates for a given an_id and a_date within a 2.5 hour period.
My question is why does this comparison between a_date (a date) and 2.5/24 (a number) work and how can it be modified for ranges of days, months, or years?
The date type allows arithmetic where a unit of 1 is a day, so SYSDATE + 1 is tomorrow.
For example, try select to_char(sysdate + 1/24, 'DD-MON-YY HH:MM:SS') from dual -> 1 hour from now.
Here are the docs that talk about that specific analytic construct to give some foundational info and add to what jwilson mentioned.
http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions001.htm#i97640
It's probably clearer to use intervals to specify window ranges:
http://download.oracle.com/docs/cd/B19306%5F01/server.102/b14200/functions104.htm#i89943
SELECT last_name,
hire_date,
salary,
SUM(salary) OVER (ORDER BY hire_date
RANGE NUMTOYMINTERVAL(1,'year') PRECEDING) AS t_sal
FROM employees;