PL/SQL Not Exists Query - sql

I need help to solve this issue. The Driver_ID exists in two tables, Driver and Driver_Deliveries. I need it to display the First_Name, Surname & Driver_ID for the Driver ID's that do not appear in the Driver_Deliveries table. I'm using Oracle SQL Developer with Oracle 11G.
DECLARE
FIRSTNAME VARCHAR2(20);
SUR_NAME VARCHAR2(20);
DRIVERID VARCHAR2(5);
BEGIN
FOR i IN
(
SELECT
FIRST_NAME,
SURNAME,
a.DRIVER_ID
INTO
FIRSTNAME, SUR_NAME, DRIVERID
FROM
DRIVER_DELIVERIES,
DRIVER a,
WHERE NOT EXISTS(SELECT * FROM DRIVER_DELIVERIES WHERE DRIVER_DELIVERIES.DRIVER_ID = a.DRIVER_ID);
)
LOOP
DBMS_OUTPUT.PUT_LINE('FIRST NAME :' ||I.FIRST_NAME);
DBMS_OUTPUT.PUT_LINE('SURNAME :' || I.SURNAME);
DBMS_OUTPUT.PUT_LINE('DELIVERY JOB REQUIRED: YES ');
END LOOP;
END;
Here's the error stack:
Error report -
ORA-06550: line 23, column 9:
PL/SQL: ORA-00903: invalid table name
ORA-06550: line 10, column 5:
PL/SQL: SQL Statement ignored
ORA-06550: line 23, column 106:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
loop
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:

You need to select into something with a simple query in a PL/SQL block, but you've taken that rule too far; because your cursor loop variable i is an implicit record type that is already available to take the query results.
You also have an extra comma in your cursor query's select list, and the from clause. And an extra semicolon after the where clause.
So with that loop syntax you need:
...
FOR i IN
(
SELECT
FIRST_NAME,
SURNAME,
a.DRIVER_ID
FROM
DRIVER_DELIVERIES,
DRIVER a
WHERE
...
Though you should really use explicit join syntax, which would make it more obvious that you currently have no link between the two tables in your where clause. But you don't actually want to refer to driver_deliveries there at all - you're getting all the data from the driver table. You only need to refer to that within the exists clause. (And I've just noticed you've spelled the table name incorrectly in that clause too).
So modifying that further:
need:
...
FOR i IN
(
SELECT
FIRST_NAME,
SURNAME,
DRIVER_ID
FROM
DRIVER a
WHERE NOT EXISTS(
SELECT null FROM DRIVER_DELIVERIES
WHERE DRIVER_DELIVERIES.DRIVER_ID = a.DRIVER_ID)
)
LOOP
...
You don't appear to need PL/SQL for this, but if you have to...

Related

Oracle SQL - How to define a date bind variable based on a select statement

Using Oracle SQL, I have several SQL Queries with manual bind variables that are used to create a table as follows:
What I would like to do is to change the define anfang to (select c_year_beginning from master_date_automation).
I have tried several combinations of bind variables but could not make it work (edited with DEL comments):
begin
execute immediate 'DROP TABLE A_TEST_TABLE'
end;
/
begin
execute immediate 'create table A_TEST_TABLE (
user varchar2(100),
product varchar2(100),
datum date)';
end;
/
BEGIN
DECLARE
v_c_year_ytd DATE;
BEGIN
SELECT c_year_ytd Into v_c_year_ytd FROM master_date_automation;
BEGIN
SELECT usr.datum || ','
|| usr.user || ','
|| usr.product
INTO A_TEST_TABLE
FROM users_table usr
WHERE usr.datum = v_c_year_ytd
AND kto.kontonummer = '00510199065';
END;
END;
END;
ERROR message (edited after Del comment):
Error report -
ORA-06550: line 12, column 6:
PLS-00403: expression 'A_TEST_TABLE' cannot be used as an INTO-target of a SELECT/FETCH statement
ORA-06550: line 12, column 21:
PL/SQL: ORA-00904: : invalid identifier
ORA-06550: line 9, column 1:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Would be really thankful if someone could help me out.
Thousand thanks in advance!
So, to sum up my comments from above, you don't need PL/SQL to do this. You could do all of your script with straight SQL:
CREATE TABLE A_TEST_TABLE
(
user varchar2(100),
product varchar2(100),
datum date
);
INSERT INTO a_test_table
SELECT usr.user,
usr.product,
usr.datum
FROM users_table
WHERE usr.datum = (SELECT c_year_ytd FROM master_date_automation)
AND usr.user= '123456789'
Drop the table if you need to first.
You don't need to use dynamic SQL inside a PL/SQL block to create the tables; you can do it using SQL statements:
DROP TABLE A_TEST_TABLE;
CREATE TABLE A_TEST_TABLE (
user_name varchar2(100),
product varchar2(100),
datum date
);
Note: USER is a reserved word and you CANNOT use it as an unquoted identifier; you will need to find another identifier such as user_name or you will have to use a quoted identifer and refer to it as "USER" everywhere it is used (however, that is bad practice).
Then if you want to use PL/SQL you can use:
DECLARE
v_c_year_ytd DATE;
BEGIN
SELECT c_year_ytd
Into v_c_year_ytd
FROM master_date_automation;
INSERT INTO a_test_table (datum, user_name, product)
SELECT datum,
user_name
product
FROM users_table
WHERE datum = v_c_year_ytd
AND kontonummer = '00510199065';
END;
/
But it would be much simpler to just use one SQL statement:
INSERT INTO a_test_table (datum, user_name, product)
SELECT datum,
user_name
product
FROM users_table
WHERE datum = (SELECT c_year_ytd FROM master_date_automation)
AND kontonummer = '00510199065';

hierarchical query task with pl/sql

It's my first question.I have such task in school to do.We have table with columns NAME,PARENT,MONEY,CITY.The task is to produce output names,money and average money of descendants for whom it is true, that the average money
of the descendants is greater than the person's money.
I write this code but can't understand error...
CREATE OR REPLACE PROCEDURE rich_avg_descendant IS
cnt INTEGER;
BEGIN
FOR rec IN (SELECT name, money,AVG(money) FROM ourtable) loop
SELECT count(*) INTO cnt FROM ourtable
GROUP BY name
HAVING AVG(money)> rec.money
START WITH name = rec.name CONNECT BY PRIOR name = parent;
IF cnt > 0 THEN dbms_output.put_line(name,money,AVG(money)); END IF;
END loop;
END;
/
Error is in START WITH clause that is START underlined.
Error starting at line : 14 in command -
BEGIN rich_avg_descendant(); END;
Error report -
ORA-06550: line 1, column 7:
PLS-00905: object myschoolcode.RICH_AVG_DESCENDANT is invalid
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Can you please tell my mistake in this code?
Thanks in advance.
the connect by clause should be preceding the start with clause

Error report: ORA-06550: line 7, column 134: PL/SQL: ORA-00913: too many values ORA-06550: line 7,

> set serveroutput on
> set autoprint on;
> declare
> v_first_name employees.first_name%type;
> v_street_address locations.street_address%type;
> v_city locations.city%type;
> v_postal_code locations.postal_code%type;
> begin
> select employee_id first_name,street_address,city,postal_code into:b_employee_id,v_first_name,v_street_address,v_city,v_postal_code
>
> from employees natural join locations
> where employee_id=156; // how to get employee_id stored in b_employee_ud
> dbms_output.put_line('the employee'||v_first_name ||' is located at:'||v_street_address|| v_city ||v_postal_code );
> end;
> /
getting error
Error report:
ORA-06550: line 7, column 134:
PL/SQL: ORA-00913: too many values
ORA-06550: line 7, column 1:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
b_employee_id
156
i want to use employee_id which is stored in b_employee_id
First of all, you're missing a comma after employee_id in your SELECT clause.
select employee_id first_name,street_address,city,postal_code
into :b_employee_id,v_first_name,v_street_address,v_city,v_postal_code
should be
select employee_id, first_name,street_address,city,postal_code
into :b_employee_id,v_first_name,v_street_address,v_city,v_postal_code
Now, coming back to the part where you want to use employee_id = 156
i want to use employee_id which is stored in b_employee_id
If your intent is not hard coding the employee_id for which you want to run your query (I am guessing this by reading the commented line at the end of your WHERE clause), then you need to use substitution variable in the WHERE clause, something like this:
WHERE employee_id = :b_emp_id
Another assumption here will be there, there is one record for one employee in the table from where you are trying to retrieve the records. You should not use a substitution variable in the variables of INTO clause
In case you want to override the value being returned in the INTO clause to some other value for any reason, you can use another variable later on in your program to do that.

OracleEE 11g WITH clause causing error procedure won't compile

This is my first run through with PL SQL so I might be making some sort of silly error.
I am trying to write a procedure in Oracle Express Edition 11g. I am running into an error that has to do with my WITH clause in the procedure body.
Whenever I try and run it I see two errors.
Error report:
ORA-06550: line 14, column 50:
PL/SQL: ORA-00918: column ambiguously defined
ORA-06550: line 12, column 7:
PL/SQL: SQL Statement ignored
ORA-06550: line 29, column 3:
PLS-00306: wrong number or types of arguments in call to 'FIND_HIGH_AVG'
ORA-06550: line 29, column 3:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Code for the procedure is found below.
DECLARE
myTerm courses.term%type;
myLine courses.lineno%type;
procedure find_high_avg (term IN courses.term%type,
line IN courses.lineno%type,
s_fname OUT students.fname%type,
s_lname OUT students.lname%type,
s_sid OUT students.side%type,
s_avg OUT number) is
begin
WITH grades as (select * from components co --line 12 here
join scores sc on co.term = sc.term and co.lineno = sc.lineno and CO.COMPNAME = SC.COMPNAME
where sc.lineno = line and sc.term = term) --line 14 here
select *
into s_fname, s_lname, s_sid, s_avg
from (
select s.fname, s.lname, s.sid, round(sum(points/maxpoints * weight),0) as AV
from grades, students
join students s on grades.sid = s.sid
group by s.sid, s.fname, s.lname
order by AV)
where rownum = 1;
end;
BEGIN
myTerm:='F12';
myLine:='1031';
find_high_avg(myTerm, myLine); --line 29 here
END;
The error at line 10 occurs because parameters to stored procedures do not take a length. The procedure declaration should be something like
procedure find_high_avg (term IN courses.term%type,
line IN courses.lineno%type,
s_fname OUT students.fname%type,
s_lname OUT students.lname%type,
s_sid OUT students.side%type,
average OUT number)
is
The error at line 14 is likely because the parameters to your procedure have the same name as columns in your table. In a SQL statement's scope resolution rules, column names take precedence over local PL/SQL variables. So when you code something like
sc.term = term
Oracle tries to resolve the unqualified TERM using a column in one of the tables. If both tables have a column named TERM, that generates an ambiguous column reference-- Oracle doesn't know which of the two tables to use. Of course, in reality, you don't want it to use the column from either table, you want it to use the parameter. The most common approach to this problem is to add a prefix to your parameter names to ensure that they do not collide with the column names. Something like
procedure find_high_avg (p_term IN courses.term%type,
p_line IN courses.lineno%type,
s_fname OUT students.fname%type,
s_lname OUT students.lname%type,
s_sid OUT students.side%type,
p_average OUT number)
is
The error on line 29 occurs because the procedure takes 6 parameters-- 2 IN and 4 OUT. In order to call it, therefore, you would need to use 6 parameters. Something like
DECLARE
myTerm courses.term%type;
myLine courses.lineno%type;
l_fname students.fname%type;
l_lname students.lname%type;
l_sid students.side%type;
l_avg number;
BEGIN
myTerm:='F12';
myLine:='1031';
find_high_avg(myTerm, myLine,l_fname,l_lname, l_sid, l_avg);
END;
I think it should be end find_high_avg; instead of end; after the select statement.

Using variables in PLSQL SELECT statement

I have a query that queries on ReportStartDate and ReportEndDate so I thought I would use variables in PLSQL. Not sure what I am missing here, but I get an error:
CLEAR;
DECLARE
varReportStartDate Date := to_date('05/01/2010', 'mm/dd/yyyy');
varReportEndDate Date := to_date('05/31/2010', 'mm/dd/yyyy');
BEGIN
SELECT
'Value TYPE',
1 AS CountType1,
2 AS CountType2,
3 AS CountType3
FROM DUAL;
SELECT COUNT (*)
FROM CDR.MSRS_E_INADVCH
WHERE 1=1
AND ReportStartDate = varReportStartDate
AND ReportEndDate = varReportEndDate
;
END;
/
The Error is:
Error starting at line 2 in command:
Error report:
ORA-06550: line 6, column 5:
PLS-00428: an INTO clause is expected in this SELECT statement
ORA-06550: line 8, column 5:
PLS-00428: an INTO clause is expected in this SELECT statement
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
This happens in Toad as well as in SQL Developer.
What is the proper way of using the variables in my WHERE clause?
You cannot use SQL statements directly in a PL/SQL block ( unless you use EXECUTE IMMEDIATE). The columns will need to be fetched into variables ( which is what PL/SQL is telling you with PLS-00428: an INTO clause is expected in this SELECT statement error). So you'll have to rewrite your statements as below.
SELECT
'Value TYPE',
1 AS CountType1,
2 AS CountType2,
3 AS CountType3
INTO
V_VALUE_TYPE,
V_CountType1,
V_CountType2,
V_CountType3
FROM DUAL;
SELECT COUNT(*)
INTO V_COUNT
FROM CDR.MSRS_E_INADVCH
WHERE 1=1
AND ReportStartDate = varReportStartDate
AND ReportEndDate = varReportEndDate
Be sure to add Exception Handlers, since PL/SQL expects only 1 row to be returned. If the statement returns no rows, you'll hit a NO_DATA_FOUND exception - and if the statement fetches too many rows, you'll hit a TOO_MANY_ROWS exception.
The question you have to answer is what do you want to do with the data that has been selected?
Sathya gave you one approach - declare variables in your PL/SQL block and select the columns INTO those variables. Note that this requires that the SELECT statement returns exactly one row - any more or less rows will throw an error. Another way is to declare collection types using the BULK COLLECT option: http://oracletoday.blogspot.com/2005/11/bulk-collect_15.html
Yet another option is to have the procedure return a cursor. This is useful in the case where the calling code expects to be able to fetch the data that the procedure has selected:
PROCEDURE GET_MY_REPORT( varReportStartDate in date, varReportEndDate in date, cur out sys_refcursor) is
begin
OPEN cur FOR SELECT *
FROM CDR.MSRS_E_INADVCH
WHERE 1=1
AND ReportStartDate = varReportStartDate
AND ReportEndDate = varReportEndDate;
END GET_MY_REPORT;