SQL Query did not show result on Data Block Oracle Forms - oracle

I want show SQL Query Data into Data Block Through When-Button-Pressed.
Code:
DECLARE
p_cnic VARCHAR2(20);
BEGIN
p_cnic := 'SELECT cnicno FROM hof WHERE cnicno IN (SELECT cnic_no FROM we_group_hof_k)';
:we_group_hof_k.CNIC_NO := p_cnic;
END;
The data block "CNIC_NO" Data Type is VARCHAR
When I pressed the button then I am getting error
FRM-40735: WHEN-BUTTON-PRESSED Trigger raised unhandled exception

You just need to use an INTO clause with a SELECT statement without quotes as :
BEGIN
SELECT cnicno
INTO :we_group_hof_k.CNIC_NO
FROM hof
WHERE cnicno IN (SELECT cnic_no FROM we_group_hof_k);
EXCEPTION WHEN no_data_found THEN null;
END;
In your case, no need to use a local variable ( p_cnic ), e.g. you
can directly assign value to the field. If you really needed, prefer
defining as p_cnic hof.cnicno%type instead, in which no matter what the data type of the column.
Add Exception handling against the possibility to raise no_data_found
exception
If you need to bring multiple records, using an SELECT .. INTO
clause is not suitable, since you'd get
ORA-01422: exact fetch returns more than one requested number
in such a case.
Prefer using a cursor instead, against the situation above :
BEGIN
go_block('myblock');
first_record;
for c in
(
SELECT cnicno
FROM hof
WHERE cnicno IN (SELECT cnic_no FROM we_group_hof_k)
)
loop
:we_group_hof_k.CNIC_NO := c.cnicno;
next_record;
end loop;
END;

Related

PLS-00357: Table,View Or Sequence reference 'JANUARY_2020' not allowed in this context

I am using this code to see if it will work for a procedure. I want to be able to make a procedure in which i can decide what data to extract by typing the time ('jan-2020') in which it is recorded and also to decide in which table i want to place the data in (january_2020). i get the error that the table is not able to be used in this context. What do i have to change in the code to be in the right context?
Is it because i am using dynamic sql in a loop that requires the loop to be executed to put the data in the table? or is it because i am using %rowtype as the attribute for the table ALL_DATA to create its own columns? If it is any of these what should i do to change it?
DECLARE
time_v varchar2(9);
table_v varchar2(200);
sql_code varchar2(300);
TYPE Copied_Table IS TABLE OF Gastos%ROWTYPE;
All_Data Copied_Table;
BEGIN
time_v := 'jan-2020';
SELECT *
BULK COLLECT INTO All_Data FROM Gastos
Where TO_CHAR(DATE_, 'MON-YYYY') = UPPER(time_v);
FOR I in All_Data.First .. All_Data.Last LOOP
sql_code := 'INSERT INTO :table_v ( DATE_, DESCRIPTION, ORIGINAL_DESCRIPTION, AMOUNT,
TRANSACTION_TYPE, CATEGORY, ACCOUNT_NAME)
Values ( ALL_Data(i).date_, ALL_Data(i).description, ALL_Data(i).original_description,
ALL_Data(i).amount, ALL_Data(i).transaction_type, ALL_Data(i).category, ALL_Data(i).account_name)';
table_v := january_2020;
execute immediate sql_code
using table_v;
END LOOP;
END upload_monthly_expenses;
Pass table name as input parameter and replace bind variable with normal variable for the table name and concatenate it to the DML statement.Modify your code as below,
CREATE OR REPLACE PROCEDURE upload_monthly_expenses(table_v IN VARCHAR2,time_v IN VARCHAR2) AS
DECLARE
sql_code varchar2(300);
TYPE Copied_Table IS TABLE OF Gastos%ROWTYPE;
All_Data Copied_Table;
BEGIN
SELECT *
BULK COLLECT INTO All_Data FROM Gastos
Where TO_CHAR(DATE_, 'MON-YYYY') = UPPER(time_v);
FOR I in All_Data.First .. All_Data.Last LOOP
sql_code := 'INSERT INTO '||table_v||' ( DATE_, DESCRIPTION, ORIGINAL_DESCRIPTION, AMOUNT,
TRANSACTION_TYPE, CATEGORY, ACCOUNT_NAME)
Values ( ALL_Data(i).date_, ALL_Data(i).description, ALL_Data(i).original_description,
ALL_Data(i).amount, ALL_Data(i).transaction_type, ALL_Data(i).category, ALL_Data(i).account_name)';
execute immediate sql_code;
END LOOP;
END;
From a PL/SQL block procedure can be executed as below,
BEGIN
upload_monthly_expenses('jan-2020','january_2020');
END;

DBMS_SQL.EXECUTE not giving output when the SQL have DBMS_XMLGEN.GETXML

I am generating a dynamic SQL which generates XML data, using SELECT DBMS_XMLGEN.GETXML, as the output have special characters,
which are not supported in XML. I tried and tested SQL in separately, It is working as expected.
I am not able to fetch the data, when I call it using Dynamic SQL.
I have used DBMS_SQL.DESC_TAB2 as it is treating whole SQL starting DBMS_XMLGEN.GETXML as one column.
--Snippet
Declaration
SelectCursorId NUMBER; --For Dynamic SQL binding
RowProcessed INTEGER; --For Dynamic SQL binding
MstrSeqNbr NUMBER := 1;
ColumnCount NUMBER; --For Dynamic SQL binding
RecordSqrNbr NUMBER := 0; --For Dynamic SQL binding
ColumnDescTbl DBMS_SQL.DESC_TAB2; --dbms_sql.desc_tab2
ColumnValue VARCHAR2(4000); --For Dynamic SQL binding
DymanicSQLCols VARCHAR2(4000); -- For debugging purpose, columns returned
SelectSQL VARCHAR2(6000);
BEGIN
--Snippet
SelectSQL := 'SELECT DBMS_XMLGEN.GETXML('SELECT MRQ.BatchNBR AS Batch_NUMBER,
MRQ.BatchRUNSEQNBR AS Batch_RUN_INSTANCE,
MRQ.BatchRUNDATE AS RUN_DATE,
MRQ.SPDATE AS POST_DATE,
MRQ.BatchNAME AS Batch_NAME,
MRQ.RPTNAME AS DATABASE_NAME,
MRQ.EFFDATE AS RUN_TIME,
MRQ.BatchSTARTDATE AS ELAPSED_TIME ,
CURSOR ( SELECT MRI.MSTREPORTRECSEQNBR AS RECORD_SEQUENCE_NUMBER,
MRI.RTTEXT1VC100 AS COUNTRY_NAME,
MRI.RTTEXT2VC100 AS CURRENCY_USED,
MRI.RTTEXT1VC50 AS COUNTRY_SHORT_CODE,
MRI.RTNUM1P0 AS ISO_CURRENCY_CODE
FROM MasterReporting MRI
WHERE BatchNbr = MR.BatchNbr
AND BatchRUNSEQNBR = MR.BatchRUNINSTANCE
ORDER BY MSTREPORTRECSEQNBR )Record
FROM BatchRUNHIST MR , MastRptSeqDtl MRQ
WHERE MR.BatchNbr = 100
AND MR.BatchRUNINSTANCE IN( 67)
AND MRQ.BatchRUNSEQNBR = MR.BatchRUNINSTANCE
AND MR.BatchRunStatCD =''COMPL''
')
FROM DUAL ';
SelectCursorId := DBMS_SQL.OPEN_CURSOR; --Pass
DBMS_SQL.PARSE ( SelectCursorId, SelectSQL, DBMS_SQL.NATIVE); --Pass
DBMS_SQL.DESCRIBE_COLUMNS2( SelectCursorId, ColumnCount, ColumnDescTbl); --Pass
** However ColumnDescTbl(1).col_name is only giving following, not sure if this is the issue
DBMS_XMLGEN.GETXML('SELECT MRQ.BatchNBRASBatch_NUMBER, MRQ.BatchRUNSEQNBRASBatch_RUN_INSTANCE, MRQ.BatchRUNDATEASRUN_DATE, MRQ.SPDATEASPOST_DATE, MRQ.BatchNAMEASBatch_NAME, MRQ.RPTNAMEASDATABAS
Next step also passes
For k in 1..ColumnCount LOOP
DBMS_SQL.DEFINE_COLUMN(SelectCursorId, k, ColumnValue, 4000); --Pass
END LOOP;
RowProcessed := DBMS_SQL.EXECUTE(SelectCursorId);
--Passes but gives 0 as output,
--Whereas, running the SQL separately gives you one row of XML data.
Minimum One row of XML Data should be returned.
From the documentation for dbms_sql.execute:
The return value is only valid for INSERT, UPDATE, and DELETE statements; for other types of statements, including DDL, the return value is undefined and must be ignored.
As your statement is a select the RowProcessed return value has no meaning; it isn't significant that you're seeing zero there.
You need to do a fetch_rows step after that, or change it to execute_and_fetch. Either way you then need to process the fetched data, of course.
(The col_name looks like a non-issue, it's just an automatically generated name/alias. If you change your dynamic statement to do ...) AS my_col_alias FROM DUAL then the col_name will be reported as MY_COL_ALIAS.)

Oracle Trigger causing invalid sql statement error [duplicate]

In SQL Server we can use this:
DECLARE #variable INT;
SELECT #variable= mycolumn from myTable;
How can I do the same in Oracle? I'm currently attempting the following:
DECLARE COMPID VARCHAR2(20);
SELECT companyid INTO COMPID from app where appid='90' and rownum=1;
Why this is not working?
SELECT INTO
DECLARE
the_variable NUMBER;
BEGIN
SELECT my_column INTO the_variable FROM my_table;
END;
Make sure that the query only returns a single row:
By default, a SELECT INTO statement must return only one row. Otherwise, PL/SQL raises the predefined exception TOO_MANY_ROWS and the values of the variables in the INTO clause are undefined. Make sure your WHERE clause is specific enough to only match one row
If no rows are returned, PL/SQL raises NO_DATA_FOUND. You can guard against this exception by selecting the result of an aggregate function, such as COUNT(*) or AVG(), where practical. These functions are guaranteed to return a single value, even if no rows match the condition.
A SELECT ... BULK COLLECT INTO statement can return multiple rows. You must set up collection variables to hold the results. You can declare associative arrays or nested tables that grow as needed to hold the entire result set.
The implicit cursor SQL and its attributes %NOTFOUND, %FOUND, %ROWCOUNT, and %ISOPEN provide information about the execution of a SELECT INTO statement.
Not entirely sure what you are after but in PL/SQL you would simply
DECLARE
v_variable INTEGER;
BEGIN
SELECT mycolumn
INTO v_variable
FROM myTable;
END;
Ollie.
One Additional point:
When you are converting from tsql to plsql you have to worry about no_data_found exception
DECLARE
v_var NUMBER;
BEGIN
SELECT clmn INTO v_var FROM tbl;
Exception when no_data_found then v_var := null; --what ever handle the exception.
END;
In tsql if no data found then the variable will be null but no exception
ORA-01422: exact fetch returns more than requested number of rows
if you don't specify the exact record by using where condition, you will get the above exception
DECLARE
ID NUMBER;
BEGIN
select eid into id from employee where salary=26500;
DBMS_OUTPUT.PUT_LINE(ID);
END;
For storing a single row output into a variable from the select into query :
declare v_username varchare(20);
SELECT username into v_username FROM users WHERE user_id = '7';
this will store the value of a single record into the variable v_username.
For storing multiple rows output into a variable from the select into query :
you have to use listagg function. listagg concatenate the resultant rows of a coloumn into a single coloumn and also to differentiate them you can use a special symbol.
use the query as below
SELECT listagg(username || ',' ) within group (order by username) into v_username FROM users;

Declaring a variable and setting its value from a SELECT query in Oracle

In SQL Server we can use this:
DECLARE #variable INT;
SELECT #variable= mycolumn from myTable;
How can I do the same in Oracle? I'm currently attempting the following:
DECLARE COMPID VARCHAR2(20);
SELECT companyid INTO COMPID from app where appid='90' and rownum=1;
Why this is not working?
SELECT INTO
DECLARE
the_variable NUMBER;
BEGIN
SELECT my_column INTO the_variable FROM my_table;
END;
Make sure that the query only returns a single row:
By default, a SELECT INTO statement must return only one row. Otherwise, PL/SQL raises the predefined exception TOO_MANY_ROWS and the values of the variables in the INTO clause are undefined. Make sure your WHERE clause is specific enough to only match one row
If no rows are returned, PL/SQL raises NO_DATA_FOUND. You can guard against this exception by selecting the result of an aggregate function, such as COUNT(*) or AVG(), where practical. These functions are guaranteed to return a single value, even if no rows match the condition.
A SELECT ... BULK COLLECT INTO statement can return multiple rows. You must set up collection variables to hold the results. You can declare associative arrays or nested tables that grow as needed to hold the entire result set.
The implicit cursor SQL and its attributes %NOTFOUND, %FOUND, %ROWCOUNT, and %ISOPEN provide information about the execution of a SELECT INTO statement.
Not entirely sure what you are after but in PL/SQL you would simply
DECLARE
v_variable INTEGER;
BEGIN
SELECT mycolumn
INTO v_variable
FROM myTable;
END;
Ollie.
One Additional point:
When you are converting from tsql to plsql you have to worry about no_data_found exception
DECLARE
v_var NUMBER;
BEGIN
SELECT clmn INTO v_var FROM tbl;
Exception when no_data_found then v_var := null; --what ever handle the exception.
END;
In tsql if no data found then the variable will be null but no exception
ORA-01422: exact fetch returns more than requested number of rows
if you don't specify the exact record by using where condition, you will get the above exception
DECLARE
ID NUMBER;
BEGIN
select eid into id from employee where salary=26500;
DBMS_OUTPUT.PUT_LINE(ID);
END;
For storing a single row output into a variable from the select into query :
declare v_username varchare(20);
SELECT username into v_username FROM users WHERE user_id = '7';
this will store the value of a single record into the variable v_username.
For storing multiple rows output into a variable from the select into query :
you have to use listagg function. listagg concatenate the resultant rows of a coloumn into a single coloumn and also to differentiate them you can use a special symbol.
use the query as below
SELECT listagg(username || ',' ) within group (order by username) into v_username FROM users;

Oracle Ref Cursor Vs Select into with Exception handling

I have a couple of scenarios:
Need to read the value of a column from three different tables in a predefined order and only 1 table will have the data
Read data from table1 if records are present for criteria given else read data from Table2 for given criteria
In Oracle Stored Procedures
The way these are being handled right now is to first get the count for a given query into a variable, and if the count > 0, then we execute the same query to read the actual data as in:
select count(*) from table1 into v_count
if v_count > 0
then
select data into v_data from table1
end if;
Return v_data
This is being done to avoid the no_data_found exception, otherwise I would need three exception handler blocks to catch the no_data_found exception for each table access.
Currently I am reimplementing this with Cursors so that I have something like this:
cursor C1 is
select data from table1;
Open C1
Fetch C1 into v_data
if C1%FOUND
then
Close C1
Return v_data
End If
I wanted to find out which one is better from a performance point of view--the one with Cursors, or the one which does a Select into a variable and has three no_data_found Exception blocks. I don't want to use the two stage query process which we have currently.
I don't know why you are so keen to avoid the exception? What is wrong with:
begin
begin
select data into v_data from table1;
exception
when no_data_found then
begin
select data into v_data from table2;
exception
when no_data_found then
begin
select data into v_data from table3;
exception
when no_data_found then
v_data := null;
end;
end;
end;
return v_data;
end;
I believe this will perform better than your other solution because it does the minimum possible work to achieve the desired result.
See How bad is ignoring Oracle DUP_VAL_ON_INDEX exception? where I demonstrate that using exceptions performs better than counting to see if there is any data.
select count(*) from table1 into v_count
if v_count > 0 then
select data into v_data from table1;
else
v_data := null;
end if;
return v_data;
is NOT equivalent to
begin
select data into v_data from table1;
return v_data;
exception
when no_data_found then
return null;
end;
in a multi-user environment. In the first case, someone could update the table between the points where you check for existence and when you read the data.
Performance-wise, I have no idea which is better, but I know that the first option makes two context switches to the sql engine and the second one only does one context switch.
The way you're handling scenario 1 now is not good. Not only are you doing two queries when one will suffice, but as Erik pointed out, it opens up the possibility of data changing between the two queries (unless you use a read-only or serializable transaction).
Given that you say that in this case the data will be in exactly one of three tables, how about this?
SELECT data
INTO v_data FROM
(SELECT data FROM table1
UNION ALL
SELECT data FROM table2
UNION ALL
SELECT data FROM table3
)
Another "trick" you can use to avoid writing multiple no-data-found handlers would be:
SELECT MIN(data) INTO v_data FROM table1;
IF v_data IS NOT NULL THEN
return v_data;
END IF;
SELECT MIN(data) INTO v_data FROM table2;
...etc...
but I don't really see any reason that's better than having three exception handlers.
For your second scenario, I think what you mean is that there may be data in both tables and you want to use the data from table1 if present, otherwise use the data from table 2. Again you could do this in a single query:
SELECT data
INTO v_data FROM
(SELECT data FROM
(SELECT 1 sort_key, data FROM table1
UNION ALL
SELECT 2 sort_key, data FROM table2
)
ORDER BY sort_key ASC
)
WHERE ROWNUM = 1
An enhanced version of "Dave Costa"'s MIN option...
SELECT COUNT(1), MIN(data) INTO v_rowcount, v_data FROM table2;
Now v_rowcount can be checked for values 0, >1 (greater than 1) where normal select query will throw NO_DATA_FOUND or TOO_MANY_ROWS exception. Value "1" will indicate that exact one row exists and will serve our purpose.
DECLARE
A VARCHAR(35);
B VARCHAR(35);
BEGIN
WITH t AS
(SELECT OM_MARCA, MAGAZIA FROM ifsapp.AKER_EFECTE_STOC WHERE (BARCODE = 1000000491009))
SELECT
(SELECT OM_MARCA FROM t) OM_MARCA,
(SELECT MAGAZIA FROM t) MAGAZIA
INTO A, B
FROM DUAL;
IF A IS NULL THEN
dbms_output.put_line('A este null');
END IF;
dbms_output.put_line(A);
dbms_output.put_line(B);
END;
/
Use the "for row in cursor" form of a loop and the loop will just not process if there is no data:
declare cursor
t1Cur is
select ... from table1;
t2Cur is
select ... from table2;
t3Cur is
select ... from table3;
t1Flag boolean FALSE;
t2Flag boolean FALSE;
t3Flag boolean FALSE;
begin
for t1Row in t1Cur loop
... processing, set t1Flag = TRUE
end loop;
for t2Row in t2Cur loop
... processing, set t2Flag = TRUE
end loop;
for t3Row in t3Cur loop
... processing, set t3Flag = TRUE
end loop;
... conditional processing based on flags
end;

Resources