I'm trying to increase the age of every customer by 1 and display them; using a cursor.
This is the Table structure.
Select * from customers;
+----+----------+-----+-----------+----------+
| ID | NAME | AGE | ADDRESS | SALARY |
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
| 2 | Khilan | 25 | Delhi | 1500.00 |
| 3 | kaushik | 23 | Kota | 2000.00 |
| 4 | Chaitali | 25 | Mumbai | 6500.00 |
| 5 | Hardik | 27 | Bhopal | 8500.00 |
| 6 | Komal | 22 | MP | 4500.00 |
Here is my code:
DECLARE
c_id customers.id%TYPE;
c_name customers.name%TYPE;
c_age customers.age%TYPE;
CURSOR c1 IS
SELECT id, name, age
FROM customers
FOR UPDATE OF salary;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO c_id, c_name, c_age;
UPDATE customers
SET age = age + 1
WHERE CURRENT OF c1;
EXIT WHEN c1%NOTFOUND;
dbms_output.put_line( c_id || ' ' || c_name || ' ' || c_age );
END LOOP;
CLOSE c1;
END;
/
However, I am getting the following error:
Error report:
ORA-01410: invalid ROWID
ORA-06512: at line 13
01410. 00000 - "invalid ROWID"
*Cause:
*Action:
1 Ramesh 32
2 Khilan 25
3 kaushik 23
4 Chaitali 25
5 Hardik 27
6 Komal 22
Why is this happening and how can I stop it?
You have your exit in the wrong place; it should be straight after the fetch. You are processing your six real rows properly, but then you have a seventh fetch - after which %notfound will be true - so there is no 'current' row to update. You need to exit before you try to do anything with that invalid row.
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO c_id, c_name, c_age;
EXIT WHEN c1%NOTFOUND;
UPDATE customers
SET age = age + 1
WHERE CURRENT OF c1;
dbms_output.put_line( c_id || ' ' || c_name || ' ' || c_age );
END LOOP;
CLOSE c1;
END;
Hopefully this is just an exercise, as it's not a very efficient way to do the updates.
Related
I have two table :
on table is tbl_ledger with columns
| balance | eff_date |
| -------- | -------- |
| 200$ | 2FEB 2008 |
| 500$ | 2FEB 2008 |
| 250$ | 3FEB 2008 |
| 150$ | 5feb 2008 |
ANOTHER TABLE NAME IS : tbl_ledger_input
with columns balance and eff_date
| balance | eff_date |
| -------- | -------- |
|700$ | 2FEB 2008 |
| 250$ | 3FEB 2008 |
| 150$ |5FEB 2008 |
| ........ | ....... |
**I want insert into table tbl_ledger_input for each day from tbl_ledger sum (balance) for each date with LOOP FOR
**
PLEASE HELP ME .
I WANT INSERT INTO TABLE TBL_LEDGER_INPUT using LOOP FOR each EFF_DATE .
As you've already seen in comments, loop isn't the best choice for that. Row-by-row processing is slow; you won't notice any difference if you're dealing with small data sets (as in this example), though.
If you want to do it in PL/SQL (using a loop), a simple option is a cursor FOR loop:
SQL> begin
2 for cur_r in (select eff_date, sum(balance) balance
3 from tbl_ledger
4 group by eff_date
5 )
6 loop
7 insert into tbl_ledger_input (balance, eff_date)
8 values (cur_r.balance, cur_r.eff_date);
9 end loop;
10 end;
11 /
PL/SQL procedure successfully completed.
SQL> select * From tbl_ledger_input;
BALANCE EFF_DATE
---------- -----------
700 02-FEB-2008
250 03-FEB-2008
What you should really be doing is
SQL> truncate table tbl_ledger_input;
Table truncated.
SQL> insert into tbl_ledger_input (balance, eff_date)
2 select sum(balance), eff_date
3 from tbl_ledger
4 group by eff_date;
2 rows created.
SQL> select * From tbl_ledger_input;
BALANCE EFF_DATE
---------- -----------
700 02-FEB-2008
250 03-FEB-2008
SQL>
Actually, I want to create an oracle procedure to normalize the table and for that we required a multiple queries/task to perform /execute. Below are the steps for which will help to write an oracle procedure:
step 1) Suppose a table name temp and there are nearly millions of records in it. So get the distinct records from whole table and
insert into same table by updating value for ver_id column as -1. Below is the example and table structure to understand
| pid | address | key |ver_id
| 1 | 242 Street | 123 | 1
| 2 | 242 Street | 123 |2
| 3 | 242 Street | 123 |3
| 4 | 242 Long St | 456 |4
Expected Resultis below :
select 2 distinct records for duplicate records from above table and insert that distinct record at the end of the table by updating ver_id as -1 like below:
| pid | address | key |ver_id
| 1 | 242 Street | 123 | 1
| 2 | 242 Street | 123 |2
| 3 | 242 Street | 123 |3
| 4 | 242 Long St | 456 |4
| 5 | 242 Street | 123 |-1
step 2) Find all parents records of given key in ADDRESS_TEMP table and update them with
pid (primary key of temp table) which is newly created in step 1 i.e pid as 5 and 6
ADDRESS_TEMP table (parent of temp table)
pid is the primary key of temp table and foreign key of ADDRESS_TEMP table
|addr_id | ver_id | pid
| 11 | 1 | 1
| 12 | 2 | 2
| 13 | 3 | 3
| 14 | 4 | 4
| 15 | 5 | 5
| 16 | 6 | 6
After Update
|addr_id | ver_id | pid
| 11 | 1 | 1
| 12 | 2 | 2
| 13 | 3 | 3
| 14 | 4 | 4
| 15 | 5 | 5
| 15 | 6 | 6
| 15 | 7 | 7
step 3): Delete all temp table records where key is 123 and 456 (in short delete all duplicate records) whose ver_id is not equal to -1
so the expected result of temp table is like below:
| pid | address | key |ver_id
| 4 | 242 Long St | 456 |4
| 5 | 242 Street | 123 |-1
For the first step ,exeecuted this query as below and its working as expected buti want to perfrom all the above steps in one single procedure .
insert into temp (id, address, key, ver_id)
with data as
(select t.*,
row_number() over (partition by address, key order by id) rn
from temp t
),
data2 as
(select distinct d.address, d.key
from data d where d.rn > 1
)
select seq_temp.nextval, address, key, -1
From data2.`
But above one is just for the step first but we need to create a procedure for all the above 3 steps and also i have tried to add the above insert statement into the procedure and it gets executed but for step2 and 3 it needs to be created
CREATE OR replace PROCEDURE p1 AS
cursor c_temp IS
SELECT * FROM temp ;
r_temp c_temp%ROWTYPE;
BEGIN
OPEN c_temp;
LOOP
FETCH c_temp INTO r_temp;
EXIT WHEN c_temp%NOTFOUND;
insert into temp (id, address, key, ver_id)
with data as
(select t.*,
row_number() over (partition by address, key order by id) rn
from temp t
),
data2 as
(select distinct d.address, d.key
from data d where d.rn > 1
)
select seq_temp.nextval, address, key, -1
From data2;
END LOOP;
CLOSE c_temp;
END;
Your instructions are quite confusing and I'm not sure that I got it completely. Anyway, below is the answer (with comments in the code). Even If it isn't exactly what you wanted - the logic and the structure of the code should be ok. You will have to adjust it to your context anyyway.
Table temp data before and after:
/* TEMP before
P_ID ADDRESS A_KEY VER_ID
1 242 Street 123 1
2 242 Street 123 2
3 242 Street 123 3
4 242 Long St 456 4
TEMP after
P_ID ADDRESS A_KEY VER_ID
4 242 Long St 456 4
5 242 Street 123 -1 */
Table TEMP_ADDRESS before and after:
/* TEMP_ADDRESS before
ADDR_ID VER_ID P_ID
11 1 1
12 2 2
13 3 3
14 4 4
TEMP_ADDRESS after
ADDR_ID VER_ID P_ID
11 1 5
12 2 5
13 3 5
14 4 4 */
Here is the code with comments...
SET SERVEROUTPUT ON
DECLARE
-- Declare cursor to fetch you distinct records from table TEMP
CURSOR c_temp IS SELECT Count(*) "CNT", ADDRESS, A_KEY FROM temp WHERE VER_ID > 0 GROUP BY ADDRESS, A_KEY HAVING Count(*) > 1 ORDER BY A_KEY;
-- ------------- Cursor records below ----------------
-- CNT ADDRESS A_KEY
-- ------ ------------ ----------
-- 3 242 Street 123
-- ----------------------------------------------------
cSet c_temp%ROWTYPE;
sq VarChar2(1) := ''''; -- single quote character (4 single quotes) -- using it to construct Sql commands
mSql VarChar2(512) := '';
mID TEMP.P_ID%TYPE;
mAddr TEMP.ADDRESS%TYPE;
mKey TEMP.A_KEY%TYPE;
BEGIN
Select Nvl(Max(P_ID), 0) Into mID From TEMP; -- Last (max) P_ID from taable TEMP --> 4 (empty table would return 0)
--
OPEN c_temp;
LOOP
FETCH c_temp INTO cSet;
EXIT WHEN c_temp%NOTFOUND;
mID := mID + 1; -- add 1 to mID for insert into TEMP
-- constructing INSERT commands for every cursor record
mSql := 'INSERT INTO TEMP(P_ID, ADDRESS, A_KEY, VER_ID) VALUES(' || mID || ', ' || sq || cSet.ADDRESS || sq || ', ' || cSet.A_KEY || ', -1)';
--
-- --------------- constructed command(s) for cursor record(s) ---------------------------------
-- INSERT INTO TEMP(P_ID, ADDRESS, A_KEY, VER_ID) VALUES(5, '242 Street', 123, -1)
--
Execute Immediate mSql; -- execute created INSERT command(s)
--
-- While you still have m_ID and cursor record's ADDRESS and A_KEY use them to update parent table TEMP_ADDRESS
Begin
UPDATE TEMP_ADDRESS SET P_ID = mID WHERE P_ID IN(Select P_ID FROM TEMP WHERE ADDRESS = cSet.ADDRESS And A_KEY = cSet.A_KEY And VER_ID > 0);
-- after that you can delete records from table temp that are not needed any more
DELETE FROM TEMP WHERE P_ID IN(Select P_ID FROM TEMP WHERE ADDRESS = cSet.ADDRESS And A_KEY = cSet.A_KEY And VER_ID > 0);
Commit;
Exception
WHEN OTHERS THEN
Rollback; -- if anything went wrong - Rollback and send a message
DBMS_OUTPUT.PUT_LINE('ERR - UPDATE or DELETE ' || Chr(10) || SQLERRM);
End;
--
END LOOP;
CLOSE c_temp;
Exception
WHEN OTHERS THEN
Rollback; -- if anything went wrong - Rollback and send a message
DBMS_OUTPUT.PUT_LINE('ERR - INSERT ' || Chr(10) || SQLERRM);
END;
Regards...
I have a cursor for loop include credit and debit amount of my daily transaction account, and I want to show them respectively.
DECLARE
v_debit := 0;
v_credit := 0;
CURSOR c_acct_rec is
select bk.acctname, bk.bsb, bk.accountnr, bk.value, bk.code
from mybank_tbl bk
r_acct_rec c_acct_rec%ROWTYPE;
FOR r_acct_rec in c_acct_rec LOOP
if r_acct_rec.code = 'dr' then
v_debit := show_debit;
else
v_credit := v_credit + r_acct_rec;
end if;
DBMS_OUTPUT.PUT_LINE(v_credit || v_debit);
END LOOP;
END;
This part of the SQL query from the above is showing all the debit and credit values.
+-----------------+
| value | code | |
+-----------------+
| | 12 | dr | |
| | 5 | dr | |
| | 7 | dr | |
| | 33 | dr | |
| | 16 | dr | |
| | 1000 | cr | |
+-----------------+
The problem I found because they are in the cursor loop, it will populate all value in one column like below
+-------+--------+
| Debit | Credit |
+-------+--------+
| 12 | |
| 5 | |
| 7 | |
| 33 | |
| 16 | |
| 1000 | |
+-------+--------+
The if/else statement can't control it to show all row records.
+-------+--------+
| Debit | Credit |
+-------+--------+
| 12 | 0 |
| 5 | 0 |
| 7 | 0 |
| 33 | 0 |
| 16 | 0 |
| 1000 | 0 |
| 1000 | 1000 |
+-------+--------+
Is that a way to separate it using a stored procedure or separate the row from the account value (r_acct_rec.value) to check to get this expected outcome?
+-------+--------+
| Debit | Credit |
+-------+--------+
| 12 | |
| 5 | |
| 7 | |
| 33 | |
| 16 | |
| | 1000 |
+-------+--------+
You have two variables. You need to populate one and blank the other, depending on the value of code. Use pad functions in the display to align the output nicely.
DECLARE
v_debit number := 0;
v_credit number := 0;
CURSOR c_acct_rec is
select bk.acctname, bk.bsb, bk.accountnr, bk.value, bk.code
from mybank_tbl bk
r_acct_rec c_acct_rec%ROWTYPE;
BEGIN
DBMS_OUTPUT.PUT_LINE(rpad('credit',10)|| ' | ' || rpad('debit',10));
FOR r_acct_rec in c_acct_rec LOOP
if r_acct_rec.code = 'dr' then
v_credit := null;
v_debit := r_acct_rec.value;
else
v_credit := r_acct_rec.value;
v_debit := null;
end if;
DBMS_OUTPUT.PUT_LINE(lpad(v_credit,10)|| ' | ' || lpad(v_debit,10));
END LOOP;
END;
Another option is to make the transformation from code-value into separate values in the query and eliminate the code for determining them.
declare
cursor c_acct_rec is
select bk.acctname
, bk.bsb
, bk.accountnr
, case when bk.code = 'dr' then bk.value else null end as debit
, case when bk.code = 'cr' then bk.value else null end as credit
from mybank_tbl bk;
r_acct_rec c_acct_rec%rowtype;
begin
dbms_output.put_line(rpad('credit',10)|| ' | ' || rpad('debit',10));
for r_acct_rec in c_acct_rec loop
dbms_output.put_line(lpad(r_acct_rec.credit,10)|| ' | ' || lpad(r_acct_rec.debit,10));
end loop;
end;
Your requirement can be met in SQL only, it does not require PL/SQL.
select
bk.acctname,
bk.bsb,
bk.accountnr,
bk.value,
bk.code,
case
when bk.code = 'dr' then
bk.value
end as debit,
case
when bk.code = 'cr' then
bk.value
end as credit
from mybank_tbl bk
Say we have 3 records in table: orig_tab
---------------------------------------------
| PK | Name | Address | Postal Code |
---------------------------------------------
| 1 | AA | Street1 | 11111 |
| 2 | BB | Street2 | 22222 |
| 3 | CC | Street3 | 33333 |
---------------------------------------------
Now the data is changed:
---------------------------------------------
| PK | Name | Address | Postal Code |
---------------------------------------------
| 1 | AA | Street1 | 11111 |
| 2 | BB | Street2 | 44444 |
| 3 | CC | Dtreet7 | 33333 |
---------------------------------------------
What client wants is the update records and only the updated columns (yes, I know it doesn't make any sense but they use some old system from 1970s and they want to do some logging etc.). So the reporting table should be like this:
---------------------------------------------
| PK | Name | Address | Postal Code |
---------------------------------------------
| 2 | | | 44444 |
| 3 | | Dtreet7 | |
---------------------------------------------
This what I tried:
CREATE OR REPLACE TRIGGER vr_reporting_trigger
AFTER UPDATE ON orig_tab
FOR EACH ROW
BEGIN
IF inserting THEN
INSERT INTO rep_tab(pk, name, address, code)
SELECT :new.pk, :new.name, :new.address, :new,code FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM rep_tab WHERE pk = :new.pk);
UPDATE rep_tab t SET t.name = :new.name, t.address = :new.address, t.code = :new.code
WHERE t.pk = :new.pk;
ELSIF updating THEN
IF :new.pk <> :old.pk THEN
UPDATE rep_tab t
SET t.name = :new.name, t.address = :new.address, t.code =: new.code
WHERE t.pk = :old.pk ;
END IF;
MERGE INTO rep_tab d
USING DUAL ON (d.pk = :old.pk)
WHEN MATCHED THEN
UPDATE SET d.name = :new.name, d.address = :new.address, d.code =: new.code
WHEN NOT MATCHED THEN
INSERT (d.pk,d.name, d.address, d.code) VALUES (:new.pk,:new.name, new.address, new.code);
END IF;
END;
with this solution, I get:
---------------------------------------------
| PK | Name | Address | Postal Code |
---------------------------------------------
| 2 | BB | Street2 | 44444 |
| 3 | CC | Dtreet7 | 33333 |
---------------------------------------------
I know that it somewhere in insert claus in when updating statement but I can't figure out how to have this claus changed as per my requirement. Any suggestion?
Thanks in advance.
You need this:
In an UPDATE trigger, a column name can be specified with an UPDATING
conditional predicate to determine if the named column is being
updated. For example, assume a trigger is defined as the following:
CREATE OR REPLACE TRIGGER ...
... UPDATE OF Sal, Comm ON Emp_tab ...
BEGIN
... IF UPDATING ('SAL') THEN ... END IF;
END;
From Oracle documentation(9i)
11gR2 documentation
Trigger for only changed values.
As an example (only NOT NULL support) (comparison operators with NULL always return FALSE):
CREATE OR REPLACE TRIGGER trigger_department_update
BEFORE UPDATE OF
department_id,
cluster_id,
division_id,
macroregion_id,
address,
email
ON cl_department
FOR EACH ROW
BEGIN
IF (
:new.department_id != :old.department_id
OR :new.cluster_id != :old.cluster_id
OR :new.division_id != :old.division_id
OR :new.macroregion_id != :old.macroregion_id
OR :new.address != :old.address
OR :new.email != :old.email
)
THEN :new.update_date := CURRENT_TIMESTAMP;
END IF;
END;
With NULL support (Thanks to #Oleg in the comments below this post) (Thanks to #Chris Bandy for the answer):
CREATE OR REPLACE TRIGGER trigger_department_update
BEFORE UPDATE OF
department_id,
cluster_id,
division_id,
macroregion_id,
address,
email
ON cl_department
FOR EACH ROW
BEGIN
IF (
((:new.department_id <> :old.department_id OR :new.department_id IS NULL OR :old.department_id IS NULL) AND NOT (:new.department_id IS NULL AND :old.department_id IS NULL))
OR ((:new.cluster_id <> :old.cluster_id OR :new.cluster_id IS NULL OR :old.cluster_id IS NULL) AND NOT (:new.cluster_id IS NULL AND :old.cluster_id IS NULL))
OR ((:new.division_id <> :old.division_id OR :new.division_id IS NULL OR :old.division_id IS NULL) AND NOT (:new.division_id IS NULL AND :old.division_id IS NULL))
OR ((:new.macroregion_id <> :old.macroregion_id OR :new.macroregion_id IS NULL OR :old.macroregion_id IS NULL) AND NOT (:new.macroregion_id IS NULL AND :old.macroregion_id IS NULL))
OR ((:new.address <> :old.address OR :new.address IS NULL OR :old.address IS NULL) AND NOT (:new.address IS NULL AND :old.address IS NULL))
OR ((:new.email <> :old.email OR :new.email IS NULL OR :old.email IS NULL) AND NOT (:new.email IS NULL AND :old.email IS NULL))
)
THEN :new.update_date := CURRENT_TIMESTAMP;
END IF;
END;
to treat null :
... nvl(to_char(:new.department_id,' ') <> nvl(to_char(:old.department_id,' ') OR ...
We're using a PL/SQL table (named pTable) to collect a number of ids to be updated.
However, the statement
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
FROM TABLE (pTable));
takes a long time to execute.
It seems that the optimizer comes up with a very bad execution plan, instead of using the index that is defined on id (as the primary key) it decides to use a full table scan on the aTable. pTable usually contains very few values (in most cases just one).
What can we do to make this faster? The best we've come up with is to handle low pTable.Count (1 and 2) as special cases, but that is certainly not very elegant.
Thanks for all the great suggestions. I wrote about this issue in my blog at http://smartercoding.blogspot.com/2010/01/performance-issues-using-plsql-tables.html.
You can try the cardinality hint. This is good if you know (roughly) the number of rows in the collection.
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT /*+ cardinality( pt 10 ) */
COLUMN_VALUE
FROM TABLE (pTable) pt );
Here's another approach. Create a temporary table:
create global temporary table pTempTable ( id int primary key )
on commit delete rows;
To perform the update, populate pTempTable with the contents of pTable and execute:
update
(
select aColumn
from aTable aa join pTempTable pp on aa.id = pp.id
)
set aColumn = 1;
The should perform reasonably well without resorting to optimizer hints.
The bad execution plan is probably unavoidable (unfortunately). There is no statistics information for the PL/SQL table, so the optimizer has no way of knowing that there are few rows in it. Is it possible to use hints in an UPDATE? If so, you might force use of the index that way.
It helped to tell the optimizer to use the "correct" index instead of going on a wild full-table scan:
UPDATE /*+ INDEX(aTable PK_aTable) */aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
FROM TABLE (CAST (pdarllist AS list_of_keys)));
I couldn't apply this solution to more complicated scenarios, but found other workarounds for those.
You could try adding a ROWNUM < ... clause.
In this test a ROWNUM < 30 changes the plan to use an index.
Of course that depends on your set of values having a reasonable maximum size.
create table atable (acolumn number, id number);
insert into atable select rownum, rownum from dual connect by level < 150000;
alter table atable add constraint atab_pk primary key (id);
exec dbms_stats.gather_table_stats(ownname => user, tabname => 'ATABLE');
create type type_coll is table of number(4);
/
declare
v_coll type_coll;
begin
v_coll := type_coll(1,2,3,4);
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
FROM TABLE (v_coll));
end;
/
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------
UPDATE ATABLE SET ACOLUMN = 1 WHERE ID IN (SELECT COLUMN_VALUE FROM TABLE (:B1 ))
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | | | 142 (100)| |
| 1 | UPDATE | ATABLE | | | | |
|* 2 | HASH JOIN RIGHT SEMI | | 1 | 11 | 142 (8)| 00:00:02 |
| 3 | COLLECTION ITERATOR PICKLER FETCH| | | | | |
| 4 | TABLE ACCESS FULL | ATABLE | 150K| 1325K| 108 (6)| 00:00:02 |
----------------------------------------------------------------------------------------------
declare
v_coll type_coll;
begin
v_coll := type_coll(1,2,3,4);
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT COLUMN_VALUE
FROM TABLE (v_coll)
where rownum < 30);
end;
/
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------
UPDATE ATABLE SET ACOLUMN = 1 WHERE ID IN (SELECT COLUMN_VALUE FROM TABLE (:B1 ) WHERE
ROWNUM < 30)
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | | | 31 (100)| |
| 1 | UPDATE | ATABLE | | | | |
| 2 | NESTED LOOPS | | 1 | 22 | 31 (4)| 00:00:01 |
| 3 | VIEW | VW_NSO_1 | 29 | 377 | 29 (0)| 00:00:01 |
| 4 | SORT UNIQUE | | 1 | 58 | | |
|* 5 | COUNT STOPKEY | | | | | |
| 6 | COLLECTION ITERATOR PICKLER FETCH| | | | | |
|* 7 | INDEX UNIQUE SCAN | ATAB_PK | 1 | 9 | 0 (0)| |
---------------------------------------------------------------------------------------------------
I wonder if the MATERIALIZE hint in the subselect from the PL/SQL table would force a temp table instantiation and help the optimizer?
UPDATE aTable
SET aColumn = 1
WHERE id IN (SELECT /*+ MATERIALIZE */ COLUMN_VALUE
FROM TABLE (pTable));