So I've got a nested query that works fine by itself but results in an error message when used in a PL/SQL block: (ORA-00979: not a GROUP BY expression) when running it in SQL developer.
When I format the code in SQL developer (such that ‘substr’ is on a different line from its parameters), or run the same code on Oracle Live SQL, it works fine.
Note: I know that this nested query can be simplified and broken down into several queries. I'm just curious (1) why the nested query runs fine by itself but causes an error in a block (2) the error only exists in SQL developer in the current format, but disappears when formatted, or run on oracle live SQL.
The PL/SQL block that causes an error in SQL developer:
declare
V_CUSTOMER_NUMBER INTEGER;
begin
select max(numbers_of_customer) into V_CUSTOMER_NUMBER
from (select substr(phone, 0, 3) as areacode, count(*) as numbers_of_customer from customer group by substr(phone, 0, 3));
DBMS_OUTPUT.PUT_LINE(V_CUSTOMER_NUMBER);
end;
The error message:
Error starting at line : 1 in command - declare
V_CUSTOMER_NUMBER INTEGER; begin
select max(numbers_of_customer) into V_CUSTOMER_NUMBER
from (select substr(phone, 0, 3) as areacode, count(*) as numbers_of_customer from customer group by substr(phone, 0, 3));
DBMS_OUTPUT.PUT_LINE(V_CUSTOMER_NUMBER); end; Error report - ORA-00979: not a GROUP BY expression ORA-06512: at line 4
00979. 00000 - "not a GROUP BY expression"
*Cause:
*Action:
And the error disappears when formatted:
DECLARE
v_customer_number INTEGER;
BEGIN
SELECT
MAX(numbers_of_customer)
INTO v_customer_number
FROM
(
SELECT
substr(
phone, 0, 3
) AS areacode,
COUNT(*) AS numbers_of_customer
FROM
customer
GROUP BY
substr(
phone, 0, 3
)
);
dbms_output.put_line(v_customer_number);
END;
And the nested query works fine on its own.
select max(numbers_of_customer)
from (select substr(phone, 0, 3) as areacode, count(*) as numbers_of_customer from customer group by substr(phone, 0, 3));
Thank you!
Edit: below are the table definition, in case you want to try on your end.
create table Customer(
CustomerID varchar2(10) ,
CompanyName varchar2(40) ,
CustFirstName varchar2(15) ,
CustLastName varchar2(20) ,
CustTitle varchar2(5) ,
Address varchar2(40) ,
City varchar2(20) ,
State varchar2(2) ,
PostalCode varchar2(10) ,
Phone varchar2(12) ,
Fax varchar2(12) ,
EmailAddr varchar2(50)
);
Related
In MYTABLE there are courses and their predecessor courses.
What I am trying to is to find the courses to be taken after the specified course. I am getting missing SELECT keyword error. Why I am getting this error although I have SELECT statement in FOR statement ? Where am I doing wrong ?
DECLARE
coursename varchar2(200) := 'COURSE_101';
str varchar2(200);
BEGIN
WITH DATA AS
(select (select course_name
from MYTABLE
WHERE predecessors like ('''%' || coursename||'%''')
) str
from dual
)
FOR cursor1 IN (SELECT str FROM DATA)
LOOP
DBMS_OUTPUT.PUT_LINE(cursor1);
END LOOP;
end;
Unless I'm wrong, WITH factoring clause can't be used that way; you'll have to use it as an inline view, such as this:
declare
coursename varchar2(200) := 'COURSE_101';
str varchar2(200);
begin
for cursor1 in (select str
from (select (select course_name
from mytable
where predecessors like '''%' || coursename||'%'''
) str
from dual
)
)
loop
dbms_output.put_line(cursor1.str);
end loop;
end;
/
Apart from the fact that it doesn't work (wrong LIKE condition), you OVERcomplicated it. This is how it, actually, does something:
SQL> create table mytable(course_name varchar2(20),
2 predecessors varchar2(20));
Table created.
SQL> insert into mytable values ('COURSE_101', 'COURSE_101');
1 row created.
SQL>
SQL> declare
2 coursename varchar2(20) := 'COURSE_101';
3 begin
4 for cursor1 in (select course_name str
5 from mytable
6 where predecessors like '%' || coursename || '%'
7 )
8 loop
9 dbms_output.put_line(cursor1.str);
10 end loop;
11 end;
12 /
COURSE_101
PL/SQL procedure successfully completed.
SQL>
Also, is that WHERE clause correct? PREDECESSORS LIKE COURSENAME? I'm not saying that it is wrong, just looks somewhat strange.
To extend #Littlefoot's answer a bit: you can use a common table expression (WITH clause) in your cursor, but the WITH must be part of the cursor SELECT statement, not separate from it:
DECLARE
coursename varchar2(200) := 'COURSE_101';
BEGIN
FOR aRow IN (WITH DATA AS (select course_name AS str
from MYTABLE
WHERE predecessors like '''%' || coursename||'%''')
SELECT str FROM DATA)
LOOP
DBMS_OUTPUT.PUT_LINE(aRow.str);
END LOOP;
END;
Also note that the iteration variable in a cursor FOR-loop represents a row returned by the cursor's SELECT statement, so if you want to display whatever was returned by the cursor you must use dotted-variable notation (e.g. aRow.str) to extract fields from the row.
Best of luck.
CREATE TABLE product
(
PRODUCT_ID int Primary key,
NAME VARCHAR (20) not null,
Batchno int not null,
Rate int not null,
Tax int not null,
Expiredate date not null
);
INSERT INTO PRODUCT VALUSES(1 , 'vasocare', 32 , 15 , 2 , 01-JAN-2021);
I have declared a number table like:
v_areas_hijas dbms_sql.number_table;
I fill the table and then I'm trying this comparation:
select
idarea
from areas
where
IDAREAPADRE in v_areas_hijas;
but I'm getting:
Error(45,22): PLS-00382: expression is of wrong type
How should I do that IN statement?
EDIT 1: using "member of"
select
idarea
from areas
where
IDAREAPADRE member of v_areas_hijas;
I've got this two errors:
Error(45,29): PLS-00382:expression is of wrong type
Error(45,29): PL/SQL: ORA-00932: inconsistent datatypes expected UDT got CHAR
in the table AREAS, IDAREA and IDAREAPADRE are NUMBER,
As in your other question, v_areas_hijas is not a table. If it's a collection type defined in SQL you can use the member of collection operator:
select idarea bulk collect into v_areas_hijas_tmp
from areas
where idareapadre member of v_areas_hijas;
For example, using the predefined type sys.dbms_debug_vc2coll (look inall_coll_types for more examples),
declare
t_demo sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll('X','Y','Z');
l_result integer;
begin
select count(*) into l_result
from dual
where dummy member of t_demo;
-- Or this:
select count(*) into l_result
from dual
where dummy in
( select column_value from table(t_demo) );
-- Or this:
select count(*) into l_result
from dual d
join table(t_demo) t on t.column_value = d.dummy;
dbms_output.put_line(l_result);
end;
You can use the table() operator on nested table and varray types declared in packages:
create or replace package demo
as
type demo_collection_type is table of varchar2(1);
end demo;
then
declare
t_demo demo.demo_collection_type := demo.demo_collection_type('X','Y','Z');
l_result integer;
begin
select count(*) into l_result
from dual
where dummy in
( select column_value from table(t_demo) t );
dbms_output.put_line(l_result);
-- Or:
select count(*) into l_result
from dual d
join table(t_demo) t on t.column_value = d.dummy;
dbms_output.put_line(l_result);
end;
As of 12.1 you can't use member of for collection types declared in PL/SQL packages.
Some of these restrictions are lifted in 12.2.
I have a procedure which receives as parameter a where clause (i.e. where col1 = 1). I am using this clause to search in some tables using an EXECUTE IMMEDIATE statement and the result to be inserted into a nested table, and than be displayed.
The procedure works fine if any data is found but in case no data is found, then the above error is thrown.
Can someone explain what cause this error, please?
Here is the procedure:
create or replace procedure prc_checks(pi_where varchar2) as
cursor c_tables is
select object_name,
case object_name
when 'XP_IMPORT_MW' THEN 99999999
when 'XP_IMPORT_MW_ARCH' THEN 99999998
else TO_NUMBER(SUBSTR(object_name, -8, 8))
end to_order
from dba_objects
where object_type = 'TABLE'
and object_name IN ('XP_IMPORT_MW', 'XP_IMPORT_MW_ARCH')
or REGEXP_LIKE (object_name, 'XP_IMPORT_MW_ARCH_201(5|6|7)[0-9]{4}') order by 2 desc;
type t_result is table of xp_import_mw%rowtype;
v_result t_result;
v_sql varchar2(300);
BEGIN
for i in c_tables
loop
v_sql := 'select * from ' || i.object_name || ' ' || pi_where;
execute immediate v_sql bulk collect into v_result;
if v_result.count > 0
then
for j in v_result.first .. v_result.last
loop
dbms_output.put_line(v_result(j).art_nr);
end loop;
dbms_output.put_line('... the required information was found on table name ' || upper(i.object_name));
exit;
end if;
end loop;
END prc_checks;
You'll get this is one of the tables being found by the cursor has fewer columns than xp_import_mw. For example:
create table xp_import_mw (col1 number, art_nr number, dummy number);
create table xp_import_mw_arch_20160102 (col1 number, art_nr number, dummy number);
create table xp_import_mw_arch_20160101 (col1 number, art_nr number);
insert into xp_import_mw_arch_20160101 values (1, 42);
So the main xp_import_mw table has three columns but no matching data. One of the old archive tables has one fewer columns.
I added a dbms_output.put_line(v_sql) to the procedure to see which table it fails against, then ran it:
set serveroutput on
exec prc_checks('where col1 = 1');
which got output:
select * from XP_IMPORT_MW where col1 = 1
select * from XP_IMPORT_MW_ARCH_20160102 where col1 = 1
select * from XP_IMPORT_MW_ARCH_20160101 where col1 = 1
Error starting at line : 49 in command -
BEGIN prc_checks('where col1 = 1'); END;
Error report -
ORA-01007: variable not in select list
ORA-06512: at "MY_SCHEMA.PRC_CHECKS", line 25
ORA-06512: at line 1
01007. 00000 - "variable not in select list"
*Cause:
*Action:
So the problem isn't that there is no data found; the problem is that there is matching data in a table which has the wrong structure.
You could construct the select list based on the xp_import_mw table's structure, instead of using *; that won't stop it failing, but would at least give you a slightly more helpful error message - in this case ORA-00904: "DUMMY": invalid identifier instead of ORA-01007.
You could do a quick and crude check for discrepancies with something like:
select table_name, count(column_id) as column_count,
listagg(column_name, ',') within group (order by column_id) as columns
from dba_tab_columns
where table_name IN ('XP_IMPORT_MW', 'XP_IMPORT_MW_ARCH')
or REGEXP_LIKE (table_name, 'XP_IMPORT_MW_ARCH_201(5|6|7)[0-9]{4}')
group by table_name
having count(column_id) != (
select count(column_id) from dba_tab_columns where table_name = 'XP_IMPORT_MW'
);
... although if you're using dba_* or all_* view you should really be including the owner, here and in your procedure.
I have made below code.
CREATE OR REPLACE TYPE CAL IS OBJECT(
EMPLOYEE_NAME VARCHAR2(30),
R_DATE DATE,
COMMENTS VARCHAR2(50)
);
CREATE OR REPLACE TYPE T_REC is table of cal;
create or replace function CALENDAR(v_team_name varchar2)
return t_rec
IS
v_rec t_rec;
v_COMM VARCHAR2(50);
v_name VARCHAR2(30);
BEGIN
FOR i in (Select ID,EMPLOYEE_NAME from EMPLOYEE where TEAM_NAME=v_team_name)
LOOP
v_name:=i.EMPLOYEE_NAME;
FOR k in (select EMP_ID, START_DT,END_DT-START_DT+1 DAYS,NAME,COMMENTS from EMP_ROTA where EMP_ID=i.ID)
LOOP
v_COMM:=k.NAME||', '||k.COMMENTS;
select t_rec(v_name,k.START_DT+level-1,v_COMM)
into v_rec
from dual connect by level < k.DAYS;
END LOOP;
END LOOP;
Return v_rec;
END;
Facing below error in compiling the function.
Compilation failed,line 14 (04:33:54)
PL/SQL: ORA-00932: inconsistent datatypes: expected UDT got CHARCompilation failed,line 14 (04:33:54)
PL/SQL: SQL Statement ignored
While using below query in function
select v_name,k.START_DT + level -1,v_COMM
into v_REC
from dual connect by level < k.DAYS;
Facing below error
PL/SQL: ORA-00947: not enough valuesCompilation failed,line 14 (04:50:49)
Trying small snippet of code stil facing ORA-00932.
create or replace function ROTA_CALENDAR
return t_CAL
IS
v_CAL t_CAL;
BEGIN
select t_cal('v_name',SYSDATE,'NAME')
into v_CAL
from dual;
Return v_CAL;
END;
what am I doing wrong?
I hope below snippet will help. Couple of things to take in consideration.
1 Don't use "" while any nomenclature
2 You have to select from Object type in the SELECT query as mentioned below.
3 Use BULK COLLECT instead of INTO only...
CREATE OR REPLACE
FUNCTION CALENDAR(
V_TEAM_NAME VARCHAR2)
RETURN t_rec
IS
v_rec t_rec;
v_COMM VARCHAR2(50);
v_name VARCHAR2(30);
BEGIN
FOR i IN
(
SELECT
EMPNO,
'AVRAJIT' ENAME
FROM
EMP
WHERE
JOB=v_team_name
)
LOOP
v_name:=i.ENAME;
FOR k IN
(
SELECT
EMPNO,
SYSDATE,
SYSDATE+1 DAYS,
ENAME,
JOB
FROM
EMP_V1
WHERE
EMPNO=i.EMPNO
)
LOOP
v_COMM:=k.ENAME||', '||k.JOB;
SELECT
CAL(V_NAME,SYSDATE+1,V_COMM) BULK COLLECT
INTO
v_rec
FROM
dual
CONNECT BY level < 10;
END LOOP;
END LOOP;
RETURN v_rec;
END;
-------------------------------OUTPUT-------------------------------------------
SELECT OBJECT_NAME,STATUS FROM ALL_OBJECTS
WHERE OBJECT_NAME = 'CALENDAR';
OBJECT_NAME STATUS
CALENDAR VALID
-------------------------------OUTPUT-------------------------------------------
I have table and sequence:
create table sometable (
id number(10) not null,
application varchar2(255 char) not null,
constraint sometable_pk primary key (id)
);
create sequence sometable_seq minvalue 1 start with 1 increment by 1 cache 10;
And PL/SQL procedure that suppose do insert into table but first check if such entry already exist and throw custom ORA-20000 error if that is the case:
create or replace
procedure dosth
(
application in varchar2
)
is
l_cnt integer := 0;
begin
select count(*) into l_cnt from sometable s where s.application = application;
dbms_output.put_line('count ' || l_cnt );
if (l_cnt = 0) then
insert into sometable (id, application) values (sometable_seq.nextval, application);
else
raise_application_error(-20000, 'application already exist:' || application);
end if;
end dosth;
/
When I call PL/SQL:
begin
dosth('app1');
end;
/
I receive:
anonymous block completed
count 0
All OK as there is no such entry in the table. Second call with same argument as expected:
ORA-20000: application already exist:app1
count 1
But what is strange is that consecutive calls using different argument values result in the same error.
begin
dosth('app2');
end;
/
ORA-20000: application already exist:app2
count 1
Of course there is no such entry in the table and
select count(*) from sometable s where s.application = 'app2';
returns 0 and not 1 as inside PL/SQL procedure!
It is really confusing.. probably sth really silly. Please help.
Thanks guys. To fix it as you pointed out I had to change this line:
select count(*) into l_cnt from sometable s where s.application = application;
into
select count(*) into l_cnt from sometable s where s.application = dosth.application;
although it is counter intuitive (at list for me) seems that this is the way PL/SQL works.