I am trying to save the result of show con_name to a variable in ORACLE PL/SQL SQLPLUS - plsql

I want to write something like this:
exec SHOW CON_NAME into :=connection_name
but this doesnt work.
I know I can do this but I want to know for future reference if there is any way to do this for show
EXEC SELECT SYS_CONTEXT('USERENV','CON_NAME') into :connection_name from DUAL;

exec is just a wrapper for an anonymous block, so
exec SHOW CON_NAME into :=connection_name
is the same as
begin SHOW CON_NAME into :=connection_name end
Even with other issues fixed, show is a SQL*Plus client command, and doesn't mean anything inside a SQL or PL/SQL context.
You could do:
exec :connection_name := SYS_CONTEXT('USERENV','CON_NAME');
to avoid the context switch of selecting from dual within the PL/SQL.
Looking at the statement log in SQL Developer, show con_name is doing something similar, with a checkone bind variable it uses internally; though it also puts it through a local PL/SQL variable and trims that to 30 chars.
You could also use column ... new_value ... and query from dual, without using PL/SQL, and then use a substitution variable to refer to the value later instead of a bind variable:
column con_name new_value connection_name
select SYS_CONTEXT('USERENV','CON_NAME') as con_name from dual;
-- then later...
select '&connection_name' from dual;
Not sure how useful that would be though. Depends what you want to use it for I suppose.

Related

age control 2 for pl/sql

i am trying to find out at what age an employee started working.
if he started under 16 he should report this 'Error when entering the date of birth' mistake. so my trigger is created but my trigger is not working
I get ever this error: ORA-01422: Exact retrieval returns more than the requested number of lines
i can't find the problem
Here is the code:
SET SERVEROUTPUT ON
ACCEPT Birthday PROMPT ' Pleas give you Date of birth: '
CREATE OR REPLACE TRIGGER T_Controll
before INSERT ON meine_Firma -- Table
FOR EACH ROW
DECLARE
V_Berufstart meine_Firma.Hiredate%TYPE; --Job begin
V_Geburtsdatum DATE; -- Date of birth
V_Alter number:=0; -- AGE
SELECT HIREDATE INTO V_Berufstart FROM meine_Firma;
BEGIN
V_Geburtsdatum:=('&Birthday');
V_Alter:= Round(MONTHS_BETWEEN(V_Berufstart,V_Geburtsdatum)-2)/12;
IF 16 > V_Alter THEN
RAISE_APPLICATION_ERROR(-20201,'Error when entering the date of birth');
END IF;
END;
/
SET SERVEROUTPUT OFF
If he under 16 then he may not work
sorry my english is not good (=
You have a much bigger issue in this script than the error you are getting. Even after correcting as #ShaunPeterson suggested it will still fail
, it WILL NOT generate an error it will just not run as you expect. The issue is you failed to understand substitution variables - the use of &name (Specifically here &Birthday.) I'll actually use &Birthday in the following but the discussion applies to ANY/ALL substitution variables.
people fail to understand why they can't use the "&" substitution
variables in their PL/SQL procedures and functions to prompt for input
at run time. This article will hopefully help clarify in your mind
what the differences are so that you can understand where and when to
use these.
Substitution Variables The clue here is in the name... "substitution". It relates to values being substituted into the code
before it is submitted to the database. These substitutions are
carried out by the interface being used
The effect of this substitution is the the line containing the substitution variable is physically rewritten by the interface replacing %Birthday. In this case if you don't enter a value or the date 2000-05-19 the statement before and after substitution is
BEFORE: V_Geburtsdatum:=('&Birthday');
AFTER: V_Geburtsdatum:=(''); OR V_Geburtsdatum:=('2000-05-19');
Either way the after is what the compiler sees; it does NOT see %Birthday at all. Moreover when run the trigger will not prompt for a value. As far as the compiler is concerned it is a hard coded value that will never change. Beyond that a trigger, or any other PLSQL script (stored or anonymous) never prompts for values, they are actually incapable of doing so as it is not part of the language. Any prompt is via your interface software not plsql.
I'm going to suggest a way to avoid the trigger altogether. Getting on soap box: Triggers are BAD, they have some usefulness for assigning auto incrementing keys (before 12c),logging, very limited auditing, etc. However, for business rules they should be the option of last resort. Ok Get off soap box.
The first thing is to make the columns meine_Firma.Hiredate and meine_Firma.Geburtsdatum NOT null (if not already). If either are NULL you cannot calculate anything with them, the result would be NULL.
Second create a new column age_at_hire (or whatever) as a virtual column then put a check constraint on it. And voila trigger no longer needed. See fiddle for demo.
So the proposed change (YES you will probably have to clean up the bad data first):
alter table meine_Firma modify
( hiredate not null
, Geburtsdatum not null
) ;
alter table meine_Firma add
( age_at_hire integer generated always as (trunc(months_between(hiredate,Geburtsdatum))) virtual
, constraint check_age_at_hire check (age_at_hire >= 16*12)
);
Anyway, I hope you get an understanding of substitution variables for the future. And learn to avoid triggers. Good luck.
The reason you are getting that specific error is that the below select will select ALL rows from meine_Firma as there is no where clause
SELECT HIREDATE INTO V_Berufstart FROM meine_Firma;
However because you are in a trigger you do not need to select anything you use the :NEW bind variable. So you can just use
V_Berufstart := :NEW.HIREDATE;
If this was an update trigger there would be both a :NEW and :OLD bind variable declared so that you can access the OLD and NEW values. As this is an Insert trigger the :OLD will just be null as there is no old values.

Pass parameter to spool file

Say I have a sql file which is executed from command prompt (SQL Plus), which is working fine.
sqlplus dbusername/dbpassword#DBInstance #sqlfilename.sql
Now I need to pass a parameter to this sql spool file and according to the value of parameter I should be able to determine the functionality in the sql file. In the below sql code, I need to store the value that I am passing as parameter and in the variable parameter_Value
SET SERVEROUTPUT ON
SET DEFINE OFF
SPOOL "Test.log"
var parameter_Value VARCHAR2(20);
BEGIN
IF(parameter_Value='A')
THEN
--do something
ELSE
--do something
END IF;
END;
/
SPOOL OFF;
SET DEFINE ON
SET SERVEROUTPUT OFF
Can anyone please help how to perform this task?
Thanks in advance!
Your usage of the word spoolfile is not correct. sqlfilename.sql is the sql file or sqlplus file. The output file Test.log is the spool file.
you call
sqlplus dbusername/dbpassword#DBInstance #sqlfilename.sql A
to call the following file
SPOOL "Test.log"
DEFINE MY_VALUE=&1
SET SERVEROUTPUT ON
var parameter_Value VARCHAR2(20);
EXEC :parameter_Value:='&MY_VALUE'
SET DEFINE OFF
BEGIN
IF(:parameter_Value='A')
THEN
--do something
ELSE
--do something
END IF;
END;
/
SET DEFINE ON
SET SERVEROUTPUT OFF
SPOOL OFF;
the substitution of the & variables is a simple text substitution. It can be turned off by executing SET DEFINE OFF. The substituion raises information messages by sqlplus that can be turned off by executing SET VERIFY OFF
you should start with the SPOOL command, you won't see errors thrown by commands executed before you start spooling into the logfile. This can make debugging tedious. Also you should execute all commands before SPOOL OFF
the parameters in the command line are referend by their position number. I prefer assigning them to a named variable and use the named variable later. This is not necessary. You can use the positional parameter later, too, instead of using a named parameter. But using named parameter makes it easier to change the file if the position of a parameter changes. Also it can document the purpose of the parameter if you choose an appropriate name. I didn't in this example.
You cannot disable the special property of the & character with SET DEFINE OFF before the parameter substitution. You have to postpone it until you have done all your substitutions. Otherwise the substitution will not work.
a bind variable is definded with the VAR ... statement in sqlplus. In an SQL text it must be referenced with a preceding colon (:).
EXEC statement is an abbreviation for BEGIN statement; END
be aware that the --do something is in a PL/SQL block. So it must be replaced by PL/SQL statements and not by SQL statements or SQL*Plus statements.
So it cannot be replaced by a CREATE TABLE ... statement (this is SQL but not PL/SQL) and it cannot be replaced by a SPOOL OFF statement, which is SQL*Plus but not PL/SQL.

PLSQL: No output displayed when using dynamic query inside Stored Procedure

I have been asked to create an SP which creates temporary table and insert some records.
I am preparing some sample code for the same as mentioned below but the output is not displayed.
create or replace procedure Test
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp_1(id number(10))';
execute immediate stmt;
insert into temp_1(id) values (10);
execute immediate 'Select * from temp_1';
execute immediate 'Drop table temp_1';
commit;
end;
When i am executing the SP by (Exec Test) desired O/P is not displayed.
I am expecting O/P of "Select * from temp_1" to be displayed. But it is not happening. Please suggest where i am doing wrong.
But i am interesting in knowing why ( execute immediate 'Select * from temp_1';) do not yield any result
For two reasons. Firstly because as #a_horse_with_no_name said PL/SQL won't display the result of a query. But more importantly here, perhaps, the query is never actually executed. This behaviour is stated in the documentation:
If dynamic_sql_statement is a SELECT statement, and you omit both into_clause and bulk_collect_into_clause, then *execute_immediate_statement( never executes.
You would have to execute immediate into a variable, or more likely a collection if your real scenario has more than one row, and then process that data - iterating over the collection in the bulk case.
There is not really a reliable way to display anything from PL/SQL; you can use dbms_output but that's more suited for debugging than real output, and you usually have no guarantee that the client will be configured to show whatever you put into its buffer.
This is all rather academic since creating and dropping a GTT on the fly is not a good idea and there are better ways to accomplish whatever it is you're trying to do.
The block you showed shouldn't actually run at all; as you're creating temp_1 dynamically, the static SQL insert into temp_1 will error as that table does not yet exist when the block is compiled. The insert would have to be dynamic too. Any dynamic SQL is a bit of a warning sign you're maybe doing something wrong, though it is sometimes necessary; having to do everything dynamically suggests the whole approach needs a rethink, as does creating objects at runtime.

Increase performance on insert cursor?

I would like to ask you how would you increase the performance on Insert cursor in this code?
I need to use dynamic plsql to fetch data but dont know how to improve the INSERT in best way. like Bulk Insert maybe?
Please let me know with code example if possible.
// This is how i use cur_handle:
cur_HANDLE integer;
cur_HANDLE := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cur_HANDLE, W_STMT, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS2(cur_HANDLE, W_NO_OF_COLS, W_DESC_TAB);
LOOP
-- Fetch a row
IF DBMS_SQL.FETCH_ROWS(cur_HANDLE) > 0 THEN
DBMS_SQL.column_value(cur_HANDLE, 9, cont_ID);
DBMS_SQL.COLUMN_VALUE(cur_HANDLE, 3, proj_NR);
ELSE
EXIT;
END IF;
Insert into w_Contracts values(counter, cont_ID, proj_NR);
counter := counter + 1;
END LOOP;
You should do database actions in sets whenever possible, rather than row-by-row inserts. You don't tell us what CUR_HANDLE is, so I can't really rewrite this, but you should probably do something like:
INSERT INTO w_contracts
SELECT ROWNUM, cont_id, proj_nr
FROM ( ... some table or joined tables or whatever... )
Though if your first value there is a primary key, it would probably be better to assign it from a sequence.
Solution 1) You can populate inside the loop a PL/SQL array and then just after the loop insert the whole array in one step using:
FORALL i in contracts_tab.first .. contracts_tab.last
INSERT INTO w_contracts VALUES contracts_tab(i);
Solution 2) if the v_stmt contains a valid SQL statement you can directly insert data into the table using
EXECUTE IMMEDIATE 'INSERT INTO w_contracts (counter, cont_id, proj_nr)
SELECT rownum, 9, 3 FROM ('||v_stmt||')';
"select statement is assembled from a website, ex if user choose to
include more detailed search then the select statement is changed and
the result looks different in the end. The whole application is a web
site build on dinamic plsql code."
This is a dangerous proposition, because it opens your database to SQL injection. This is the scenario in which Bad People subvert your parameters to expand the data they can retrieve or to escalate privileges. At the very least you need to be using DBMS_ASSERT to validate user input. Find out more.
Of course, if you are allowing users to pass whole SQL strings (you haven't provided any information regarding the construction of W_STMT) then all bets are off. DBMS_ASSERT won't help you there.
Anyway, as you have failed to give the additional information we actually need, please let me spell it out for you:
will the SELECT statement always have the same column names from the same table name, or can the user change those two?
will you always be interested in the third and ninth columns?
how is the W_STMT string assembled? How much control do you have over its projection?

Easiest method to test an Oracle Stored Procedure

I'm working on an ASP.NET project with an Oracle Database. We are using TOAD to add/manage the stored procedures -- and in general I like TOAD and Oracle. The one thing I've found frustrating is finding a simple way to test an Oracle Stored Proc, such as SQL Server's "exec [SP_NAME] Param1, Param2, ParamN" syntax.
All of our stored procedures output Ref Cursors. Here is an example of a Stored Proc:
CREATE OR REPLACE PROCEDURE APP_DB1.GET_JOB
(
p_JOB_ID IN JOB.JOB_ID%type,
outCursor OUT MYGEN.sqlcur
)
IS
BEGIN
OPEN outCursor FOR
SELECT *
FROM JOB
WHERE JOB_ID = p_JOB_ID;
END GET_JOB;
/
Any suggestions?
You just need a script that calls your stored procedure and has a bind variable for the ref cursor output to display it in TOAD's grid in the Editor window.
DECLARE
type result_set is ref cursor;
BEGIN
APP_DB1.GET_JOB(1, :result_set);
END;
When you then run this TOAD will prompt you to 'bind' :result_set, just select ref cursor from the list of types and then the result will display in the grid. The trick is to think of yourself as a 'client' calling your stored procedure and you need your own ref cursor to store the result.
If you just looking for a way to invoke the SP, then the Oracle way is:
begin
sp_name(....);
end;
I don't use Toad, but you should be able to put this into a SQL window and execute it.
In sqplus you can use the syntax
SQL>var rc refcursor
SQL>exec APP_DB1.GET_JOB(the job id you want to query, :rc)
SQL>print rc
That should do it. The first line defines a bind variable. You could also define a variable for the job id, or just type it in.
TOAD shows the result in a grid just fine with sample script from Russel. Run as script.
variable P_CUR refcursor;
exec PACK.GETEXECUTION ( '9f363e49-88c1-4295-b61e-60812d620d7e', '6', :P_CUR );
print P_CUR;
Thanks!
The idea is just to bind the outCursor variable to a cursor when toad prompts you for the variable type. Just pass the other variables the usual way. Like in the example below.
BEGIN
APP_DB1.GET_JOB(1, :v_outCursor);
END;
Run it and a dialogue box will prompt you to bind the :outCursor variable as shown in the following image.
Toad will then display the result in the result grid.

Resources