Trouble with a cursor loop in an SQL event - mariadb

My current code:
CREATE EVENT lifeinsurance_reset
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP()
ON COMPLETION NOT PRESERVE
ENABLE
BEGIN
DECLARE Cur = CURSOR FOR
SELECT `char_id` FROM `life_insurance` WHERE `renewal_date` <= CURRENT_TIMESTAMP()
OPEN Cur
LOOP
UPDATE `characters` SET `bank` = `bank` - 25000 WHERE `id` = Cur.char_id;
UPDATE `life_insurance` SET `renewal_date` = DATE_ADD(NOW(), INTERVAL 1 MONTH) WHERE `char_id` = Cur.char_id
END LOOP
CLOSE Cur
END
Which results in a syntax error:
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 'BEGIN
DECLARE Cur = CURSOR FOR
SELECT `char_id` FROM `life_insurance...' at line 7
I've done a lot of google searches regarding this, a lot of which stated I needed to use a delimiter. Upon trying that, with the following code, I get an error as well.
delimiter $$
CREATE EVENT lifeinsurance_reset
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP()
ON COMPLETION NOT PRESERVE
ENABLE
BEGIN
DECLARE Cur = CURSOR FOR
SELECT `char_id` FROM `life_insurance` WHERE `renewal_date` <= CURRENT_TIMESTAMP()
OPEN Cur
LOOP
UPDATE `characters` SET `bank` = `bank` - 25000 WHERE `id` = Cur.char_id;
UPDATE `life_insurance` SET `renewal_date` = DATE_ADD(NOW(), INTERVAL 1 MONTH) WHERE `char_id` = Cur.char_id
END LOOP
CLOSE Cur
END$$
delimiter;
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 'delimiter $$
CREATE EVENT lifeinsurance_reset
ON SCHEDULE EVERY 1 HOUR
STARTS...' at line 1
I'm not very well-versed in SQL, so any help would be appreciated.
For context, I'm trying to loop through all the rows in an insurance table for a game, if the renewal date is past current timestamp, it'll auto deduct money from the characters bank and reset the renewal date to a month from now.

Related

How do I reset a sequence in Oracle APEX or fill my PK automatically without sequence and trigger, starting from number 1 every time i delete my data?

i have this table
TABLE "KEYWORD_RSLT"
( "ID" NUMBER PRIMARY KEY,
"SESSION_MONTH" VARCHAR2(40) NOT NULL ENABLE,
"PATIENT_NAME" VARCHAR2(50)
and i have this procedure that imports data to my table KEYWORD_RSLT.
create or replace PROCEDURE "PR_KEYWORD_SEARCH" (v_patient_id NUMBER, v_keyWord varchar2)
IS
BEGIN
delete from KEYWORD_RSLT;
insert into KEYWORD_RSLT (SESSION_MONTH, PATIENT_NAME)
select distinct
to_char(s.SESSION_DATE, 'MM-YYYY') as SESSION_MONTH,
p.FIRST_NAME ||' '||p.LAST_NAME as PATIENT_NAME
from SESSIONS s,
CLIENTS p
where s.CLIENTS_ID = p.ID
and (s.CRITICAL_POINT like LOWER ('%'||v_keyWord||'%') and s.CLIENTS_ID = v_patient_id
or s.ACTIONS like LOWER ('%'||v_keyWord||'%') and s.CLIENTS_ID = v_patient_id);
END PR_KEYWORD_SEARCH;
I want my primary key "ID" to take automatically the next available number starting from 1, but when my procedure deletes all data from this table, i want to start again from 1.
I tried with sequence ("SQ_KEYWORD_RSLT_INCREAMENT") and trigger but i can not reset this sequence from a new procedure using this code:
alter sequence SQ_KEYWORD_RSLT_INCREAMENT restart start with 1;
How can i fill my ID automatically from the beginning every time i delete all the data?
You say i can not reset this sequence but you don't say why so I'm assuming that you got an error. It is not possible to execute ddl statements in pl/sql directly, but it can be done using EXECUTE IMMEDIATE.
CREATE SEQUENCE koen_s START WITH 1;
Sequence KOEN_S created.
SELECT koen_s.NEXTVAL FROM DUAL;
NEXTVAL
----------
1
BEGIN
EXECUTE IMMEDIATE 'ALTER SEQUENCE koen_s RESTART START WITH 1';
END;
/
PL/SQL procedure successfully completed.
SELECT koen_s.NEXTVAL FROM DUAL;
NEXTVAL
----------
1

Creating a Database Trigger that checks if more than one record was added on a date?

CREATE OR REPLACE TRIGGER POSITION_NUMBER
BEFORE UPDATE OR INSERT OR DELETE ON APPLIES
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
NUMBER_OF_POSITIONS NUMBER;
BEGIN
SELECT count(pnumber) INTO NUMBER_OF_POSITIONS
FROM APPLIES WHERE anumber = :NEW.anumber;
IF( NUMBER_OF_POSITIONS > 2 AND count(APPDATE) > 2 )
THEN
RAISE_APPLICATION_ERROR(-20000,'an Employee cannot apply for
more than two positions');
END IF;
END;
/
Im attemtping to create a trigger that goes off if an Applicant applys for more than two Positions on the Same Day, but im not sure how i would implement the Date side of it. Below is the set of relational Schemeas
You can use the TRUNC function to remove the time portion and then see if the application date matches today's date, regardless of time.
Also, there is no need for the autonomous transaction pragma. You are not executing any DML.
CREATE OR REPLACE TRIGGER position_number
BEFORE UPDATE OR INSERT OR DELETE
ON applies
DECLARE
number_of_positions NUMBER;
BEGIN
SELECT COUNT (pnumber)
INTO number_of_positions
FROM applies
WHERE anumber = :new.anumber AND TRUNC (appdate) = TRUNC (SYSDATE);
IF number_of_positions > 2
THEN
raise_application_error (
-20000,
'An Employee cannot apply for more than two positions on the same day');
END IF;
END;
/

Comparing CLOB using dbms_lob.compare, not geting the result I wanted

Not use to use dbms_lob.compare, so the update works fine, but when it reach the IF statement look like only do the same thing when it suppost to alternate like u can see bellow
My PL/SQL code:
CREATE OR REPLACE PROCEDURE teste
IS
aux CLOB;
cnt NUMBER := 0;
cnt1 NUMBER := 0;
BEGIN
FOR rec IN (SELECT xxxx)
LOOP
aux := rec.VALUE;
UPDATE db
SET VALUE = TO_CLOB(deletexml(
xmltype(VALUE),
'//*:getPaymentDetailsResponse/*:Payment/*:childs[./*:status[text()="Failed"]]'
))
WHERE id=rec.gb_ID;
--Teste
IF DBMS_LOB.compare(rec.VALUE, aux) = 0 THEN
DBMS_OUTPUT.put_line('### ORDERS NOT CHANGED ###');
cnt1 := cnt1 + 1; --count orders without any change
DBMS_OUTPUT.put_line(cnt1 || '- ' || rec.ORDER_PUBLIC_ID);
ELSE
DBMS_OUTPUT.put_line('### ORDERS CHANGED ###');
cnt := cnt +1; --count changed orders
DBMS_OUTPUT.put_line(cnt || '- ' || rec.ORDER_PUBLIC_ID);
END IF;
END LOOP;
-- Print count results
DBMS_OUTPUT.put_line('Orders without changing: '|| cnt1 || ' orders.');
DBMS_OUTPUT.put_line('Orders updated: '|| cnt || ' orders.');
END;
/
This is what i am currently getting:
ORDERS NOT CHANGED
1- 160000
ORDERS NOT CHANGED
2- 160000
ORDERS NOT CHANGED
3- 160313
ORDERS NOT CHANGED
4- 160313
What I want to happen:
ORDERS CHANGED
1- 160000
ORDERS NOT CHANGED
2- 160000
ORDERS CHANGED
3- 160313
ORDERS NOT CHANGED
4- 160313
When you create a cursor it is a snapshot of the data as it exists at the time you run the select. Any table changes that are made during the execution of the cursor do not get reflected in the cursor. see Do database cursors pick up changes to the underlying data? for more details.
So you update the base table for your cursor (I assume as you don't show the actual select)
UPDATE db
SET VALUE = TO_CLOB(deletexml(
xmltype(VALUE),
'//*:getPaymentDetailsResponse/*:Payment/*:childs[./*:status[text()="Failed"]]'
))
WHERE id=rec.gb_ID;
Then you do a compare using the details in your cursor
IF DBMS_LOB.compare(rec.VALUE, aux) = 0 THEN
As the cursor is a snapshot this will always come back as Not Changed, you would have to re-select the value from your base table to or use SQL%ROWCOUNT to check if your update statment affected any rows (see https://community.oracle.com/thread/2370954?start=0&tstart=0) for details of how to use this.

PL/SQL Execute immediate create table and insert data into it

Let's have a look on my source code:
CREATE OR REPLACE PROCEDURE MAKE_COPY_OF_CLASSROOMS AUTHID CURRENT_USER AS
TYPE classrooms_table_type IS TABLE OF classrooms%ROWTYPE INDEX BY PLS_INTEGER;
classrooms_backup classrooms_table_type;
CURSOR classrooms_cursor IS
SELECT *
FROM classrooms
WHERE year = 1
ORDER BY name;
v_rowcnt PLS_INTEGER := 0;
BEGIN
OPEN classrooms_cursor;
FETCH classrooms_cursor
BULK COLLECT INTO classrooms_backup;
CLOSE classrooms_cursor;
EXECUTE IMMEDIATE 'CREATE TABLE classrooms_copy AS (SELECT * FROM classrooms WHERE 1 = 2)';
--COPY ALL STORED DATA FROM classrooms_backup TO classrooms_copy
END MAKE_COPY_OF_classrooms;
I'm stucked for hours on trying to insert data from "classrooms_backup" into the table "classrooms_copy", which is created by EXECUTE IMMEDIATE command. It's necessary to create table "classrooms_copy" via EXECUTE IMMEDIATE command. I tried to create another EXECUTE command with for loop in it:
EXECUTE IMMEDIATE 'FOR i IN classrooms_backup.FIRST..classrooms_backup.LAST LOOP
INSERT INTO classrooms_copy(id,room_id,year,name)
VALUES(classrooms_backup(i).id,classrooms_backup(i).room_id,classrooms_backup(i).year,classrooms_backup(i).name);
END LOOP;';
But it's road to the hell. I'm retrieving an invalid SQL statement error.
Thanks for your help!
There's no need for much PL/SQL here. Also, try to avoid the keyword CURSOR - there's almost always a better way to do it.
create or replace procedure make_copy_of_classrooms authid current_user as
begin
execute immediate '
create table classrooms_copy as
select *
from classrooms
where year = 1
order by name
';
end make_copy_of_classrooms;
/

create dynamic table within procedure?

I am trying to create dynamic table within procedure but i am getting error please
tell me whats the error
CREATE OR REPLACE PROCEDURE check_sms_bundle_25 (
MON VARCHAR2,
YEAR_P VARCHAR2 DEFAULT TO_CHAR (SYSDATE, 'YY'),
QUARTER VARCHAR2,
TYPE VARCHAR2 DEFAULT 'NEW')
IS
BEGIN
IF UPPER (QUARTER) = 1
THEN
EXECUTE IMMEDIATE 'BEGIN CREATE OR REPLACE TABLE (''SMS_Bundle_25_'''|| UPPER (MON)|| '''_Q'''|| UPPER (QUARTER)|| '||''_''||'''|| UPPER (YEAR_P)|| ''')
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = ''1-aug-2014'' AND ohstatus = ''IN''
END';
END IF;
END;
The error msg is
ORA-06550: line 2, column 4: PLS-00103: Encountered the symbol
"CREATE" when expecting one of the following:
begin case declare exit for goto if loop mod null pragma raise
return select update while with << close current delete
fetch lock insert open rollback savepoint set sql execute commit
forall merge pipe ORA-06512: at "FI_SDINE.CHECK_SMS_BUNDLE_25", line 9
ORA-06512: at line 1
As well as 'or replace' not being valid as part of the create table syntax, you're enclosing the DDL statement inside another anonymous PL/SQL block, you're trying to put the table name inside parentheses, and you're including quote marks - which are not allowed in an (unquoted) object identifier. So if you pass in argukments AUG, 14, 1 NEW then your dynamic statement is trying to run:
BEGIN CREATE OR REPLACE TABLE ('SMS_Bundle_25_'AUG'_Q'1||'_'||'14')
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = '1-aug-2014' AND ohstatus = 'IN'
END
The BEGIN/END make is a block and the DDL statement isn't valid in PL/SQL; that's why you're having to use execute immediate in the first place. If you change your construction to:
EXECUTE IMMEDIATE 'CREATE TABLE SMS_Bundle_25_'|| UPPER (MON)
|| '_Q'|| UPPER (QUARTER) ||'_' || UPPER (YEAR_P)|| '
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = ''1-aug-2014'' AND ohstatus = ''IN''');
you'd then be trying to run:
CREATE TABLE SMS_Bundle_25_AUG_Q1_14
AS
SELECT customer_id, otxact
FROM ordertrailer INNER JOIN orderhdr_all ON ohxact = otxact
WHERE sncode = 343 AND ohentdate = '1-aug-2014' AND ohstatus = 'IN'
which at least looks more viable, assuming the tables you're selecting from exist. No idea why you're passing the year and quarter numbers as strings or applying upper() to them. And presumably you really want the select to be filtered on the same year and month as the table name.
It's useful to display the command you're trying to execute, for example with dbms_output, to see exactly what you've created; and you can then also run that manually to see where it's going wrong more clearly.
But as noted in comments, it's unusual to create objects from a procedure like this. Your schema should usually be static, not modified on the fly. You won't be able to refer to this table from other code unless that is also being built dynamically. You seem to be creating a table as a snapshot of the other tables; a view or a materialised view might be more appropriate. But I'm not really sure why you're doing this so it's not entirely clear what you should be doing instead.

Resources