I am getting an error
ORA-00957: duplicate column name error while trying to create procedure
What is the problem ? How can I fix my query?
The procedure was created successfully. But when I try to execute the procedure, I got
ORA-00957: duplicate column name ORA-06512: at "SQL_BOTTZCVHAFEXQPWFMKGLKIGTC.SP_CRNA", line 91
When I execute each command in the query one-by-one, it executes properly. But executing it from the procedure call is not working.
CREATE OR replace PROCEDURE sp_crna
AS
v_query1 CLOB;
BEGIN
BEGIN
v_query1:='DROP TABLE TOP20 PURGE';
EXECUTE IMMEDIATE v_query1;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
BEGIN
v_query1:=
'CREATE TABLE top20 AS
SELECT ROWNUM AS rn,
country
|| '' ''
||total_cases AS top20
FROM (
SELECT *
FROM corona_project
ORDER BY total_cases DESC)
WHERE ROWNUM<21';
EXECUTE IMMEDIATE v_query1;
END;
BEGIN
v_query1:='DROP TABLE BOTTOM20 PURGE';
EXECUTE IMMEDIATE v_query1;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
BEGIN
v_query1:=
'CREATE TABLE bottom20 AS
SELECT ROWNUM AS rn,
country
|| '' ''
||total_cases AS BOTTOM20
FROM (SELECT *
FROM corona_project
ORDER BY total_cases)
WHERE ROWNUM < 21';
EXECUTE IMMEDIATE v_query1;
END;
BEGIN
v_query1:='DROP TABLE MIDDLE_ROW PURGE';
EXECUTE IMMEDIATE v_query1;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
BEGIN
v_query1:=
'CREATE TABLE MIDDLE_ROW AS
SELECT ROWNUM AS rn,
SUM(total_cases) AS total_cases,
SUM(deaths) deaths,
SUM(recovered) recovered,
SUM(new_cases) new_cases,
SUM(new_deaths) new_deaths,
SUM(seriouscritical) seriouscritical
FROM corona_project
GROUP BY ROWNUM,
total_cases,
deaths,
recovered,
new_cases,
new_deaths,
seriouscritical';
EXECUTE IMMEDIATE v_query1;
END ;
BEGIN
v_query1:='DROP TABLE DISPLAY PURGE';
EXECUTE IMMEDIATE v_query1;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
BEGIN
v_query1:=
'CREATE TABLE display AS
SELECT T.*,
M.*,
B.*
FROM top20 T
left join bottom20 B
ON T.rn = B.rn
left join middle_row M
ON M.rn = T.rn';
EXECUTE IMMEDIATE v_query1;
END;
END sp_crna;
/
EXEC sp_crna;
From my point of view, the whole procedure is wrong. In Oracle, we really, Really, REALLY rarely create tables dynamically. We do create them once (even if they are (global or private) temporary tables) and use them many times.
What you should do, in my opinion, is to create tables at SQL level and - using the stored procedure (if you want it) INSERT rows into those pre-created tables. When you don't need that data any more, TRUNCATE tables or DELETE their contents. According to what you posted, you'd delete tables at the beginning of that procedure.
Something like this:
create table top20
(rn number,
top20 varchar2(100));
create table bottom20
(rn number,
bottom20 varchar2(100));
create table middle_row
(rn number,
total_cases number,
deaths number,
...);
Now, create the procedure which will insert rows:
create or replace procedure p_rows as
begin
delete from top20;
delete from bottom20;
delete from middle_row;
insert into top20 (rn, top20)
select ...;
...
end;
/
Run it whenever you want:
begin
p_rows;
end;
/
Instead of the display table, use a view:
create or replace view display as
select t.rn,
t.top20,
b.bottom20,
m.total_cases,
m.deaths,
m....
from top20 t left join bottom20 b on b.rn = t.rn
left join middle_row m on m.rn = t.rn;
Though, I'm not sure what is the rn column supposed to do in this context. ROWNUM maybe isn't the best column to be used for joins, so ... think about it (and its replacement).
As of error you got, this piece of code is wrong (in your procedure):
CREATE TABLE display AS
SELECT T.*,
M.*,
B.*
FROM top20 T
left join bottom20 B
ON T.rn = B.rn
left join middle_row M
ON M.rn = T.rn
Why? Because those table share the same column (rn), and you can't have two (or more) columns with the same name in the same table. It means that you can't (and shouldn't) use asterisk, but name all columns - one-by-one - providing column aliases where necessary, e.g.
CREATE TABLE display AS
SELECT T.rn top_rn, t.top20,
M.rn middle_rn, m.total_cases, m.deaths, m...,
b.rn bottom_rn, b.bottom20
FROM top20 T
left join bottom20 B
ON T.rn = B.rn
left join middle_row M
ON M.rn = T.rn
Related
I have a rather complex plpgsql stored procedure and I need to select from multiple tables and insert as well.
This is part of what I currently have.
BEGIN
RETURN query
SELECT domains.id, webpages.id as page_id ...
FROM domains
LEFT JOIN domain_settings
ON domain_settings.domain_id = domains.id
RIGHT JOIN webpages
ON webpages.domain_id = domains.id
LEFT JOIN subscriptions
ON webpages.id = subscriptions.page_id
AND subscriptions.user_id = query_user_id
AND subscriptions.comment_id IS NULL
WHERE domains.domain_address = query_domain_url
IF NOT FOUND THEN ...
END;
$$ language plpgsql;
Now, I would like add an insert query into another table using certain values from the return query before the 'if not found then' statement:
INSERT INTO page_visits (domain_id, page_id)
SELECT id, page_id FROM ?? (return query statement)
And after the insert, I want to return the initial return query values. How do I go about doing this? I tried using WITH AS statements, but I can't seem to get it to work
A set-returning PL/pgSQL function builds the return stack while processing the function body. There is no way to access that return stack from within the same function. You could nest the function. Or use a temporary table.
But using a CTE is probably the simplest way for the cas at hand. Going out on a limb, you may be looking for something like this:
CREATE OR REPLACE FUNCTION demo(query_user_id int, query_domain_url text)
RETURNS TABLE (c1 int, c2 int)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
WITH sel AS (
SELECT d.id, w.id as page_id ...
FROM webpages w
JOIN domains d ON d.id = w.domain_id
LEFT JOIN domain_settings ds ON ds.domain_id = d.id
LEFT JOIN subscriptions s ON s.page_id = w.id
AND s.user_id = query_user_id -- origin?
AND s.comment_id IS NULL
WHERE d.domain_address = query_domain_url -- origin?
)
, ins AS (
INSERT INTO tbl (col1, col2)
SELECT main.id, sel.page_id
FROM (SELECT 'foo') AS main(id)
LEFT JOIN sel USING (id) -- LEFT JOIN ?
)
TABLE sel;
IF NOT FOUND THEN
-- do something
END IF;
END
$func$;
Remember, if the transaction does not commit successfully, the INSERT is also rolled back.
The final TABLE sel is just short syntax for SELECT * FROM sel. See:
Is there a shortcut for SELECT * FROM?
I'm trying to write a trigger that raises error when someone attempts to rate a product which has not been bought. I have come up with a query to get the purchase history of a client :
SELECT nref
FROM CartClient a
INNER JOIN PaidCart b
ON a.idpurchase = b.idpurchase
INNER JOIN CartDetails c
ON b.idpurchase = c.idpurchase
WHERE a.Id = '12345672X'
which works fine.
So the next trigger should check if the product of a new rating (:new.NRef) has not been bought, namely is not part of the result of the last query (NOT IN).
CREATE OR REPLACE TRIGGER cant_rate
BEFORE INSERT ON Rating
FOR EACH ROW
BEGIN
IF (:new.NRef NOT IN (SELECT nref FROM CartClient a
INNER JOIN PaidCart b
ON a.idpurchase = b.idpurchase
INNER JOIN CartDetails c
ON b.idpurchase = c.idpurchase
WHERE a.Id =:new.Id)) THEN
RAISE_APPLICATION_ERROR(-20603,'Cant rate a not bought product');
END IF;
END;
I get error:
"PLS-00103:Encountered the symbol “INNER” when expecting one of".
I have tried to store the result of the query in a temporal variable using SELECT INTO. But, it's a multiple row result. What could I do?
How about such an approach?
CREATE OR REPLACE TRIGGER cant_rate
BEFORE INSERT ON Rating
FOR EACH ROW
DECLARE
l_exists NUMBER(1) := 0;
BEGIN
SELECT MAX(1)
INTO l_exists
FROM dual
WHERE EXISTS (SELECT nref FROM CartClient A
INNER JOIN PaidCart b
ON A.idpurchase = b.idpurchase
INNER JOIN CartDetails C
ON b.idpurchase = C.idpurchase
WHERE A.ID = :NEW.ID
AND a.nref = :NEW.nref --> is it "a.nref"?
);
IF l_exists = 0 THEN
RAISE_APPLICATION_ERROR(-20603,'Cant rate a not bought product');
END IF;
END;
Note remark "is it a.nref?" - you never said which table owns that nref column so I presumed it is cartclient; modify it, if necessary.
As of your attempt: if you executed it in SQL*Plus or SQL Developer, you'd see a message regarding subquery in IF; something like this:
LINE/COL ERROR
--------- -------------------------------------------------------------
2/3 PL/SQL: Statement ignored
2/21 PLS-00405: subquery not allowed in this context
So, no - you can't do it that way.
You probably don't get the actual error: PLS-00405 subquery not allowed in this context.
Simple stupid test:
BEGIN
IF 1 NOT IN ( SELECT 2 FROM DUAL )
THEN
NULL;
END IF;
END;
/
You can adjust your trigger in this way or similar:
CREATE OR REPLACE TRIGGER cant_rate
BEFORE INSERT ON Rating
FOR EACH ROW
DECLARE
l_test PLS_INTEGER;
BEGIN
SELECT CASE
WHEN :new.NRef NOT IN ( SELECT nref
FROM CartClient a
INNER JOIN PaidCart b
ON a.idpurchase = b.idpurchase
INNER JOIN CartDetails c
ON b.idpurchase = c.idpurchase
WHERE a.Id = :new.Id
)
THEN 1
ELSE 0
END
INTO l_test;
FROM DUAL;
IF l_test = 1
THEN
...
END IF;
END;
/
I have a query that I am trying to use within a procedure. The query works exactly as it should but once I place it inside of the BEGIN and END of the procedure, it tells me "name is already used by an existing object". Not sure what this means, any help would be great.
Here is the orignal query:
SELECT
course.course_no,
course.description,
section.section_no,
enrcount.countofenrollment
FROM
course
INNER JOIN section ON course.course_no = section.course_no
INNER JOIN (
SELECT
section_id,
COUNT(*) countofenrollment
FROM
enrollment
GROUP BY
section_id
) enrcount ON section.section_id = enrcount.section_id
WHERE
enrcount.countofenrollment < 6;
Here is the procedure:
CREATE PROCEDURE PRC_Enrollment
AS
BEGIN
SELECT
course.course_no,
course.description,
section.section_no,
enrcount.countofenrollment
FROM
course
INNER JOIN section ON course.course_no = section.course_no
INNER JOIN (
SELECT
section_id,
COUNT(*) countofenrollment
FROM
enrollment
GROUP BY
section_id
) enrcount ON section.section_id = enrcount.section_id
WHERE
enrcount.countofenrollment < 6;
END;
Use create or replace procedure.. instead of just create procedure.. in order to resolve the error youve mentioned above. That means already the procedure name has been used.
First of all INTO clause should be added to return the values of the columns
CREATE OR REPLACE PROCEDURE PRC_Enrollment AS
v_course_no course.course_no%type;
v_description course.description%type;
v_section_no section.section_no%type;
v_count_enrl int;
BEGIN
SELECT c.course_no, c.description, s.section_no, e.countofenrollment
INTO v_course_no, v_description, v_section_no , v_count_enrl
FROM course c
JOIN section s
ON c.course_no = s.course_no
JOIN (SELECT section_id, COUNT(*) countofenrollment
FROM enrollment
GROUP BY section_id) e
ON s.section_id = e.section_id
WHERE e.countofenrollment < 6;
EXCEPTION WHEN NO_DATA_FOUND THEN NULL;
END;
/
Alternatively you can return those values as out parameters by using
CREATE OR REPLACE PROCEDURE PRC_Enrollment(
v_course_no out course.course_no%type,
v_description out course.description%type,
v_section_no out section.section_no%type,
v_count_enrl out int
) AS
instead of returning them as local variables as in the first case.
alias your tables with a letter, usually preferred the first letter
of the tables
add exception handling, at least against the case no data found in
order not to get error
Btw, you get such an error, if OR REPLACE has not been added after CREATE keyword for the second and subsequent compilations.
Is it possible to define a with clause in a stored procedure and use it in if else statements because I always get an error?
BEGIN
WITH Test (F, A) AS
(
SELECT FM.ID, FM.Name
FROM [Test.Abc] FM
INNER JOIN [Organization] O on O.ABCID = FM.ID
)
IF(#var = 'case1')
BEGIN
SELECT *
FROM Test F
WHERE NOT F.ID = 'someID'
END
I always get an "Incorrect syntax" error before the if statement
If I move the with clause into the if statement it works fine. But I need the with statement outside to reuse it in different if else statements.
Here's another version of the same answers you're getting:
Your with common table expresson has to be in the same statement as the query that calls it, and it has to be referenced by a query (or other cte) or it is a syntax error.
Reference the documentation Guidelines for Creating and Using Common Table Expressions.
BEGIN -- doing stuff
-- .... doing stuff over here
IF(#var = 'case1')
BEGIN
with Test (F, A) as (
select FM.ID, FM.Name from [Test.Abc] FM
inner join [Organization] O on O.ABCID = FM.ID
)
select * from Test F
where not F.ID = 'someID'
END
-- .... and doing some other stuff over here too
END -- done with this stuff
Just use a temporary table or table variable. The scoping rules of SQL Server ensure that such a table is dropped at the end of the procedure:
BEGIN
select FM.ID, FM.Name
into #test
from [Test.Abc] FM inner join
[Organization] O
on O.ABCID = FM.ID;
IF(#var = 'case1')
BEGIN
select *
from #Test F
where not F.ID = 'someID'
END;
This has the advantage that you can add indexes to the table, and these might improve performance.
WITH is not a standalone, it always a part of a whole statement and only one statement.
It is not recognizable outside the scope ofits statement.
BEGIN
with my_cte (n) as (select 1+1)
select * from my_cte
-- The following statement yields the error "Invalid object name 'my_cte'."
-- select * from my_cte
END
I have a type object as below
Emptable type is empname,empid,rank
Then I have a Plsql function as below and this errors out. I need to run a sql select statement against the returned list of empids and load it the returned list. and below code keeps erroring..
create or replace function emp_details return emptable
is
l_result_col emptable := emptable();
n integer := 0;
rxvalue number;
begin
for r in (select empname,empid from table)
loop
l_result_col.extend;
n := n + 1;
(select sum(xyz) into rxvalue from A inner join B on A.x=B.x and A.id=r.empid);
l_result_col(n) := t_col(r.empname, r.empid,rxvalue);
end loop;
return l_result_col;
end;
/
Any Help is appreciated..
Thank you!
Why not do it straightforward without involving PL/SQL code?
select t_col(r.empname, r.empid, sum(xyz))
bulk collect into l_result_col
from table r
left join (A join B on A.x = B.x) on A.id = r.empid
group by r.empname, r.empid;
I think your select shouldn't be in parentheses. In this context it is not a subselect but a separate PL/SQL statement.