Oracle Stored Procedure update rows if records exist - stored-procedures

I have following Oracle stored procedure. Either subcode or stylecode is passed in. Currently it is update rows if value is not null. I want to add logic so that make update only if rows are exists on the table, if not, I like to print message like "The subcode xxxx doesn't exists" or "The stylecode xxxx doesn't exists". I don't think merge into works here.
create or replace PROCEDURE "REMOVE_PRICES"
(
RESULT OUT VARCHAR2
, STYLECODE_ IN NUMBER
, SUBCODE_ IN NUMBER
) AS
BEGIN
IF (SUBCODE_ is null AND STYLECODE_ is null)
THEN
raise_application_error(-20005, 'ERROR: Please provide either SUBCODE or STYLECODE!');
END IF;
IF SUBCODE_ IS NOT NULL THEN
UPDATE prices
SET type = null
WHERE subcode=SUBCODE_;
RESULT := SQL%ROWCOUNT || ' price for subcode ' || SUBCODE_ || ' is removed';
ELSIF STYLECODE_ IS NOT NULL THEN
UPDATE prices
SET type = null
WHERE stylecode=STYLECODE_;
RESULT := SQL%ROWCOUNT || ' price for stylecode ' || STYLECODE_ || ' is removed';
END IF;
END REMOVE_PRICES;

You can't only choose to do the update if it will affect rows unless you query with the same conditions first, doing a count; and you could still have a race condition with other sessions that means the data could change between the select and the update, unless you lock the rows. Which all seems a bit excessive and costly.
You can just check the value of SQL%ROWCOUNT and show that message if it is zero, or your current message otherwise:
IF SUBCODE_ IS NOT NULL THEN
UPDATE prices
SET type = null
WHERE subcode=SUBCODE_;
IF SQL%ROWCOUNT = 0 then
RESULT := 'The subcode ' || SUBCODE || ' does not exist';
ELSE
RESULT := SQL%ROWCOUNT || ' price for subcode ' || SUBCODE_ || ' is removed';
END IF;
ELSIF STYLECODE_ IS NOT NULL THEN
UPDATE prices
SET type = null
WHERE stylecode=STYLECODE_;
IF SQL%ROWCOUNT = 0 then
RESULT := 'The stylecode ' || STYLECODE || ' does not exist';
ELSE
RESULT := SQL%ROWCOUNT || ' price for stylecode ' || STYLECODE_ || ' is removed';
END IF;
END IF;

Related

How to leave out empty values in queries?

I'm trying to place these values into one column, but sometimes there is no value for 'County'. How can I make it that It doesn't show up as Name_of_town, , Postcode?
SQL> select SuppName || ', ' || Street || ', ' || Town || ', ' || County || ', ' || PostCode AS "Supplier Address"
2 from Suppliers
3 ORDER BY 1;
This is what I currently get back from the query. (Example of ',' by itself is on the first query result)
Supplier Address
--------------------------------------------------------------------------------
Business Systems Ltd., 155 Stradleigh Place, London, , E10 6LL
Fastorder Stationers, Riverside View, Newport, Gwent, NP1 7XJ
I.T. Supplies (Wales), 3 Marlborough Ave., Cardiff, , CF1 1IT
Legal Services Ltd., Westway Road, London, , N8 8PA
Office Matters, 20 Berrick Street, Bridgend, Mid Glam., CF38 3BB
TKG Tools Ltd., 7 High Street, Swansea, , SA7 2WG
It seems like Oracle's NVL is the way to go. It will replavce null values with a string of your choosing. I am assuming when you say there is no value for County, that field contains null. Otherwice oracle REPLACE would work.
NVL
Try this:
select SuppName || ', ' || Street || ', ' || Town || ', ' || NVL(County, 'No_County') || ', ' || PostCode AS "Supplier Address"
from Suppliers
ORDER BY 1;

Using dynamic table name in db2

Currently in my project development need of generating the record count based on certain criteria where the table names are stored in separate table.For instance say xx table stores the table name under the column name is tableInfo.
I've written the stored procedure in such a way that
DECLARE FGCURSOR CURSOR FOR SELECT tableInfo FROM xx WHERE col1='PO';
OPEN FGCURSOR;
FETCH FROM FGCURSOR INTO FILEGROUPMEM;
WHILE SQLCODE <> 100
DO
SET COUNTVal = 'SELECT COUNT(*) FROM ' || FILEGROUPMEM || ' WHERE ICLS= ' || CLASS || ' AND IVEN= ' || VENDOR || ' AND ISTY= ' || STYLE || ' AND ICLR= ' || COLOR || ' AND ISIZ= ' || SIZE ;
IF(COUNTVal >= 1) THEN
RETURN 1;
END IF;
FETCH FROM FGCURSOR INTO FILEGROUPMEM;
END WHILE;
CLOSE FGCURSOR;
Getting the exception on executing the procedure saying that
Message: [SQL0420] Character in CAST argument not valid. Cause . . . .
. : A character in the argument for the CAST function was not
correct. Recovery . . . : Change the result data type to one that
recognizes the characters in the CAST argument, or change the argument
to contain a valid representation of a value for the result data type.
Try the request again.
This line is not correct:
SET COUNTVal = 'SELECT COUNT(*) FROM ' || FILEGROUPMEM || ' WHERE ICLS= ' || CLASS || ' AND IVEN= ' || VENDOR || ' AND ISTY= ' || STYLE || ' AND ICLR= ' || COLOR || ' AND ISIZ= ' || SIZE ;
To use it the way you are trying, you'd have to use a static SQL statement like so
exec sql SELECT COUNT(*) INTO :COUNTVal
FROM MYTBL
WHERE ICLS= :CLASS AND IVEN= :VENDOR AND ISTY= :STYLE
AND ICLR= :COLOR AND ISIZ= :SIZE;
However, while a static statement can use variables, the table name in the FROM clause can not be variable.
Thus you have to prepare and use a dynamic statement. Unfortunately, SELECT INTO can not be used in a dynamic statement. VALUES INTO can be used dynamically.
set wSqlStmt = 'VALUES ( SELECT COUNT(*) FROM ' || FILEGROUPMEM
|| ' WHERE ICLS= ' || CLASS || ' AND IVEN= '
|| VENDOR || ' AND ISTY= ' || STYLE || ' AND ICLR= '
|| COLOR || ' AND ISIZ= ' || SIZE ||') INTO ?';
exec sql PREPARE S1 FROM :wSqlStmt;
exec sql EXECUTE S1 USING COUNTVal;
WARNING the above code could be subject to SQL Injection attacks. To protect against SQL injection, dynamic SQL should use parameter markers instead of concatenating input directly to a statement. While you can't use a parameter marker for the table name, you can for the rest of the variables like so:
set wSqlStmt = 'VALUES ( SELECT COUNT(*) FROM ' || FILEGROUPMEM
|| ' WHERE ICLS= ? AND IVEN= ? '
|| ' AND ISTY= ? AND ICLR= ?'
|| ' AND ISIZ= ?) INTO ?';
exec SQL PREPARE S1 FROM :wSqlStmt;
exec SQL EXECUTE S1 USING :CLASS, :VENDOR, :STYLE, :COLOR, :SIZE, :COUNTVal;

Rails migration error: Unicode string return null value inside "execute >> SQL"

I'm trying to do the things below in migration.
Split "Name" as splitted_name array.
SET first_name as splitted_name[0].
SET last_name as splitted_name[-1] if the splitted_name[0] and splitted_name[-1] were differnt.
SET last_name as empty string if the splitted_name[0] and splitted_name[1] are same.
Here is the code.
class ConvertNameIntoFirstAndLastName < ActiveRecord::Migration
def up
execute <<-SQL
DO
$do$
DECLARE
u record;
BEGIN
FOR u IN SELECT * FROM users LOOP
DECLARE
splitted_name text[];
BEGIN
splitted_name := CASE WHEN u.name IS NULL THEN '{''}'
ELSE regexp_split_to_array(u.name, E'\\s+')
END;
UPDATE users
SET
first_name = splitted_name[0],
last_name = CASE WHEN splitted_name[0] = splitted_name[-1] THEN ''
ELSE splitted_name[-1]
END,
name = splitted_name[0] || ' ' || (CASE WHEN splitted_name[0] = splitted_name[-1] THEN '{''}'
ELSE splitted_name[-1]
END)
WHERE id = u.id;
END;
END LOOP;
END;
$do$;
SQL
end
def down
end
end
The problem is u.name always return null when the name is unicode characters. The database encoding is set as Unicode.
Here is the error message.
PG::NotNullViolation: ERROR: null value in column "name" violates not-null constraint
=> The name is actually not null but some Unicode string.
Do you have any idea what is the cause of this error and how to solve this?
Sorry, I was so naive that I didn't know that I should use splitted_name[1] for the first_name and there's no array[-1].
This code worked.
class ConvertNameIntoFirstAndLastName < ActiveRecord::Migration
def up
execute <<-SQL
DO
$do$
DECLARE
u record;
BEGIN
FOR u IN SELECT * FROM users LOOP
DECLARE
splitted_name text[];
BEGIN
splitted_name := regexp_split_to_array(regexp_replace(u.name, ' ', ' '), ' ');
UPDATE users
SET
first_name = splitted_name[1],
last_name = CASE WHEN splitted_name[1] = splitted_name[array_upper(splitted_name, 1)] THEN ''
ELSE splitted_name[array_upper(splitted_name, 1)]
END,
name = splitted_name[1] || ' ' || (CASE WHEN splitted_name[1] = splitted_name[array_upper(splitted_name, 1)] THEN ''
ELSE splitted_name[array_upper(splitted_name, 1)]
END)
WHERE id = u.id;
END;
END LOOP;
END;
$do$;
SQL
end
def down
end
end

How do I pass my variable value into my stored procedure

I created a variable className and I assigned values to it.
I have another procedure in oracle that sends emails to me.
How do I pass this value into header and body of my email?
VARIABLE className varchar2(30)
:classname := 0;
BEGIN
FOR i IN
(
SELECT CLASS_INSTANCE_COUNT , CLASS_NAME
FROM MODEL_CLASS_COUNTS
WHERE TRUNC(COUNT_DATETIME) = TRUNC(SYSDATE)
)
LOOP
IF i.CLASS_INSTANCE_COUNT = 0
THEN
:className := i.CLASS_NAME;
EMAIL('myemail#col.com', 'email header: &className is 0', 'body: count for &className is 0');
END IF;
END LOOP;
END;
/
My guess is that you don't want to have either a SQL*Plus variable or a substitution variable. I'm guessing that you just want
BEGIN
FOR i IN
(
SELECT CLASS_INSTANCE_COUNT , CLASS_NAME
FROM MODEL_CLASS_COUNTS
WHERE TRUNC(COUNT_DATETIME) = TRUNC(SYSDATE)
)
LOOP
IF i.CLASS_INSTANCE_COUNT = 0
THEN
EMAIL('myemail#col.com',
'email header: ' || i.class_name || ' is 0',
'body: count for ' || i.class_name || ' is 0');
END IF;
END LOOP;
END;

dynamic db2 query in stored procedure

Hi i had a stored procedure as below,
Create PROCEDURE my_car(
in diesel integer,
out milege integer)
P1: BEGIN
SET START_query = 'SELECT*';
SET FROM_CLAUSE = ' FROM car, diesel';
SET WHERE_CLAUSE = ' WHERE car.c1 = diesel.c2';
IF (diesel =0) THEN
SET WHERE_CLAUSE = +WHERE_CLAUSE+'AND car.diesel=0' ;
ELSE
SET WHERE_CLAUSE = +WHERE_CLAUSE+' AND car.diesel='+diesel ;
END IF;
END P1
But its throwing error as below.
DB2 SQL Error: SQLCODE=-402, SQLSTATE=42819, SQLERRMC=+, DRIVER=3.52.90
and if we use || instead of +
ie.,
SET WHERE_CLAUSE = WHERE_CLAUSE || 'AND car.diesel=0' ;
then its throwing error as below,
DB2 SQL Error: SQLCODE=-440, SQLSTATE=42884, SQLERRMC=||;FUNCTION, DRIVER=3.52.90
please help on this.
In DB2, || rather than + is used for string concatenation.
Also, diesel is a character string and you are comparing it to zero. If you want diesel to be a 0 or 1 value, you should set its type to integer. If it needs to be a string, you should compare it to '' or '0' or whatever makes sense.
I'm not sure what you meant by +V_WHERE_CLAUSE+, but your code should look something like this:
IF (diesel = '0') THEN
SET WHERE_CLAUSE = WHERE_CLAUSE || 'AND car.diesel=0';
ELSE
SET WHERE_CLAUSE = WHERE_CLAUSE || ' AND car.diesel=' || diesel;
END IF;
Or maybe just this:
SET WHERE_CLAUSE = WHERE_CLAUSE || ' AND car.diesel=' || diesel;

Resources