Hello so i was coding a procedure for my database but i got stuck when i the compiler(Oracle SQL Developer) started telling me that
there is a missing right parenthesis
but there is no missing parenthesis as much as I observe.
Here is the procedure:
CREATE OR REPLACE PROCEDURE generer_listes_preferees (
p_id_user IN UTILISATEUR."id_user"%TYPE,
p_films OUT SYS_REFCURSOR
)
AS
-- Déclarer les variables locales
CURSOR c_films IS
SELECT *
FROM OBJET
WHERE "categorie_obj" LIKE 'Films' AND "id_objet" IN (
SELECT "id_objet"
FROM AVIS
WHERE "id_user" = p_id_user
ORDER BY "note" DESC, "date_avis" DESC
)
FETCH FIRST 10 ROWS ONLY;
BEGIN
-- Générer la liste de films préférés
OPEN p_films FOR SELECT * FROM TABLE(c_films);
END;
What could be the issue?
The procedure does not compile because the query is not valid. In particular, Oracle is looking for the closing parenthesis to finish the subquery, but it finds an unexpected ORDER BY clause instead:
SELECT *
FROM OBJET
WHERE "categorie_obj" LIKE 'Films' AND "id_objet" IN (
SELECT "id_objet"
FROM AVIS
WHERE "id_user" = p_id_user
ORDER BY "note" DESC, "date_avis" DESC
)
(Demo)
The error message could indeed be more useful, but the problem with incorrect code is that the parser can't really figure out your intentions.
Even if the ORDER BY clause was allowed, it wouldn't accomplish anything. The IN () operator is not sensitive to order. I guess you meant to order the main outer query.
It's also worth noting that "categorie_obj" LIKE 'Films' is the same as "categorie_obj" = 'Films'. Performance overhead (it has to parse the expression looking for wildcards) is for sure negligible, but I think it's more readable to just be explicit.
On a side note, I'd advise against double quoting identifiers. The only feature it provides is to hard-code the table/column case, so you're forced to type the quotes and the exact case every time since expressions WHERE categorie_obj or WHERE CATEGORIE_OBJ will no longer work.
Related
While reading upon the difference between decode function and CASE WHEN statement, I got confused while understanding that if both give the same result for a simple equation like the following:
DECODE(col1, 'ABC', 'DEF', 'OTHERS' ) AS COL2
CASE WHEN COL1 = 'ABC' THEN 'DEF'
ELSE 'OTHERS'
END AS COL2
Then what is the difference between the functionality of a function and a statement?
p.s. I am aware Case is capable of taking more complex equations... my question here is more about understanding difference between function and statement.
In SQL, there is no such thing as a "CASE statement". Your example is a CASE expression. (CASE statements do exist in PL/SQL.)
The documentation states "An expression is a combination of one or more values, operators, and SQL functions that evaluates to a value." So a SQL function is not different from an expression, it is a specific type of expression.
Note that DECODE and CASE behave differently when comparing NULL values: DECODE considers two NULLs to be "the same", which is an exception to the rule that comparing a NULL to anything has an "unknown" result.
with data(a, b) as (
select 1,1 from dual union all
select 1,null from dual union all
select null,1 from dual union all
select null, null from dual
)
select a, b,
decode(a,b,'same','different') decode_result,
case when a = b then 'same' else 'different' end case_result
from data;
A B DECODE_RESULT CASE_RESULT
------ ------ ------------- -----------
1 1 same same
1 (null) different different
(null) 1 different different
(null) (null) same different
I'm pretty much sure that CASE didn't exist in pre-9i Oracle database versions so you had to use DECODE.
No difference, they will both do the same job, but CASE is simpler to maintain. DECODE can grow to a real monster when complex things have to be done, with nested DECODEs so you probably know what you're doing while typing that statement, but a month later you're in deep trouble as you don't know what belongs to what, which closing bracket is closing which open bracket, ... real nightmare.
But, for simple things, it is quite OK. Saves some typing.
A function is simply some functionality capsuled. With a function you can call a functionality from anywhere without spelling it out explicitely. There are predefined functions like DECODE() or you can define functions yourself.
A statement is something like select 1 from dual and may contain functions (select decode(col1,'1','yes','no') from mytable).
The statement is (as long as you aren't in a block, which itself may contain several statements) an order you send to the DB, a function is kindof an instruction what to do with the given arguments.
SELECT b.*
FROM buses b,
bus_stations bs,
starts st,
stops_at sa
WHERE st.station_no = ( SELECT station_id
FROM bus_stations
WHERE station_name = "golden mile_Regina"
)
AND sa.station_no = ( SELECT station_id
FROM bus_stations
WHERE station_name = 'westmount_edmonton'
)
ORDER BY DATE;
You can't use double quotes with strings - use single ones, i.e.
WHERE station_name = 'golden mile_Regina'
By the way, are you sure of spelling & letter size? Is it really mixed case, with underscores? Just asking.
Furthermore, you're ordering by DATE - that won't work either, you can't use DATE as a column name (unless you enclose it into double quotes, but I certainly wouldn't recommend that). Have a look at the following example (stupid, yes - setting date to be a number, but I used it just to emphasize that DATE can't be used as a column name):
SQL> create table test (date number);
create table test (date number)
*
ERROR at line 1:
ORA-00904: : invalid identifier
Once you fix that, you'll get unexpected result as there are 4 tables in the FROM clause, but they aren't joined with one another, so that will be a nice Cartesian product.
I know the right syntax for having a function definition in the WITH clause. I know the right syntax for having a subquery in the WITH clause. But I have been unable to find an example of having a subquery and a function definition in the WITH clause of a SELECT statement.
If I have:
with totals as ( select colum_name from some_table )
select sum(column_name) from totals;
How do I add a function definition in the WITH clause?
Since you can't find much/anything about this from Oracle, I don't think it is a good idea to use it. Anyway, this works in 18.1:
WITH
FUNCTION with_plus(p IN NUMBER) RETURN NUMBER IS
BEGIN
RETURN p + 1;
END;
FUNCTION with_min(p IN NUMBER) RETURN NUMBER IS
BEGIN
RETURN p - 1;
END;
qry1 AS (
SELECT with_plus(10) plus
FROM DUAL
),
qry2 AS (
SELECT plus, with_min(10) min
FROM qry1
)
SELECT *
FROM qry2
;
/
So don't forget the slash / at the end.
If you ever find out how to put this whole block in a subquery, please let me know
I don't think there's any such restriction. However, I suspect that your problem has to do with column aliasing. Here's what worked for me:
with totals as (select sum(column_name) c1 from some_table)
select c1 from totals;
Oracle might have complained because you were trying to do something like:
with totals as (select sum(column_name) from some_table)
select sum(column_name) from totals;
Unfortunately, this is a consequence of name resolution. The subquery's column will get named "sum(column_name)". Since sum is a function, there's no way to reference that column name without Oracle thinking you're referencing the function. You have to give it another name in order to reference it anywhere else.
Edit: It seems that you want to define a function as if you would a view subquery. I don't think anything like this is possible. View subqueries really only perform textual substitution.
PL/SQL functions require a whole different parser, name resolution, compilation process, etc. Having them work in queries alone is hard enough.
Sorry to say, but you'd have to define your packages/procedures/functions normally.
Is it possible to run a query which matches ANY rows in another table column? I'm trying to run this for example:
SELECT *
FROM emails
WHERE address ILIKE '%#' || IN (select * from dictionary.wordlist) || '.%'
However this returns [Vertica]VJDBC ERROR: Subquery used as an expression returned more than one row
Now that's a strange way of formulating it...
If you go back to a basic SQL tutorial, you will understand that a string literal like '%#' , which can be the operand of an ILIKE predicate, cannot be concatenated with an IN () clause - which is a predicate in itself.
I assume that you are looking for all rows in the emails table whose address contains any of the words in dictionary.wordlist between the at-sign and a dot.
I hope (correct me if I'm wrong) that dictionary.wordlist is a table with one column in VARCHAR() or other string format. If that is the case, you can go like this:
WITH
-- out of "dictionary.wordlist", create an in-line-table containing a column
-- with the wildcard operand to be later used in an ILIKE predicate
operands(operand) AS (
SELECT
'%#'||wordlist.word||'.%'
FROM dictionary.wordlist
)
SELECT
emails.*
FROM emails
INNER JOIN operands
ON email.address ILIKE operands.operand
;
There are other ways of doing it, of course, but this is one of them.
I'm not trying to say it will be very fast - an ILIKE predicate as a JOIN condition can't be performant ...
Good luck
Marco the Sane
I would like to have your advise how to implement the plsql. Below is the situation that i want to do..
select * from table A
loop - get each records from #1 step, and execute the store procedure, processMe(a.field1,a.field2,a.field3 || "test",a.field4);
i dont have any idea how to implement something like this. Below is sample parameter for processMe
processMe(
number_name IN VARCHAR,
location IN VARCHAR,
name_test IN VARCHAR,
gender IN VARCHAR )
Begin
select objId into obj_Id from tableUser where name = number_name ;
select locId into loc_Id from tableLoc where loc = location;
insert into tableOther(obj_id,loc_id,name_test,gender)
values (obj_Id ,loc_Id, name_test, gender)
End;
FOR rec IN (SELECT *
FROM table a)
LOOP
processMe( rec.field1,
rec.field2,
rec.field3 || 'test',
rec.field4 );
END LOOP;
does what you ask. You probably want to explicitly list the columns you actually want in the SELECT list rather than doing a SELECT * (particularly if there is an index on the four columns you actually want that could be used rather than doing a table scan or if there are columns you don't need that contain a large amount of data). Depending on the data volume, it would probably be more efficient if a version of processMe were defined that could accept collections rather than processing data on a row-by-row bases as well.
i just add some process. but this is just a sample. By the way, why
you said that this is not a good idea using loop? i interested to know
Performance wise, If you can avoid looping through a result set executing some other DMLs inside a loop, do it.
There is PL/SQL engine and there is SQL engine. Every time PL/SQL engine stumbles upon a SQL statement, whether it's a select, insert, or any other DML statement, it has to send it to the SQL engine for the execution. It calls context switching. Placing DML statement inside a loop will cause the switch(for each DML statement if there are more than one of them) as many times as many times the body of a loop has to be executed. It can be a cause of a serious performance degradation. if you have to loop, say, through a collection, use foreach loop, it minimizes context switching by executing DML statements in batches.
Luckily, your code can be rewritten as a single SQL statement, avoiding for loop entirely:
insert into tableOther(obj_id,loc_id,name_test,gender)
select tu.objId
, tl.locid
, concat(a.field3, 'test')
, a.field4
from table a
join tableUser tu
on (a.field1 = tu.name)
join tableLoc tl
on (tu.field2 = tl.loc)
You can put that insert statement into a procedure, if you want. PL/SQL will have to sent this SQL statement to the SQL engine anyway, but it will only be one call.
You can use a variable declared using a cursor rowtype. Something like this:
declare
cursor my_cursor is
select * from table;
reg my_cursor%rowtype;
begin
for reg in my_cursor loop
--
processMe(reg.field1, reg.field2, reg.field3 || "test", reg.field4);
--
end loop;
end;