Hello, you are not logged in.  Login or sign up
Toad on Twitter Follow Toad Search Toad World Search
Blogger List   

All Recent Blog Entries
 

Johannes Ahrends
Unicode and Toad

Ben Boise
Toad SC Discussions

Kevin Dalton
Benchmark Factory

Steven Feuerstein
PL/SQL Obsession

Devin Gallagher
Toad SC discussions

Stuart Hodgins
JProbe Discussions

  Henrik "Mauritz" Johnson
Toad Tips & Tricks on the "other" Toads
  Mark Kurtz
Toad SC discussions
  Michael Lumbard
Toad SC discussions
Daniel Norwood
Toad for Data Analysts,
Toad Extension for Visual Studio
Debbie Peabody
Toad for Data Analysts
Gary Piper
Toad Reports Manager
John Pocknell
Toad for Oracle, JProbe
Kuljit Sangha
Toad SC discussions
Bert Scalzo Indicates Oracle ACE status
Toad for Oracle, Data Modeling, Benchmarking
Jeff Smith
Toad product family
Richard To
SQL Optimization
Jim Wankowski
DB2 - LUW and z/OS
John Weathington
  Toad World Editor
Toad World issues

  Toad Data Modeler Opens in a new window
Data Modeling
 
  Real Automated Code Testing for Oracle
Quest Code Tester blog

Blogs
Toad and Database Commentaries

Toad World blogs are a mix of insightful how-tos from Quest experts as well as their commentary on experiences with new database technologies.  Have some views of your own to share?  Post your comments!  Note:  Comments are restricted to registered Toad World users.

Do you have a topic that you'd like discussed?  We'd love to hear from you.  Send us your idea for a blog topic.

Avoiding repetitive coding for repetitive column structures
 
Location: Blogs Steven Feuerstein's Blog    
 StevenFeuersteinTW Wednesday, July 15, 2009 6:10 AM
I recently received this email:
 
Hi Steven,
 
I was reading your article on 2 dimensional arrays. I was wondering if I could apply this technique to solve the following problem.
 
I have a table with 48 columns that are named as follows: no_1, no_2.......no_48.
 
I need to do a calculation on all columns no_1 then no_2 ... no_48 and l would like to do it without having to repeat the code 48 times. Could I use a two dimensional array to hold this data and then run my calculations?
Here is the answer I sent to him:
 
I don't think that a two-dimensional array is going to help in this case, though I do encourage you to keep the technique in mind for the future. Why won't it help? Well, you need to come up with a way to extract the value of a specific, numbered column from within a query. You can fetch a whole row easily enough, but you will not be able to use dynamic SQL (EXECUTE IMMEDIATE or DBMS_SQL) to work with that record. Records are PL/SQL structures and cannot be bound into a dynamic PL/SQL block.
 
Instead, however, you can use EXECUTE IMMEDIATE to obtain a specific column value, and then execute your function on that value. The following statements create a table and function, and then a driver PL/SQL block to demonstrate the technique.
 
DROP TABLE five_columns
/
CREATE TABLE five_columns
(
   id     NUMBER
 , val1   NUMBER
 , val2   NUMBER
 , val3   NUMBER
 , val4   NUMBER
 , val5   NUMBER
)
/
BEGIN
   INSERT INTO
five_columns
       VALUES (1, 1, 2, 3, 4, 5
              ); 
   INSERT INTO five_columns
       VALUES (2, 10, 20, 30, 40, 50
              ); 
   COMMIT;
END;
/
CREATE OR REPLACE FUNCTION double_value (val_in IN NUMBER)
   RETURN
NUMBER
IS
BEGIN
   RETURN
val_in * 2;
END;
/
 
DECLARE
   TYPE
results_tt IS TABLE OF NUMBER
                         INDEX BY PLS_INTEGER;
   l_results   results_tt;
BEGIN
   FOR
col_num IN 1 .. 5
   LOOP
      EXECUTE IMMEDIATE
  
        'DECLARE l_curval NUMBER;
         BEGIN SELECT val'
|| col_num
           || ' INTO l_curval FROM five_columns WHERE id = :id;'
            || ' :newval := double_value (l_curval);
         END;'
         USING IN 1 /* The ID value */
                   , OUT l_results (col_num);
   END LOOP; 
   FOR indx IN 1 .. l_results.COUNT
   LOOP
      DBMS_OUTPUT.put_line (l_results (indx));
   END LOOP;
END;
/
Now, one problem I can see with this approach is that you execute N queries for each row of data, where N is the number of columns in the table.
 
Perhaps there is also something you can do with a pivot of the SELECT statement (for an entire row), but pivoting is outside my area of expertise. So I will stop here. Hope this helps!
Permalink |  Trackback

Comments (3)  
By StevenFeuersteinTW on Friday, July 17, 2009 2:12 PM
Tom Kyte took on my implicit challenge ("I don't think that a two-dimensional array is going to help in this case.") and came up with a solution that uses precisely this structure. He asked me to post it on the blog for him. Here we go:

Steven - two dimensional arrays - wouldn't this work?

ops$tkyte%ORA10GR2> create type numArray as table of number
2 /

Type created.

ops$tkyte%ORA10GR2> create type num2dim as table of numArray
2 /

Type created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> select * from five_columns;

ID VAL1 VAL2 VAL3 VAL4 VAL5
---------- ---------- ---------- ---------- ---------- ----------
1 1 2 3 4 5
2 10 20 30 40 50

ops$tkyte%ORA10GR2> declare
2 l_id numArray;
3 l_data num2dim;
4 begin
5 select id, numArray(val1,val2,val3,val4,val5)
6 bulk collect into l_id, l_data
7 from five_columns;
8
9 for i in 1 .. l_id.count
10 loop
11 dbms_output.put_line( 'l_id('||i||') = ' || l_id(i) );
12 for j in 1..5
13 loop
14 dbms_output.put_line( '- l_data(' || i || ')(' || j ||
') = ' || l_data(i)(j) );
15 end loop;
16 end loop;
17 end;
18 /
l_id(1) = 1
- l_data(1)(1) = 1
- l_data(1)(2) = 2
- l_data(1)(3) = 3
- l_data(1)(4) = 4
- l_data(1)(5) = 5
l_id(2) = 2
- l_data(2)(1) = 10
- l_data(2)(2) = 20
- l_data(2)(3) = 30
- l_data(2)(4) = 40
- l_data(2)(5) = 50

PL/SQL procedure successfully completed.

--
Thanks
--
Thomas Kyte thomas.kyte@oracle.com
Senior Technologist, Oracle Server Technologies

http://asktom.oracle.com/

By StevenFeuersteinTW on Friday, July 17, 2009 2:24 PM
And here's what I wrote back to Tom: I like it!

So why didn't I think of that and what do I think of this solution?

Why didn't I think of this approach? I believe that my blind spot on this solution reflects too heavy a bias towards a PL/SQL solution and not enough thought given to what I can do in SQL. I need to keep this (existence of a bias) in mind more in the future.

What do I think of this solution? Very elegant. And I expect it is more efficient than mine, though I have not run any performance tests. One concern with this approach is that if you have a whole lot of columns, it is quite awkward to write this solution, particularly this line:

numArray(val1,val2,val3,val4,val5)

I'd probably consider generating this line from USER_TAB_COLUMNS.

And if you have varying number of columns in the table (that is, you need to apply the solution to different tables with similar structures), you will need to switch to dynamic SQL.

Thanks, Tom!

By hillbillyToad on Monday, July 20, 2009 6:37 AM
Hey Tom, the water's warm over here, feel free to dip your toes in and get yourself a TW account :)

Search Blog Entries
 
Copyright 2010 by Quest Software  | Terms Of Use | Privacy Statement | Contact Us