Can't declare a variable in Oracle SQL(SQL Fiddle) - oracle

When I run declare query, it always gives me errors.
DECLARE
the_variable date;
BEGIN
SELECT MIN("Start Date") INTO the_variable FROM "Employee_Master";
END;
ORA-06550: line 2, column 20: PLS-00103: Encountered the symbol
"end-of-file" when expecting one of the following: := . ( # % ; not
null range with default character.
I am not sure where is the problem.

Works OK for me:
SQL> create table "Employee_Master" ("Start Date" date);
Table created.
SQL> insert into "Employee_Master" values (sysdate);
1 row created.
SQL> declare
2 the_variable date;
3 begin
4 select min("Start Date") into the_variable
5 from "Employee_Master";
6 end;
7 /
PL/SQL procedure successfully completed.
SQL>
Though, why, oh, why are you making your life miserable? Avoid double quotes while working with Oracle. If you do that, you always have to reference tables (and columns) using double quotes and matching letter case exactly as during creation process.
By default, Oracle stores everything as uppercase, but you can reference those objects any way you want (upper, lower, mixed case - doesn't matter). But, with double quotes, as I've said - exact matching is required.
By the way, screenshot you attached shows a lot of nothing and just a little bit of something. Couldn't you have taken a better screenshot?

On the SQL Fiddle website, you need to tell it that you are going to be using PL/SQL and a statement will be ended with a / terminator (and not by a ; semi-colon). To do this you need to click on the "Query Terminator" button (right of the "Run SQL" and "Edit Fullscreen" buttons) and change it from [;] to [/].
Then it will work:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Employee_Master ( Start_Date DATE );
INSERT INTO Employee_Master ( Start_Date ) VALUES ( SYSDATE );
Query 1:
DECLARE
the_variable date;
BEGIN
SELECT MIN(Start_Date) INTO the_variable FROM Employee_Master;
END;
/
Results:

Related

Oracle PL/SQL SELECT INTO clause thinks it needs another INTO

I have a simple test function where I'm passing in a specific ID (the primary key of the table I'm selecting from), and computing a simple function on it and the parameters.
The skeleton code and test:
create or replace function test(id varchar2, area float) return float is
theRow forest%ROWTYPE;
begin
select * into theRow from forest where Forest_No = id;
return area / theRow.Area;
end;
begin
select test('1', 16000) from dual;
end;
The output:
[2019-10-14 21:19:10] [65000][6550] ORA-06550: line 2, column 5:
[2019-10-14 21:19:10] PLS-00428: an INTO clause is expected in this SELECT statement
I am at a loss for what to do here, as far as I can tell the documentation and examples use the same order and syntax. I have tried moving the into clause to the end as in Postgresql, but that did not work.
What have I missed here?
Issue is in calling statement.
Whenever select statement is used in plsql block it must have into clause to assign return value to variable.
You should remove begin and end from your calling code:
--begin -- remove this
select test('1', 16000) from dual;
--end; -- remove this
Or if you want to use it in plsql block then add into clause:
Declare
Area_ float(precision);
begin
select test('1', 16000) into area_ from dual;
-- use area_ in your code wherever required
dbms_output.put_line('area: ' || area_);
end;
Cheers!!

PL/SQL: statement ignored when inserting

I'm trying to create a stored procedure that can only be executed in the month of march based on system date. Here is a snippot of such code:
CREATE PROCEDURE sp_time_window (
p_sales_ID in sales.sales_ID%TYPE,
p_product in sales.product%TYPE,
p_unitCost in sales.unitcost%TYPE,
p_quantity in sales.quantity%TYPE)
IS BEGIN
DECLAR v_date varchar(3)
BEGIN
SELECT TO_CHAR(sysdate,'MM') into v_date from duaL
IF (v_date in ('MAR')) THEN
INSERT INTO sales (sales_ID, product, unitcost, quantity) values
(p_sales_ID, p_ product, p_unitCost, p_quantity);
ENDIF;
END;
END;
/
ORACLE gives me this error:
LINE/COL ERROR
-------- ---------------------------------------------------
14/2 PL/SQL: SQL Statement ignored
16/5 PL/SQL: ORA-00933: SQL command not properly ended
please help
You have a redundant layer of BEGIN/END, and various typos, some of which I think you've just introduced creating the question, like DECLAR instead of DECLARE. But the first thing that might get that error is the missing semicolon after dual.
SELECT TO_CHAR(sysdate,'MM') into v_date from duaL;
You can assign the month value directly to the variable without the select from dual anyway:
v_date := TO_CHAR(sysdate,'MM');
... but as you have that defined as varchar2(3) and later look for MAR, that format mask should be MON not MM. However, using numbers is safer anyway, otherwise you need to be sure your session is in English. Your variable should be the appropriate type.
You also have a spaces in p_ product, and ENDIF instead of END IF, but again those might not be in your real code.
You can reduce what you have to:
CREATE OR REPLACE PROCEDURE sp_time_window (
p_sales_ID in sales.sales_ID%TYPE,
p_product in sales.product%TYPE,
p_unitCost in sales.unitcost%TYPE,
p_quantity in sales.quantity%TYPE)
AS
BEGIN
IF extract(month from sysdate) = 3 THEN
INSERT INTO sales (sales_ID, product, unitcost, quantity)
VALUES (p_sales_ID, p_product, p_unitCost, p_quantity);
END IF;
END;
/
The extract(month from sysdate) gets the month number from the date, so you can check if that is 3, as a number not a string. You also don't need the local v_date variable if you do that extraction in the IF clause.
Silently ignoring the insert seems odd, as the caller will no have no idea if that happens. And this won't stop inserts outside the procedure. In this case it might be appropriate to use a trigger that raises an exception if the month number is not 3.

Why EXECUTE IMMEDIATE is needed here?

I am a SQL Server user and I have a small project to do using Oracle, so I’m trying to understand some of the particularities of Oracle and I reckon that I need some help to better understand the following situation:
I want to test if a temporary table exists before creating it so I had this code here:
DECLARE
table_count INTEGER;
var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
hello varchar(1000) NOT NULL)';
BEGIN
SELECT COUNT(*) INTO table_count FROM all_tables WHERE table_name = 'TEST';
IF table_count = 0 THEN
EXECUTE IMMEDIATE var_sql;
END IF;
END;
It works normally, so after I executed it once, I added an else statement on my IF:
ELSE
insert into test (hello) values ('hi');
Executed it again and a line was added to my test table.
Ok, my code was ready and working, so I dropped the temp table and tried to run the entire statement again, however when I do that I get the following error:
ORA-06550: line 11, column 19:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 11, column 7:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Then I changed my else statement to this and now it works again:
ELSE
EXECUTE IMMEDIATE 'insert into test (hello) values (''hi'')';
My question is why running individually I can simply use the insert instead of the EXECUTE IMMEDIATE and also why my SELECT statement right after BEGIN still works when all the rest appears to need EXECUTE IMMEDIATE to run properly?
The whole PL/SQL block is parsed at compile time, but the text within a dynamic statement isn't evaluated until runtime. (They're close to the same thing for an anonymous block, but still distinct steps).
Your if/else isn't evaluated until runtime either. The compiler doesn't know that the table will always exist by the time you do your insert, it can only check whether or not it exists at the point it parses the whole block.
If the table does already exist then it's OK; the compiler can see it, the block executes, your select gets 1, and you go into the else to do the insert. But if it does not exist then the parsing of the insert correctly fails with ORA-00942 at compile time and nothing in the block is executed.
Since the table creation is dynamic, all references to the table have to be dynamic too - your insert as you've seen, but also if you then query it. Basically it makes your code much harder to read and can hide syntax errors - since the dynamic code isn't parsed until run-time, and it's possible you could have a mistake in a dynamic statement in a branch that isn't hit for a long time.
Global temporary tables should not be created on-the-fly anyway. They are permanent objects with temporary data, specific to each session, and should not be created/dropped as part of your application code. (No schema changes should be made by your application generally; they should be confined to upgrade/maintenance changes and be controlled, to avoid errors, data loss and unexpected side effects; GTTs are no different).
Unlike temporary tables in some other relational databases, when you create a temporary table in an Oracle database, you create a static table definition. The temporary table is a persistent object described in the data dictionary, but appears empty until your session inserts data into the table. You create a temporary table for the database itself, not for every PL/SQL stored procedure.
Create the GTT once and make all your PL/SQL code static. If you want something closer to SQL Server's local temporary tables then look into PL/SQL collections.
PL/SQL: ORA-00942: table or view does not exist
It is compile time error, i.e. when the static SQL is parsed before even the GTT is created.
Let's see the difference between compile time and run time error:
Static SQL:
SQL> DECLARE
2 v number;
3 BEGIN
4 select empno into v from a;
5 end;
6 /
select empno into v from a;
*
ERROR at line 4:
ORA-06550: line 4, column 26:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 4, column 1:
PL/SQL: SQL Statement ignored
Dynamic SQL:
SQL> DECLARE
2 v number;
3 BEGIN
4 execute immediate 'select empno from a' into v;
5 end;
6 /
DECLARE
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at line 4
In the 1st PL/SQL block, there was a semantic check at compile time, and you could see the PL/SQL: ORA-00942: table or view does not exist. In the 2nd PL/SQL block, you do not see the PL/SQL error.
Bottomline,
At compile time it is not known if the table exists, as it is
only created at run time.
In your case, to avoid this behaviour, you need to make the INSERT also dynamic and use EXECUTE IMMEDIATE. In that way, you can escape the compile time error and get the table created dynamically and also do an insert into it dynamically at run time.
Having said that, the basic problem is that you are trying to create GTT on the fly which is not a good idea. You should create it once, and use it the way you want.
I have modified your code a litle bit and it works as far as logic is concerned. But as exp[lained in earlier posts creating GTT on the fly at run time is not at all is a goood idea.
--- Firstly by dropping the table i.e NO TABLE EXISTS in the DB in AVROY
SET serveroutput ON;
DECLARE
table_count INTEGER;
var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
hello varchar(1000) NOT NULL)';
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE AVROY.TEST'; --Added the line just to drop the table as per your comments
SELECT COUNT(*)
INTO table_count
FROM all_tables
WHERE table_name = 'TEST'
AND OWNER = 'AVROY';
IF table_count = 0 THEN
EXECUTE IMMEDIATE var_sql;
dbms_output.put_line('table created');
ELSE
INSERT INTO AVROY.test
(hello
) VALUES
('hi'
);
END IF;
END;
--------------------OUTPUT-----------------------------------------------
anonymous block completed
table created
SELECT COUNT(*)
-- INTO table_count
FROM all_tables
WHERE table_name = 'TEST'
AND OWNER = 'AVROY';
COUNT(*)
------
1
--------
-- Second option is without DROPPING TABLE
SET serveroutput ON;
DECLARE
table_count INTEGER;
var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
hello varchar(1000) NOT NULL)';
BEGIN
--EXECUTE IMMEDIATE 'DROP TABLE AVROY.TEST';
SELECT COUNT(*)
INTO table_count
FROM all_tables
WHERE table_name = 'TEST'
AND OWNER = 'AVROY';
IF table_count = 0 THEN
EXECUTE IMMEDIATE var_sql;
dbms_output.put_line('table created');
ELSE
INSERT INTO AVROY.test
(hello
) VALUES
('hi'
);
dbms_output.put_line(SQL%ROWCOUNT||' Rows inserted into the table');
END IF;
END;
-------------------------------OUTPUT-------------------------------------
anonymous block completed
1 Rows inserted into the table
---------------------------------------------------------------------------

Writing Oracle Stored Procedure

I haven't written many stored procedures with oracle. I read through some tutotorials (for example: http://plsql-tutorial.com/plsql-procedures.htm) and tried to model my sp after what I saw, but I am still encountering an error. Here's is a small sample procedure and error:
create or replace
PROCEDURE TEST_SP()
BEGIN
insert into tablespace.tablename
select * from testtable;
END TEST_SP;
PLS-00103: Encountered the symbol ")" when expecting one of the following:
<an identifier> <a double-quoted delimited-identifier>
I get the impression that I am missing the declaration section, but I do not understand what I am supposed to be declaring :-/
Any help would be appreciated.
Followed Justin's advice from first response, now getting different error:
create or replace
PROCEDURE TEST_SP
AS
BEGIN
insert into tablespace.tablename (col1, col2)
select (col1, col2) from testtable;
END TEST_SP;
PLS-00103: Encountered the symbol "AS" when expecting one of the following:
. , # in <an identifier> <a double-quoted delimited-identifier> partition subpartition
It sounds like you're after something like this. You don't want to have parenthesis after the name of the procedure if you are not declaring any parameters. And you need the keyword AS (or IS) before your BEGIN even if you're not going to declare any local variables.
create or replace PROCEDURE TEST_SP
AS
BEGIN
insert into tablespace.tablename
select * from testtable;
END TEST_SP;
Generally, however, it's a bad idea to write code like this that omits the list of columns. That assumes that the two tables have exactly the same columns defined in exactly the same order so if someone decides to add another column to one of the tables, your code will break. It also creates the possibility that you're inadvertently copying data from the wrong column. It is generally more robust to write something like
create or replace PROCEDURE TEST_SP
AS
BEGIN
insert into tablespace.tablename( <<list of columns>> )
select <<list of columns>>
from testtable;
END TEST_SP;
As an example
SQL> create table foo( col1 number );
Table created.
SQL> create table foo_cpy( col1 number );
Table created.
SQL> ed
Wrote file afiedt.buf
1 create or replace procedure test_sp
2 as
3 begin
4 insert into foo( col1 )
5 select col1
6 from foo_cpy;
7* end test_sp;
SQL> /
Procedure created.

Oracle PL/SQL: Forwarding whole row to procedure from a trigger

In have an Oracle (10i) PL/SQL Row-Level trigger which is responsible for three independent tasks. As the trigger is relatively cluttered that way, I want to export these three tasks into three stored procedures.
I was thinking of using a my_table%ROWTYPE parameter or maybe a collection type for the procedures, but my main concern is how to fill these parameters.
Is there a way to put the whole :NEW row of a trigger into a single variable easily?
So far the only way I could find out was assigning each field separately to the variable which is not quite satisfying, looking at code maintenance etc.
Something like
SELECT :NEW.* INTO <variable> FROM dual;
would be preferred. (I haven't tried that actually but I suppose it wouldn't work)
In the vast majority of cases, the only way to assign the new values in the row to a %ROWTYPE variable would be to explicitly assign each column. Something like
CREATE OR REPLACE TRIGGER some_trigger_name
BEFORE INSERT OR UPDATE ON some_table
FOR EACH ROW
DECLARE
l_row some_table%rowtype;
BEGIN
l_row.column1 := :NEW.column1;
l_row.column2 := :NEW.column2;
...
l_row.columnN := :NEW.columnN;
procedure1( l_row );
procedure2( l_row );
procedure3( l_row );
END;
If your table happens to be declared based on an object, :NEW will be an object of that type. So if you have a table like
CREATE OR REPLACE TYPE obj_foo
AS OBJECT (
column1 NUMBER,
column2 NUMBER,
...
columnN NUMBER );
CREATE TABLE foo OF obj_foo;
then you could declare procedures that accept input parameters of type OBJ_FOO and call those directly from your trigger.
The suggestion in the other thread about selecting the row from the table in an AFTER INSERT/ UPDATE thread, unfortunately, does not generally work. That will generally lead to a mutating table exception.
1 create table foo (
2 col1 number,
3 col2 number
4* )
SQL> /
Table created.
SQL> create procedure foo_proc( p_foo in foo%rowtype )
2 as
3 begin
4 dbms_output.put_line( 'In foo_proc' );
5 end;
6 /
Procedure created.
SQL> create or replace trigger trg_foo
2 after insert or update on foo
3 for each row
4 declare
5 l_row foo%rowtype;
6 begin
7 select *
8 into l_row
9 from foo
10 where col1 = :new.col1;
11 foo_proc( l_row );
12 end;
13 /
Trigger created.
SQL> insert into foo values( 1, 2 );
insert into foo values( 1, 2 )
*
ERROR at line 1:
ORA-04091: table SCOTT.FOO is mutating, trigger/function may not see it
ORA-06512: at "SCOTT.TRG_FOO", line 4
ORA-04088: error during execution of trigger 'SCOTT.TRG_FOO'
It's not possible that way.
Maybe my answer to another question can help.
Use SQL to generate the SQL;
select ' row_field.'||COLUMN_NAME||' := :new.'||COLUMN_NAME||';' from
ALL_TAB_COLUMNS cols
where
cols.TABLE_NAME = 'yourTableName'
order by cols.column_name
Then copy and paste output.
This is similar to Justins solution but a little bit shorter (no typing of left part of each assignment) :
-- use instead of the assignments in Justins example:
select :new.column1,
:new.column2,
...
:new.columnN,
into l_row from dual;

Resources