Error when creating procedure with existing query - sql

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.

Related

ORA-00957: duplicate column name while executing the procedure

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

Select query, insert into table, and return initial query in postgres

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?

SQL with clause in stored procedure

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

Use stored procedure in Select query

I have a stored procedure that I want to use in a SELECT like below but I get an error.
The stored procedure's output is a table with one column of type int
select * from users where resCode in (EXEC getUserResselers 1)
-- stored procedure
create procedure getUserResselers
#userCode int
as
;WITH Directories AS (SELECT code FROM Resseler WHERE code =(select resselerCode from Users where code=#userCode)
UNION ALL SELECT d.code FROM Resseler d INNER JOIN Directories p ON d.parent = p.code)
SELECT * FROM Directories d
Direct selection from a stored procedure is not supported. However, this type of operation is a good candidate for a table-valued user-defined function, which might look something like:
Note that you have misspelled "resellers". I've left the misspelling so that you could test with your existing schema.
CREATE FUNCTION getUserResselers
(
#UserCode INT
)
RETURNS TABLE AS
RETURN(
WITH Directories AS (
SELECT code FROM Reseller WHERE code = (select resselerCode from Users where code=#userCode)
UNION ALL SELECT d.code FROM Resseler d INNER JOIN Directories p ON d.parent = p.code
)
SELECT * FROM Directories
)
You could then include a join to the function in your query, or you could continue to use IN, which should be something like:
select * from users where resCode in (SELECT Code FROM getUserResselers(1));

DB2 set default when null on join / Open table after FETCH

This is kind of a double question, just thinking of ways to accomplish my problem.
Also, I'm pretty new to DB2 and stored procedures, so bear with me.
I'm creating a stored procedure that gets a value from two tables using a Left Join statement. This will result in some of the values in the second table returning a null value (since they don't exist in tableB).
DECLARE CURSOR C1 WITH RETURN FOR
select a.name, a.title, b.order from tableA a
left outer join tableB b on a.name = b.name;
Now, I need some way to set these null values to a default value of 0.
The program I'm working with can do it ( CAST-IRON ) but if the result set is too large, it slows down the orchestrations and truncates the job log. So I'm trying to figure it out using the stored procedure.
My first thought was to use the FETCH INTO statement and a WHILE loop.
WHILE AT_END = 0 DO
FETCH C1 INTO CHNAME, CHTITLE, CHORDER;
IF CHORDER IS NULL
THEN SET CHORDER = 0;
END IF;
IF SQLCODE = 100
THEN SET AT_END = 1;
END IF;
END WHILE;
But it seems like that would require a temporary table being created, and declaring another cursor with that table, using an insert command after the 'FETCH INTO'. So I was wondering if there were another way to do this, or to automatically set a default in the select statement?
Set a default in the select statement using COALESCE.
DECLARE CURSOR C1 WITH RETURN FOR
select a.name, a.title, COALESCE(b.order,0) as order
from tableA a
left outer join tableB b on a.name = b.name;