Creating one line of output for my result using a CURSOR - sql

I'm successful to acquire the data I need but I wish to put the results as a continuous string separated by pipes here is my sample code
CONNECT mgs/mgs;
SET SERVEROUTPUT ON;
DECLARE
CURSOR PRODUCT_SUMMARY_CURSOR IS
SELECT PRODUCT_NAME ,LIST_PRICE FROM PRODUCTS
ORDER BY LIST_PRICE DESC;
PRODUCT_SUMMARY_ROW PRODUCTS%ROWTYPE;
BEGIN
FOR PRODUCT_SUMMARY_ROW IN PRODUCT_SUMMARY_CURSOR LOOP
IF (PRODUCT_SUMMARY_ROW.LIST_PRICE > 700) THEN
DBMS_OUTPUT.PUT_LINE ('"' || PRODUCT_SUMMARY_ROW.PRODUCT_NAME || '",' || '"' || PRODUCT_SUMMARY_ROW.LIST_PRICE || '"' || '|');
END IF;
END LOOP;
END;
/

Depending on how many products you are going to be processing the below should work. If the string gets to long then you might need to look at using a clob rather than a varchar2.
CONNECT mgs/mgs;
SET SERVEROUTPUT ON;
DECLARE
v_output_string varchar2(4000) default null;
CURSOR PRODUCT_SUMMARY_CURSOR IS
SELECT PRODUCT_NAME ,LIST_PRICE FROM PRODUCTS
ORDER BY LIST_PRICE DESC;
PRODUCT_SUMMARY_ROW PRODUCTS%ROWTYPE;
BEGIN
FOR PRODUCT_SUMMARY_ROW IN PRODUCT_SUMMARY_CURSOR LOOP
IF (PRODUCT_SUMMARY_ROW.LIST_PRICE > 700) THEN
v_output_string := v_output_string || '"' || PRODUCT_SUMMARY_ROW.PRODUCT_NAME || '",' || '"' || PRODUCT_SUMMARY_ROW.LIST_PRICE || '"' || '|';
END IF;
END LOOP;
dbms_output.put_line(v_output_string);
END;
/

Related

How to execute query in string mode in pl/sql?

I m trying to execute the procedure but it is showing the errror of missing expression while executing this line execute immediate sqlQuery into test;
My query returns 6 columns which i need to store into the variables , please help to execute this query.
procedure getAttributeOptions(subID number, compID number,docNumber varchar2,transType varchar2 ,rowNum varchar2 ,
Attribute1 out varchar2,Attribute2 out varchar2,Attribute3 out varchar2,Attribute4 out varchar2,
Attribute5 out varchar2, Attribute6 out varchar2) is
sqlQuery varchar2(4000);
storageColumns varchar2(4000);
colName varchar2(1000);
tableName varchar2(1000);
primaryKey varchar2(1000);
test varchar2(4000);
begin
colName:=' ATTRIBUTE_OPTION_1,ATTRIBUTE_OPTION_2,ATTRIBUTE_OPTION_3,ATTRIBUTE_OPTION_4,ATTRIBUTE_OPTION_5,ATTRIBUTE_OPTION_6 ';
storageColumns:=' Attribute1,Attribute2,Attribute3,Attribute4,Attribute5,Attribute6 ';
if transType = TY_ISSUE then
tableName:='preq_master_detail';
primaryKey:='req_number';
elsif transType=TY_TRANSFER then
tableName:='pinv_transfer_detail';
primaryKey:='transfer_id';
elsif transType=TY_RECEIVE then
tableName:='ppo_master_detail';
primaryKey:='po_number';
elsif transType=TY_MANUAL then
tableName:='part_manual_adjustment';
primaryKey:='adjustment_id';
end if;
dbms_output.put_line(tableName);
if tableName is not null then
begin
sqlQuery:='select ' || colName || 'into ' || storageColumns || 'from ' || tableName ;
sqlQuery:=sqlQuery || ' where subscriber_id=' || subID;
sqlQuery:=sqlQuery || ' and company_id=' || compID;
sqlQuery:=sqlQuery || ' and ' || primaryKey ||'='|| '''' || docNumber || '''' || ' and row_number ='||rowNum;
dbms_output.put_line(sqlQuery);
execute immediate sqlQuery into test;--issue in this line
dbms_output.put_line(Attribute1);
dbms_output.put_line(Attribute2);
dbms_output.put_line(Attribute3);
dbms_output.put_line(Attribute4);
dbms_output.put_line(Attribute5);
dbms_output.put_line(Attribute6);
exception when NO_DATA_FOUND then return ;
end;
end if;
end;
You are placing the list of variables for your INTO clause inside the SQL, rather than outside in PL/SQL. You need to use put the INTO clause outside. Hopefully you can hard-code that clause:
sqlQuery:='select ' || colName || 'from ' || tableName ;
. . .
execute immediate sqlQuery into Attribute1,Attribute2,Attribute3,Attribute4,Attribute5,Attribute6;
While you are at it, you should use bind variables for your WHERE clause:
. . .
sqlQuery:=sqlQuery || ' where subscriber_id=:subid';
sqlQuery:=sqlQuery || ' and company_id=:compID';
sqlQuery:=sqlQuery || ' and ' || primaryKey ||'= :docno'|| ' and row_number =:rowNum';
execute immediate sqlQuery
INTO Attribute1,Attribute2,Attribute3,Attribute4,Attribute5,Attribute6
USING subid,compid,docNumber,rowNum;
That will make your DBA happy.

PLSQL procedure output having weird blocks

I am trying to display like first screenshot below and I don't understand the output I am getting (second screenshot).
Below is my PLSQL code that I wrote based on my own database. How do I get rid of those weird blocks in my output? Thanks in advance ~
create or replace procedure numberOfSupplier (x int := 0 ) is
begin
for QRow in (SELECT REGION.R_NAME,NATION.N_NAME,COUNT(SUPPLIER.S_NATIONKEY) counter
FROM REGION
INNER JOIN NATION ON NATION.N_REGIONKEY=REGION.R_REGIONKEY
INNER JOIN SUPPLIER ON SUPPLIER.S_NATIONKEY=NATION.N_NATIONKEY
GROUP BY SUPPLIER.S_NATIONKEY,REGION.R_NAME,NATION.N_NAME
HAVING COUNT(SUPPLIER.S_NATIONKEY)> x)
loop
dbms_output.put_line ('R_NAME' || chr(18) || 'N_NAME' || chr(18) || 'COUNT(S_NATIONKEY)' || chr(10));
dbms_output.put_line (QRow.R_NAME || chr(18) || QRow.N_NAME || chr(18) || QRow.counter || chr(10));
end loop;
end;
/
show errors;
execute numberOfSupplier(130);

Query for particular integer value from multiple columns with number datatype

I try to search a number from multiple columns (datatype number), but get ORA-01722: invalid number error.
My Query:
SELECT *
FROM CAMPAIGN
WHERE 1481125 IN (select column_name
from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type = 'NUMBER');
What is wrong with it?
You are comparing your number 1481125 with the names of the each column, not the values of each column in your table.
To go from a column's name (from dba_tab_columns) to the values in that column, you need to use some form of dynamic SQL. Here's a relatively simple example:
DECLARE
-- Since I don't have your CAMPAIGN table or data, I'm using DBA_OBJECTS in it's place.
l_table_name VARCHAR2 (30) := 'DBA_OBJECTS';
l_search_number NUMBER := 20; -- 1481125 in your example
l_record dba_objects%ROWTYPE;
l_sql VARCHAR2 (32000);
l_column_number NUMBER := 0;
l_cur SYS_REFCURSOR;
BEGIN
-- First: build dynamic SQL statement of the form:
-- SELECT * FROM table_name WHERE
-- ( ( col_name_a = 20 ) OR ( col_name_b = 20 ) OR ... )
l_sql := 'SELECT * FROM dba_objects WHERE ( ';
FOR r_number_column IN (SELECT column_name
FROM dba_tab_columns
WHERE table_name = l_table_name
AND data_type = 'NUMBER'
ORDER BY column_id) LOOP
IF l_column_number > 0 THEN
l_sql := l_sql || ' OR ';
END IF;
l_column_number := l_column_number + 1;
l_sql := l_sql || '(' || r_number_column.column_name || ' = ' || l_search_number || ')';
END LOOP;
IF l_column_number = 0 THEN
-- No number columns in table, so there should be no matches
l_sql := l_sql || ' 1=0';
END IF;
l_sql := l_sql || ')';
DBMS_OUTPUT.put_line (l_sql);
OPEN l_cur FOR l_sql;
LOOP
FETCH l_cur INTO l_record;
EXIT WHEN l_cur%NOTFOUND;
DBMS_OUTPUT.put_line ('Object Name ' || l_record.object_name || ' has search number ' || l_search_number);
END LOOP;
END;
Your query is:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
(select column_name from all_tab_columns where table_name = 'CAMPAIGN' AND data_type='NUMBER')
Breaking that down we have:
SELECT * FROM CAMPAIGN WHERE 1481125 IN (<a set of numbers>)
and the subquery:
select column_name from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type='NUMBER'
That subquery is going to return a list of column names e.g.
CAMPAIGN_COUNT
CAMPAIGN_ID
CAMPAIGN_NUMBER_OF_SOMETHINGS
Your query is thus equivalent to:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
('CAMPAIGN_COUNT', 'CAMPAIGN_ID', 'CAMPAIGN_NUMBER_OF_SOMETHINGS')
You can see why you would get the ORA-01722 error there?
You would need to write dynamic SQL to achieve your aim.

Runtime Query in Oracle store procedures not returning proper result

This is first my stint with procedure and I am trying to execute below oracle procedure but facing some issue. Any inputs on this would be really helpful:
Issue:-----
I have a select query that returns two values:
src_Columns contains:
ID_ELEMENT
ID_ELEMENT_SA
Now, When I am trying to travesre a select-query(single column) result using "For Loop", I am not getting the values of the column rather I am getting its name only.
FOR columnItem IN (SELECT src_Columns FROM ELEM90_LNK_ELEM_BOSE)
LOOP
dbms_output.put_line('src_Columns 3: ' || columnItem.src_Columns);
query_test:= 'insert into ' || destination_Table || '(NAME,' || dest_Columns_Value || ') VALUES( ''' || src_name_Value || ''',''' || columnItem.dummyValue || ''')';
dbms_output.put_line('query_test:' || query_test);
execute immediate query_test;
END LOOP;
I mean the test query generated is following when i use variable name(src_columns):
insert into ATT_WTPART(NAME,STRINGVALUE) VALUES( 'ID_ELEMENT_SA','ID_ELEMENT_SA')
whereas if I use ID_ELEMENT_SA instead of src_Columns in FOR LOOP
FOR columnItem IN (SELECT ID_ELEMENT FROM ELEM90_LNK_ELEM_BOSE)
then I get proper values that are desired like
insert into ATT_WTPART(NAME,STRINGVALUE) VALUES( 'ID_ELEMENT_SA','ID05')
How can I make sure that I get the values even when I am using the variable name instead of any hard-coding
Below is the Complete Procedure:-------------
create or replace
PROCEDURE ELEM90_Lnk_Elem_ATT_WTPART_MK
AS
CURSOR targ_dest_relation IS
SELECT sourcecolumn FROM mapping where destinationtable='ATT_WTPART';
BEGIN
DECLARE
dest_Columns varchar2(1000);
src_Columns varchar2(1000);
src_Type varchar2(1000);
destination_Table varchar2(1000) := 'ATT_WTPART';
source_Table varchar2(1000) := 'ELEM90_LNK_ELEM_BOSE';
query_test varchar2(1000);
query_test2 varchar2(1000);
src_name varchar2(255);
src_Type_Value varchar2(255);
dest_Columns_Value varchar2(255);
src_name_Value varchar2(255);
for_query varchar2(1000);
for_query_data varchar2(1000);
dummyValue varchar2(1000);
BEGIN
FOR rec IN targ_dest_relation loop
dbms_output.put_line('destination_Table: ' || destination_Table);
dbms_output.put_line('source_Table: ' || source_Table);
src_Columns := rec.sourcecolumn;
dbms_output.put_line('src_Columns: ' || src_Columns);
src_Type := 'select data_type from user_tab_columns where table_name ='''||source_Table||'''and column_name='''|| src_Columns ||'''';
dbms_output.put_line('src_Type: ' || src_Type);
execute immediate src_Type INTO src_Type_Value;
dbms_output.put_line('src_Type_Value: ' || src_Type_Value);
dest_Columns := 'select DEST_COLUMN from ATT_PART_MAPPING where SOURCETYPE='''|| src_Type_Value || '''';
dbms_output.put_line('dest_Columns: ' || dest_Columns);
execute immediate dest_Columns INTO dest_Columns_Value;
dbms_output.put_line('dest_Columns_Value: ' || dest_Columns_Value);
src_name := 'select column_name from user_tab_columns where table_name ='''|| source_Table ||''' and column_name= ''' || src_Columns || '''';
dbms_output.put_line('src_name: ' || src_name);
execute immediate src_name INTO src_name_Value;
dbms_output.put_line('src_name_Value: ' || src_name_Value);
FOR columnItem IN (SELECT src_Columns FROM ELEM90_LNK_ELEM_BOSE)
LOOP
dbms_output.put_line('src_Columns 3: ' || columnItem.src_Columns);
query_test:= 'insert into ' || destination_Table || '(NAME,' || dest_Columns_Value || ') VALUES( ''' || src_name_Value || ''',''' || columnItem.dummyValue || ''')';
dbms_output.put_line('query_test:' || query_test);
execute immediate query_test;
END LOOP;
END loop;
END;
END;
The problem with the line
FOR columnItem IN (SELECT src_Columns FROM ELEM90_LNK_ELEM_BOSE)
is that src_Columns is a local variable. As a result, you end up selecting the same value for each row in ELEM90_LNK_ELEM_BOSE.
If you want the value of the local variable to be used as a column name in the query, you must use dynamic SQL instead. Try replacing FOR columnItem ... LOOP ... END LOOP with the following:
OPEN curs FOR 'SELECT ' || src_Columns || ' FROM ELEM90_LNK_ELEM_BOSE';
LOOP
FETCH curs INTO column_value;
EXIT WHEN curs%NOTFOUND;
dbms_output.put_line('src_Columns 3: ' || column_value);
query_test:= 'insert into ' || destination_Table || '(NAME,' || dest_Columns_Value || ') VALUES( ''' || src_name_Value || ''',''' || column_value || ''')';
dbms_output.put_line('query_test:' || query_test);
execute immediate query_test;
END LOOP;
CLOSE curs;
You will need to declare the following extra variables:
curs SYS_REFCURSOR;
column_value VARCHAR2(4000);
However, in truth it would probably be better to remove the loop entirely. You can replace it with an INSERT INTO ... SELECT ... FROM ... statement instead, such as the following:
EXECUTE IMMEDIATE 'INSERT INTO ' || destination_Table || ' (NAME,' ||
dest_Columns_Value || ') SELECT :name,' || src_Columns ||
' FROM ELEM90_LNK_ELEM_BOSE' USING src_name_Value;
This also gets rid of the need for the two local variables curs and column_value, and is also likely to be considerably faster, as there's no need to parse dynamic SQL once for each row in the destination table.
Is it working better if you try this one:
query_test:= 'insert into ' || destination_Table ||
'(NAME,'||dest_Columns_Value||') VALUES (:p1, :p2)';
execute immediate query_test USING src_name_Value, columnItem.dummyValue;
At least it should have a positive impact on performance.

Best way to maintain daywise interval partitions in 11g

I had created a sql script in 11g that would drop all partitions having high value less than 60 days in all partitioned tables in the database
DECLARE
TNAME VARCHAR2 (300);
PNAME VARCHAR2 (300);
HIGHVAL VARCHAR2 (3000);
POSITION SMALLINT;
VAL LONG;
CURSOR C1
IS
SELECT TABLE_NAME, PARTITION_NAME, PARTITION_POSITION, HIGH_VALUE
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME NOT LIKE '%$%'
AND TABLE_NAME NOT LIKE 'BIN%';
BEGIN
OPEN C1;
LOOP
FETCH C1
INTO TNAME, PNAME, POSITION, VAL;
HIGHVAL := VAL;
EXIT WHEN C1%NOTFOUND;
IF TO_DATE (SUBSTR (HIGHVAL, 10, 11), 'RRRR-MM-DD') <
TRUNC (SYSDATE)
- 60
THEN
IF POSITION = 1
THEN
DBMS_OUTPUT.PUT_LINE ('ALTER TABLE ' || TNAME
|| ' SET INTERVAL();'
);
END IF;
DBMS_OUTPUT.PUT_LINE ( 'ALTER TABLE '
|| TNAME
|| ' DROP PARTITION '
|| PNAME
|| ' UPDATE GLOBAL INDEXES PARALLEL 2;'||CHR(10)
|| '--DROPPED'
|| '--'
|| TO_DATE (SUBSTR (HIGHVAL, 10, 11),
'RRRR-MM-DD'
)
);
IF POSITION = 1
THEN
DBMS_OUTPUT.PUT_LINE
( 'ALTER TABLE '
|| TNAME
|| ' SET INTERVAL(NUMTODSINTERVAL(1,''DAY''));'
);
END IF;
END IF;
END LOOP;
COMMIT;
CLOSE C1;
END;
/
I'm executing the generated SQL text
Kindly advice if this correct and any room for enhancement???
A few hints:
It's a perfect place for implicit cursor, so you don't need to manually declare variables, fetch, open & close, ...
Filter high value in the query, earlier is better
Use EXECUTE IMMEDIATE to apply changes instead of manually executing whatever gets printed
You don't need to commit, since it's all DDL
The code:
BEGIN
FOR p IN (SELECT TABLE_NAME, PARTITION_NAME, PARTITION_POSITION, HIGH_VALUE
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME NOT LIKE '%$%'
AND TABLE_NAME NOT LIKE 'BIN%'
AND TO_DATE (SUBSTR (HIGH_VALUE, 10, 11), 'RRRR-MM-DD') < TRUNC (SYSDATE) - 60)
LOOP
IF p.PARTITION_POSITION = 1
THEN
EXECUTE IMMEDIATE 'ALTER TABLE ' || p.TABLE_NAME || ' SET INTERVAL()';
END IF;
EXECUTE IMMEDIATE 'ALTER TABLE ' || p.TABLE_NAME
|| 'DROP PARTITION ' || p.PARTITION_NAME
|| ' UPDATE GLOBAL INDEXES PARALLEL 2';
IF p.PARTITION_POSITION = 1
THEN
EXECUTE IMMEDIATE 'ALTER TABLE ' || p.TABLE_NAME
|| ' SET INTERVAL(NUMTODSINTERVAL(1,''DAY''));';
END IF;
END LOOP;
END;
/
And a few warnings:
Watch out for UPDATE GLOBAL INDEXES, it does not work with IOT tables (if you use them) (I was wrong with this one)
Watch out for resetting/setting intervals. The general rule is that you can't drop last non-interval partition. Guessing this would be position 1 is risky. Better to rely on user_tab_partitions.interval flag.