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;
Related
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;
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
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;
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;
I am very new to stored procedures and need some help.
I am trying to create a 'dynamic' stored procedure. When a parameter is NOT NULL then a certain part of the SQL should be added. This is what I have until now.
SELECT
TCId,
ENVId,
UId,
MTId,
TestSetName,
TestCaseName,
InterchangeSeqNo,
InstructionSeqNo,
TransactionSeqNo,
TestCaseDescription
FROM XML_TEST_SET_OVERVIEW
WHERE (ENVId = #MyENVId)
SELECT CASE #MyUId
WHEN IS NOT NULL THEN (AND UId = #MyUId)
END
SELECT CASE #MyMTId
WHEN IS NOT NULL THEN (AND MTId = #MyMTId)
END
SELECT CASE #MyTestSetName
WHEN IS NOT NULL THEN (AND TestSetName = #MyTestSetName)
END
SELECT CASE #MyTestCaseName
WHEN IS NOT NULL THEN (AND TestCaseName = #MyTestCaseName)
END
SELECT CASE #MyInterchangeSeqNo
WHEN IS NOT NULL THEN (AND InterchangeSeqNo = #MyInterchangeSeqNo)
END
SELECT CASE #MyInstructionSeqNo
WHEN IS NOT NULL THEN (AND InstructionSeqNo = #MyInstructionSeqNo)
END
SELECT CASE #MyTransactionSeqNo
WHEN IS NOT NULL THEN (AND TransactionSeqNo = #MyTransactionSeqNo)
END
ORDER BY ENVId, UId, MTId, TestSetName, TestCaseName, InterchangeSeqNo, InstructionSeqNo, TransactionSeqNo
Any help is appreciated
I have to guess at data types here, and I'll let you fill in the extra fluff.
DECLARE #sql NVARCHAR(MAX) = N'SELECT ...
FROM dbo.XML_TEST_SET_OVERVIEW -- always use schema prefix
WHERE ENVId = #MyENVId'
+ CASE WHEN #MyUId IS NOT NULL THEN
N' AND UId = #MyUId' ELSE '' END
+ CASE WHEN #MyMTId IS NOT NULL THEN
N' AND MTId = #MyMTId' ELSE '' END
+ CASE WHEN #MyTestSetName IS NOT NULL THEN
N' AND TestSetName = #MyTestSetName' ELSE '' END
...
+ CASE WHEN #MyTransactionSeqNo IS NOT NULL THEN
N' AND TransactionSeqNo = #MyTransactionSeqNo' ELSE '' END
+ N' ORDER BY ENVId, UId, ...;';
EXEC sp_executesql #sql,
N'#MyENVId INT, #MyUId INT, #MyMTId INT,
#MyTestSetName NVARCHAR(32), ... , #MyTransactionSeqNo INT',
#MyENVId, #MyUId, #MyMTId, #MyTestSetName, ... , #MyTransactioNSeqNo;