Postgres 9.2 select star into an array - debugging

I want to do something like this inside a stored procedure so I can see the result of an insert statement for debugging:
thing := array_to_string(ARRAY(select * from some_table limit 1 ));
raise info 'insert result: %',thing;
Where all the columns of some_table get concatenated into an array
Is there a way to do this?

Arrays are of uniform type. You can't have an array where different entries have different data types.
What you appear to want is an anonymous row (record).
DECLARE
debug_row record;
BEGIN
SELECT * FROM some_table LIMIT 1 INTO debug_row;
RAISE INFO 'insert result: %',debug_row;
Note that this only works for a single row result. For multiple rows you can call the query as the input for a loop and iterate over the result. There are examples in the PL/PgSQL documentation.

Related

How to duplicate rows with a column change for a list of tables?

Given the following example:
BEGIN
FOR r IN (
SELECT * FROM table_one WHERE change_id = 0
) LOOP
r.change_id := -1;
INSERT INTO table_one VALUES r;
END LOOP;
END;
This inserts new rows to table_one with the exact same content, except the intended change on column change_id to the value -1. I don't have to specify the columns inside of the script as I have to in an INSERT INTO table_one (change_id, ...) SELECT -1, ... FROM table_one WHERE change_id=0;
It works perfectly fine. But how to modify this script to work with a list of tables? The internal structure of those tables are different, but all of them have the necessary column change_id.
Of course the easiest solution would be to copy and paste this snippet x-times and replace the fix table name inside. But is there an option to work with a list of tables in an array?
My approach was like this:
DECLARE
TYPE tablenamearray IS VARRAY(30) OF VARCHAR2(30);
tablenames tablenamearray;
BEGIN
tablenames := tablenamearray('TABLE_ONE', 'TABLE_TWO', 'TABLE_THREE'); -- up to table 30...
FOR i IN tablenames.first..tablenames.last LOOP
/* Found no option to use tablenames(i) here with dynamic SQL */
END LOOP;
END;
Note: There is no technical primary key like an id with a sequence behind. The primary key is build by three columns incl. the change_id column.
You cannot create a SQL statement where the statement is not known at parse time. So, you cannot have a variable as a table name. What you're looking for is Dynamic SQL, which is a fairly complicated topic, but basically you're going to wind up building a SQL statement with DBMS_SQL or running a statement as a string with EXECUTE IMMEDIATE.

oracle plsql chunking the rows

I have a select statement which returns 0 or more rows.
I'm trying to come up with a plsql proc with a cursor to produce xml output fro, all rows returned into 100 rows at a time. i'm doing this to chunk loo rows at a time based on requirement.
So basically my program should follow below logic
cursor c1 is select id,code_id,code_desc from table order by id; --returns some rows
if c1%notfound
then return;` -- exit from procedure
else
loop
grab first 100 rows from select and append to a variable
and assign it to a variable;
update this variable into a clob field in a table.
grab next 100 rows and append into a variable
update this variable into a clob field in a table in another row;see below
table data
and so on
and grab remaining rows and append into a variable
print the variable;
until no data found;
exit
I'm trying to do convert the output from select statement into xml text.
The output should look something like below:
TABLE: STG_XML_DATA
LOOP_NO(NUMBER), XML_TEXT(CLOB), ROWS_PROCESSED
1 <XML><id>1</ID><id>2</ID>..<ID>100</ID></XML> 100
2 <XML><id>101</ID><id>102</ID>..<ID>200</ID></XML> 200
3 <XML><id>301</ID><id>102</ID>..<ID>320</ID></XML> 20
Can someone please help
First of all, can you do this with a single INSERT ... SELECT statement that does what you want with reasonable performance? If you're doing a million rows, yes, breaking them up into chunks may be a good idea. But if it's 100, that might be your best bet.
For your actual question, you want to use BULK COLLECT into a collection variable and possibly FORALL. So your function is going to look something like this:
DECLARE
TYPE id_tt IS TABLE OF NUMBER;
TYPE desc_tt IS TABLE OF VARCHAR2(100);
l_ids id_tt;
l_code_ids id_tt;
l_code_descs desc_tt;
cursor c1 is select id,code_id,code_desc from table
BEGIN
OPEN c1;
LOOP
FETCH c1 BULK COLLECT INTO l_ids, l_code_ids, l_code_descs
LIMIT 100;
EXIT WHEN l_ids.COUNT = 0;
FORALL idx IN 1..l_ids.COUNT
INSERT [... some insert statement here ...]
[... maybe some other processing here...]
END LOOP;
CLOSE c1;
END;
What you absolutely do not want to do is fetch a row, process it, fetch another row, etc. SQL is a set-oriented language, so try to operate on sets. Every time you switch context from SQL to PL/SQL there is a cost and it can kill your performance.
See: http://www.oracle.com/technetwork/issue-archive/2012/12-sep/o52plsql-1709862.html

Function results column names to be used in select statement

I have function which returns column names and i am trying to use the column name as part of my select statement, but my results are coming as column name instead of values
FUNCTION returning column name:
get_col_name(input1, input2)
Can И use this query to the results of the column from table -
SELECT GET_COL_NAME(input1,input2) FROM TABLE;
There are a few ways to run dynamic SQL directly inside a SQL statement. These techniques should be avoided since they are usually complicated, slow, and buggy. Before you do this try to find another way to solve the problem.
The below solution uses DBMS_XMLGEN.GETXML to produce XML from a dynamically created SQL statement, and then uses XML table processing to extract the value.
This is the simplest way to run dynamic SQL in SQL, and it only requires built-in packages. The main limitation is that the number and type of columns is still fixed. If you need a function that returns an unknown number of columns you'll need something more powerful, like the open source program Method4. But that level of dynamic code gets even more difficult and should only be used after careful consideration.
Sample schema
--drop table table1;
create table table1(a number, b number);
insert into table1 values(1, 2);
commit;
Function that returns column name
create or replace function get_col_name(input1 number, input2 number) return varchar2 is
begin
if input1 = 0 then
return 'a';
else
return 'b';
end if;
end;
/
Sample query and result
select dynamic_column
from
(
select xmltype(dbms_xmlgen.getxml('
select '||get_col_name(0,0)||' dynamic_column from table1'
)) xml_results
from dual
)
cross join
xmltable
(
'/ROWSET/ROW'
passing xml_results
columns dynamic_column varchar2(4000) path 'DYNAMIC_COLUMN'
);
DYNAMIC_COLUMN
--------------
1
If you change the inputs to the function the new value is 2 from column B. Use this SQL Fiddle to test the code.

Inserting a record in PL/SQL block using for loop is yielding error

I am creating a table of inetegers called 'integer_properties' from 1 to 1000 the table columns are:
integer,isPrime,isOdd,isEven,digitCount;
I want to insert records using a for loop i tried following but the error says: 'missing SELECT keyword'
BEGIN
for k in 1..1000
loop
insert into integer_properties(integer,
isPrime,
isEven,
isOdd,
digitCount)
values(k,null,null,null,null);
end loop;
END;
It is tedious to enter 1000 numbers with DDL command without using PL/SQL block. I am trying to enter the loop variable values in the integer column. Is it possible to do that?
You can do it in a single query.
insert into integer_properties
select level, null, null, null, null
from dual
connect by level <= 1000;
commit;
LEVEL is a pseudocolumn used in hierarchical queries.
Your code seems fine, but using INTEGER as column name might be is causing problem. If you enclose it in double quotes, it will work fine. So, better to avoid using keywords while naming columns.

Can a table variable be used in a select statement where clause?

I have a stored procedure that is doing a two-step query. The first step is to gather a list of VARCHAR2 type characters from a table and collect them into a table variable, defined like this:
TYPE t_cids IS TABLE OF VARCHAR2(50) INDEX BY PLS_INTEGER;
v_cids t_cids;
So basically I have:
SELECT item BULK COLLECT INTO v_cids FROM table_one;
This works fine up until the next bit.
Now I want to use that collection in the where clause of another query within the same procedure, like so:
SELECT * FROM table_two WHERE cid IN v_cids;
Is there a way to do this? I am able to select an individual element, but I would like to use the table variable like a would use a regular table. I've tried variations using nested selects, but that doesn't seem to work either.
Thanks a lot,
Zach
You have several choices as to how you achieve this.
If you want to use a collection, then you can use the TABLE function to select from it but the type of collection you use becomes important.
for a brief example, this creates a database type that is a table of numbers:
CREATE TYPE number_tab AS TABLE OF NUMBER
/
Type created.
The next block then populates the collection and performs a rudimentary select from it using it as a table and joining it to the EMP table (with some output so you can see what's happening):
DECLARE
-- Create a variable and initialise it
v_num_tab number_tab := number_tab();
--
-- This is a collection for showing the output
TYPE v_emp_tabtype IS TABLE OF emp%ROWTYPE
INDEX BY PLS_INTEGER;
v_emp_tab v_emp_tabtype;
BEGIN
-- Populate the number_tab collection
v_num_tab.extend(2);
v_num_tab(1) := 7788;
v_num_tab(2) := 7902;
--
-- Show output to prove it is populated
FOR i IN 1 .. v_num_tab.COUNT
LOOP
dbms_output.put_line(v_num_tab(i));
END LOOP;
--
-- Perform a select using the collection as a table
SELECT e.*
BULK COLLECT INTO v_emp_tab
FROM emp e
INNER JOIN TABLE(v_num_tab) nt
ON (e.empno = nt.column_value);
--
-- Display the select output
FOR i IN 1 .. v_emp_tab.COUNT
LOOP
dbms_output.put_line(v_emp_tab(i).empno||' is a '||v_emp_tab(i).job);
END LOOP;
END;
You can see from this that the database TYPE collection (number_tab) was treated as a table and could be used as such.
Another option would be to simply join your two tables you are selecting from in your example:
SELECT tt.*
FROM table_two tt
INNER JOIN table_one to
ON (to.item = tt.cid);
There are other ways of doing this but the first might suit your needs best.
Hope this helps.
--Doesn't work.
--SELECT item BULK COLLECT AS 'mySelectedItems' INTO v_cids FROM table_one;
SELECT table_two.*
FROM table_two INNER JOIN v_cids
ON table_two.paramname = v_cids.mySelectedItems;
Unless I'm misunderstanding the question, this should only return results that are in the table variable.
Note: I've never used Oracle, but I imagine this case would be the same.

Resources