Can anyone explain why this function is not working in mariaDB? - mariadb

Can anyone help with the below code? Im new to mariaDB and am struggling to create the function. Im not even sure if im highliting and executing it right. Whatever I do, I get many errors.
DELIMITER $$
CREATE FUNCTION singerExperience(
experience DECIMAL(10,2)
)
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
DECLARE singerExperience VARCHAR(20);
IF hours > 4000 THEN
SET singerExperience = 'PLATINUM';
ELSEIF (hours >= 4000 AND
hours <= 1000) THEN
SET singerExperience = 'GOLD';
ELSEIF hours < 1000 THEN
SET singerExperience = 'SILVER';
END IF;
RETURN (singerExperience);
END $$
DELIMITER ;
SELECT singer_id, singerExperience(experience)
FROM experiencelog
ORDER BY singer_id;

If you rename the parameter experience to hours (or variable hours to experience) the function should work.
However why do you need a function, if you can handle that within the statement?
SELECT singer_id,
case experience < 1000 THEN 'SILVER' WHEN experience < 4000 THEN 'GOLD' ELSE 'PLATINUM' END
FROM experiencelog
ORDER BY singer_id;

Related

MySql mariaDB CREATE FUNCTION DELIMITER doesnt work. has error near ''

im going insane. i dont know whats wrong with this code i see nothing wrong in it. but it kept reading thee same error each time i put the $$ after the "END". please help guys...
here is my code
DELIMITER $$
CREATE FUNCTION fungsi1 (a int)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE nilaiasal INT;
DECLARE teks VARCHAR(50);
SET nilaiasal = a * a;
IF nilaiasal > 100 THEN
SET teks = 'LEBIH DARI SERATUS';
ELSEIF nilaiasal < 100 THEN
SET teks = 'KURANG DARI SERATUS';
INSERT INTO table1 (dataone,datatwo) VALUES(a,teks);
SELECT * FROM table1;
RETURN (nilaiasal);
END $$
DELIMITER ;
And the error is this
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 15
Please help me. I've looked up create functions error and there hasnt been anyone that has a problem near ''.
You might want to consider using a stored procedue instead of a function if you want to return a result set.
You are missing END IF; as Ergest Basha suggested and you are trying to return a result set from a function which is not allowed.

PL/SQL if then else statements not running

I have written following code in oracle pl/sql
create or replace procedure sorting_criteria(criteria in varchar)
as
begin
if(criteria='lowest price')
then
declare
p_name product.p_name%type;
cursor ptr is select p_name from product order by amount ASC;
begin
open ptr;
loop
fetch ptr into p_name;
exit when ptr%notfound;
dbms_output.put_line(p_name);
end loop;
close ptr;
end;
else if(criteria='highest price')
then
declare
p_name product.p_name%type;
cursor ptr is select p_name from product order by amount DESC;
begin
open ptr;
loop
fetch ptr into p_name;
exit when ptr%notfound;
dbms_output.put_line(p_name);
end loop;
close ptr;
end;
else
dbms_output.put_line('Enter valid criteria!');
end if;
end;
/
But it is giving following error: Error at line 35: PLS-00103: Encountered the symbol ";" when expecting one of the following: Please help
The ELSE-IF statement in PL/SQL has to be written as ELSIF. Otherwise, you should close the second IF with an other END IF; statement.
You can solve the issue by changing the ELSE IF at line 17 to an ELSIF
The answer by #GregorioPalamà correctly addresses your issues. But you can drastically reduce the workload by changing your thinking away from "If...then...else" to the "set of" and letting SQL do the work. In this case the only difference is sorting either ascending or descending on amount. The same effect can be achieved by sorting ascending on amount or minus amount; and SQL can make that decision. So you can reduce the procedure to validating the parameter and a single cursor for loop:
create or replace procedure sorting_criteria(criteria in varchar2)
as
cursor ptr(c_sort_criteria varchar2) is
select p_name
from product
order by case when c_sort_criteria = 'lowest price'
then amount
else -amount
end ;
begin
if criteria in ('lowest price', 'highest price')
then
for rec in ptr(criteria)
loop
dbms_output.put_line('Product: ' || rec.p_name );
end loop;
else
dbms_output.put_line('Enter valid criteria!');
end if;
end sorting_criteria;
/
See demo here. For demonstration purposed I added the amount to the dbms_output.
A couple notes:
While it is not incorrect using p_... as a column name, it is also
not a good idea. A very common convention (perhaps almost a
standard) to use p_... to indicate parameters. This easily leads to
confusion; confusion amongst developers is a bad thing.
IMHO it is a bug to name a local variable the same as a table
column name. While the compiler has scoping rules which one to use
it again leads to confusion. The statement "where table.name = name"
is always true, except when at least one of them is null, which possible could lead to updating/deleting every row in your table. In this
case p_name is both a column and a local variable.

The difference between the user's old and new password isn't checked in Oracle APEX

I’m working with an Oracle APEX application and today I have discovered that the password verify function, which is set up for several profiles, doesn’t work properly, because the parameter, which stands for the old password, turned out to be null.
My password verify function F_VERIFY_PASSWORD is based on the example from the Oracle admin file utlpwdmg.sql: the code changes were made only for the parts which are not related to the comparison of the old password and the new one. The function code is presented below (excluding the parts which make no problems):
create or replace function F_VERIFY_PASSWORD
(
USERNAME varchar2
,PASSWORD varchar2
,OLD_PASSWORD varchar2
) return boolean is
…
DIFFER integer;
MINDIFFER number := 3;
…
begin
…
-- Check that the new password differs from the previous one no less than in MINDIFFER positions.
if OLD_PASSWORD is not null
then
DIFFER := LENGTH(OLD_PASSWORD) - LENGTH(PASSWORD);
DIFFER := ABS(DIFFER);
if DIFFER < MINDIFFER
then
if LENGTH(PASSWORD) < LENGTH(OLD_PASSWORD)
then
M := LENGTH(PASSWORD);
else
M := LENGTH(OLD_PASSWORD);
end if;
for I in 1 .. M
loop
if SUBSTR(PASSWORD,I,1) != SUBSTR(OLD_PASSWORD,I,1)
then
DIFFER := DIFFER + 1;
end if;
end loop;
if DIFFER < MINDIFFER
then
RAISE_APPLICATION_ERROR(-20011,'The new password must differ from the old one no less than in 3 positions!');
end if;
end if;
end if;
-- Return TRUE, if all is correct.
return(true);
end;
The changing password itself is made by the means of the procedure P_CHANGE_PASS, which is executed in Oracle APEX application. The code of it is presented below (excluding the parts which make no problems).
create or replace procedure P_CHANGE_PASS
(
NEWPASS varchar2
,NEWPASS2 varchar2
,USR varchar2
) as
…
begin
…
--changing the password
begin
execute immediate 'alter user ' || USR || ' identified by ' || NEWPASS;
exception
when others then
…
end;
end;
I’ve tried the following steps to make the verify function include the check of differences between the old and new passwords:
revoked the privilege ALTER USER from the user APEX_PUBLIC_USER;
added the statement AUTHID CURRENT_USER to the code of the
“client” procedure P_CHANGE_PASS;
added REPLACE <old password>
to the ALTER USER command (it’s awful thing, but I made it just
for debugging).
None of these actions helped me to solve my problem. So, what should I do to make the parameter OLD_PASSWORD not null in the function F_VERIFY_FUNCTION?
I use Oracle 11g (version 11.2.0.3.0), Oracle APEX 4.2.6.00.03.

Oracle APEX: Execute immediate on collection

I have the following code:
declare
y pls_integer := 0;
v_msg varchar2(4000);
plsql varchar(4000);
begin
if not apex_collection.collection_exists(p_collection_name=>'P16_COLLECTION') then
wwv_flow.debug('No Apex collection found!');
else
for x in (select * from apex_collections where collection_name = 'P16_COLLECTION' and seq_id > 1 order by seq_id)
loop
y := y+1;
FOR i IN 1..25
LOOP
plsql := 'begin apex_collection.update_member_attribute (p_collection_name=> ''P16_COLLECTION'', p_seq=>' || TO_CHAR(x.seq_id) || ',p_attr_number =>' || TO_CHAR(i) || ',p_attr_value=>wwv_flow.g_f' || TO_CHAR(i, 'FM00') || '(' || TO_CHAR(y) || ')); end;';
wwv_flow.debug(plsql);
EXECUTE IMMEDIATE plsql;
END LOOP;
end loop;
end if;
exception when others then
v_msg := ''||sqlerrm;
wwv_flow.debug('ERR: '||v_msg);
end;
This code is very similar to the one proposed here, but I loop through 25 columns. The issue with Oracle Apex is the max number of chars PL/SQL is allowed to have, so I am unable to just write 25 update_member_attribute - calls.
But instead of a it excecuting I get an error no data found.
I triple checked that the collection P16_COLLECTION exists.
The issue with Oracle Apex is the max number of chars PL/SQL is allowed to have
I'm not sure I understood this statement. It is PL/SQL you use. You declared a local PLSQL variable as VARCHAR2(4000). Why didn't you specify its max allowed size, 32767? Would that help?
Furthermore, saying that you got NO-DATA-FOUND exception: are you sure that this piece of code raised it? Because, there's no SELECT statement in there ... the one you used in a cursor FOR loop can't raise NO-DATA-FOUND; UPDATE either. Therefore, it must be something else, I presume.
Enable DEBUG, run the page and - when you get the error - view debug results and locate the culprit.

Optimise PL/SQL or migrate to Clojure(parallezable language)?

I have a large table and wish to iterate over the records( > 1,000,000 ), perform some checks based on another 2 sets each table >= 1 and output the result to a text file.
The PL\SQL that does this takes several hours and I could optimize it or alternatively I could just rewrite this as a clojure program that is parallelizable since there are only selects and no writes(to the tables).
Questions:
1 What challenges/limits are there in optimizing PL/SQL?
2 Are there major up sides to migrating the code to clojure vis-a-vis optimizing the PL/SQL?
EDIT
Here is the meat of it
OPEN cur;
LOOP
FETCH cur INTO l_cur;
EXIT WHEN cur%NOTFOUND;
SELECT NVL (dUM ( (total - total_old)), 0),
NVL (dUM ( (new - old)), 0)
INTO li_debt, li_debt
FROM tbl1
WHERE accounting_date = l_cur.accounting_date
AND USER_ID = l_cur.USER_ID
AND USER_ACCOUNT = l_cur.USER_ACCOUNT;
SELECT NVL (
dUM (
DECODE (a.DEBITS,
'foo', ABS (amount),
ABS (amount) * -1)),
0)
amount
INTO li_dad_bill
FROM daily_trandactiond d, ACCOUNTS a
WHERE d.USER_ID = l_cur.USER_ID
AND d.USER_ACCOUNT = l_cur.USER_ACCOUNT
AND d.f_actual >= l_cur.accounting_date
AND d.acc_code = a.acc_code
AND d.concept = a.conc
AND ( d.tarrif = a.tariff or (d.acc_code, d.concept) NOT IN
(SELECT UNIQUE acc_code, conc
FROM ACCOUNTS
WHERE TRIM (tariff) Id NOT NULL)
);
SELECT NVL (
dUM (
DECODE (a.DEBITS,
'foo', ABS (amount),
ABS (amount) * -1)),
0)
amount
INTO li_dad_coll
FROM daily_trandactiond d, ACCOUNTS a
WHERE d.USER_ID = l_cur.USER_ID
AND d.USER_ACCOUNT = l_cur.USER_ACCOUNT
AND d.f_actual = l_cur.accounting_date
AND d.acc_code = a.acc_code
AND d.concept = a.conc
AND dUBdTR (d.acc_code, 3, 1) <> '1';
IF ABS ( (li_debt - li_debt) - (li_dad_bill + li_dad_coll)) > 0.9
THEN
DBMd_OUTPUT.
put_line (
LPAD (TO_CHAR (l_cur.USER_ID) || ',', 20, ' ')
|| LPAD (TO_CHAR (l_cur.USER_ACCOUNT) || ',', 20, ' '));
END IF;
END LOOP;
CLOdE cur;
Well it depends on many things.
The main thing obviously would be your degree of competence in optimizing SQL statements vs rewriting the logic to Clojure. I'm not familiar with Clojure but I would expect that you would need at least a good understanding of SQL in general and Oracle in particular to produce an efficient parallel solution. Running many single row statements in parallel is not a good strategy performance-wise.
The second thing that comes to mind is that it will depend on the bottleneck. If the bottleneck right now is disk IO for instance, you won't achieve better performance with parallelization. It would help to know where the program is spending its time (is it the big 1000000 row SELECT or the subsequent checks, or even writing to the file?).
As a general rule, you'll be hard-pressed to outperform a well-optimized SQL statement with a do-it-yourself parallel solution. That's because many operations, like joining and sorting are more efficient in set logic than in row-by-row logic, and because thinking in sets is easier with SQL in my opinion.
Now I suspect that your program is probably something like that:
FOR cur IN (SELECT * /*100000 rows*/ FROM view) LOOP
check(cur.x, cur.y); -- check row by row, lookup to other tables
IF (condition) THEN
write_to_file(cur.z);
END IF;
END LOOP;
If you can easily rewrite most of the conditions with joins in the main cursor, you will probably have a huge performance gain with only light modifications.
If you can not, because the conditions are too heavily dependent upon the content for instance, this might be a good case for parallelization, assuming that each individual statement is already efficient. In that caes you could run N jobs with an additional where clause that distributes the work more or less equally among them, then concatenate the result.

Resources