how to pass current row into a oracle user defined function - oracle11g

I want to create an inline oracle function which will refer the column values of the current row being fetched and to return a value as per my conditions. I tried the same by passing ROWID into function and in the function body current row will be fetched using ROWID and manipulate the values.
CREATE FUNCTION CHECK_STATUS(P_ROWID) RETURN VARCHAR2 IS
V_ROW MY_TBL%TYPE
BEGIN
SELECT * INTO V_ROW FROM MY_TBL WHERE ROWID=P_ROWID;
IF V_ROW.COL1 IS NOT NULL AND
V_ROW.COL2 IS NOT NULL AND
V_ROW.COL3 IS NOT NULL THEN
RETURN 'OK';
ELSE
RETURN 'INCOMPLETE';
END IF;
END;
This function is further called at various places as
SELECT A.*,CHECK_STATUS(ROWID) FROM MY_TBL A;
it works but slows down the query badly over a thousand records because on each fetch of rows function will again do a select query for getting the column values. I know that this can also be done as
CREATE FUNCTION CHECK_STATUS(COL1,COL2,COL3) RETURN VARCHAR2 IS
here the problem is that i want to nearly check 9 column values in the function and it looks odd to write the column name during every call of the function. I doubt is there any method that the function can refer the current row being fetched without receiving ROWID/ COLUMNS through parameter something like (THIS.COL1,THIS.COL2,THIS.COL3)

You could add a virtual column for this, using a case expression, and not have a function at all:
alter table my_tbl add (status generated always as
(case when column1 is null or column2 is null or column3 is null
-- etc., all 9 columns or whatever else you want to check
then 'INCOMPLETE' else 'OK' end) virtual);
You can then query it with:
select column1, column2, ..., status from my_tbl
You can also add an index on the virtual column if that's useful for how you'll use it.
You could still use a (deterministic) function if you wanted to, with all the column values passed in - the 'looks odd' part would be hidden in the DDL, so queries wouldn't see it. It doesn't sound like using a function would add much in this case though.

Related

MariaDB Stored Procedure store paramters for update

I am trying to write a MariaDB stored procedure.
Due to SQL_SAFE_UPDATES, it is required to use the ID column to use in the WHERE clause for updates. Due to this, what is the normal approach to also select a value from one of the other columns? I do not want to have multiple SELECT statements as it seems inefficient and room for error because they could return values from different rows.
I would like to store my first select statement
SELECT id, sequence FROM RECORDSEQUENCE WHERE SEQTABLE = SeqTable;
In the following two parameters #id, #seq from two seperate columns in the above query and use them in the UPDATE statement as well as the IF statement.
CREATE DEFINER=`sd`#`%` PROCEDURE `SD_GenerateNextRecordSequence`(IN SeqTable int)
BEGIN
SELECT id, sequence FROM RECORDSEQUENCE WHERE SEQTABLE = SeqTable;
IF (#seq IS NOT NULL) THEN
SET #NEXTSEQ := #seq+1;
UPDATE RECORDSEQUENCE SET RECORDSEQUENCE = #NEXTSEQ WHERE id = #id;
ELSE
SET #NEXTSEQ := 100;
INSERT INTO RECORDSEQUENCE (RECORDSEQUENCE,SEQTABLE) VALUES (#NEXTSEQ,SeqTable);
END IF;
SELECT #NEXTSEQ as SEQUENCE;
END

expression is of wrong type for function

CREATE OR REPLACE FUNCTION k_w_b_salary(k IN NUMBER, b IN BOOLEAN)
RETURN EMP.ENAME%TYPE IS
names name_table;
BEGIN
IF (b = true) THEN
SELECT ENAME BULK COLLECT INTO names
FROM
(SELECT *
FROM EMP
ORDER BY SAL ASC)
WHERE ROWNUM <= k;
RETURN names;
ELSIF (b = false) THEN
SELECT ENAME BULK COLLECT INTO names
FROM
(SELECT *
FROM EMP
ORDER BY SAL DESC)
WHERE ROWNUM <= k;
RETURN names;
END IF;
END;
And I get this error:
12/9 PL/SQL: Statement ignored
12/16 PLS-00382: expression is of wrong type
20/9 PL/SQL: Statement ignored
20/16 PLS-00382: expression is of wrong type
I have this function that tries to find the best/worst paid employees. But i get the above error.
I think it's something to do with the ROWNUM but I am not sure.
I think the lines the error points out are not the lines with the error.
I had this function writen differently and the lines in the error where pointing to the ROWNUM <= k lines.
I have tried putting a fixed number there (<= 3) for example and I got the same error.
I have no idea what else to try, i can't really understand why this is not working.
It's not obvious to me why this is not working. I think it should work fine but obviously it dousen't.
The code for the table i use is :
CREATE OR REPLACE TYPE name_table IS TABLE OF VARCHAR2(10);
Any help is appreciated!
In the function declaration, you said
RETURN EMP.ENAME%TYPE
I assume the data type of column ENAME in table EMP is some sort of string (VARCHAR2(40) or similar) - right?
In the declarations section, you declare a variable names of data type name_table. You didn't show us the definition of the name_table type (that must be given outside the function, not in it); we can probably assume it is a nested table of some sort. Right? [EDIT - I take that back; you did show us your definition of name_table, at the end of your question.]
In the end, your function returns names. Which is of type name_table. But you said the function returns something else: EMP.ENAME%TYPE. In particular, you said the function returns a scalar data type, but you are returning a collection.
This will not work even if the collection has a single element. A table with a single "record" is not the same data type as the "record" itself - even if an actual table has a single "record" in it.
(And, much more so, when the table has three records in it!)
Rather: It seems that you want a table function: one that returns a table of things. If so, then declare the function that way. Perhaps you want the function to
RETURN NAME_TABLE
(at the top, in the function declaration)

Modify a column to NULL - Oracle

I have a table named CUSTOMER, with few columns. One of them is Customer_ID.
Initially Customer_ID column WILL NOT accept NULL values.
I've made some changes from code level, so that Customer_ID column will accept NULL values by default.
Now my requirement is that, I need to again make this column to accept NULL values.
For this I've added executing the below query:
ALTER TABLE Customer MODIFY Customer_ID nvarchar2(20) NULL
I'm getting the following error:
ORA-01451 error, the column already allows null entries so
therefore cannot be modified
This is because already I've made the Customer_ID column to accept NULL values.
Is there a way to check if the column will accept NULL values before executing the above query...??
You can use the column NULLABLE in USER_TAB_COLUMNS. This tells you whether the column allows nulls using a binary Y/N flag.
If you wanted to put this in a script you could do something like:
declare
l_null user_tab_columns.nullable%type;
begin
select nullable into l_null
from user_tab_columns
where table_name = 'CUSTOMER'
and column_name = 'CUSTOMER_ID';
if l_null = 'N' then
execute immediate 'ALTER TABLE Customer
MODIFY (Customer_ID nvarchar2(20) NULL)';
end if;
end;
It's best not to use dynamic SQL in order to alter tables. Do it manually and be sure to double check everything first.
Or you can just ignore the error:
declare
already_null exception;
pragma exception_init (already_null , -01451);
begin
execute immediate 'alter table <TABLE> modify(<COLUMN> null)';
exception when already_null then null;
end;
/
You might encounter this error when you have previously provided a DEFAULT ON NULL value for the NOT NULL column.
If this is the case, to make the column nullable, you must also reset its default value to NULL when you modify its nullability constraint.
eg:
DEFINE table_name = your_table_name_here
DEFINE column_name = your_column_name_here;
ALTER TABLE &table_name
MODIFY (
&column_name
DEFAULT NULL
NULL
);
I did something like this, it worked fine.
Try to execute query, if any error occurs, catch SQLException.
try {
stmt.execute("ALTER TABLE Customer MODIFY Customer_ID nvarchar2(20) NULL");
} catch (SQLException sqe) {
Logger("Column to be modified to NULL is already NULL : " + sqe);
}
Is this correct way of doing?
To modify the constraints of an existing table
for example... add not null constraint to a column.
Then follow the given steps:
1) Select the table in which you want to modify changes.
2) Click on Actions.. ---> select column ----> add.
3) Now give the column name, datatype, size, etc. and click ok.
4) You will see that the column is added to the table.
5) Now click on Edit button lying on the left side of Actions button.
6) Then you will get various table modifying options.
7) Select the column from the list.
8) Select the particular column in which you want to give not null.
9) Select Cannot be null from column properties.
10) That's it.

PL/SQL - LAST_VALUE return more than one row?

I am doing an school assigment where I need to get the last value of "code" so I can then insert next row with this code incremented. I tried to pull it out this way.
DECLARE
v_last_code f_shifts.code%TYPE;
BEGIN
SELECT LAST_VALUE(code) OVER (ORDER BY code)
INTO v_last_code
FROM f_shifts;
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
However I get ORA-01422: exact fetch returns more than one requested number of rows
and I have no idea why and how can a last_value be more than one row
Thanks !
You can use a nested table like this.
DECLARE
v_last_code f_shifts.code%TYPE;
TYPE t_tbl IS TABLE OF f_shifts.code%TYPE;
-- Above line creates the nested table type of the required type.
v_tbl T_TBL;
-- Above line creates the nested table variable.
BEGIN
SELECT code
BULK COLLECT INTO v_tbl -- collects all the values, ordered, into the nested table
FROM f_shifts
ORDER BY code;
v_last_code = v_tbl(v_tbl.LAST); -- gets the last values and puts into the variable
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
/

MySQL Changing Order Depending On Contents of a Column

I have a MySQL table Page with 2 columns: PageID and OrderByMethod.
I also then have a Data table with lots of columns including PageID (the Page the data is on), DataName, and DataDate.
I want OrderByMethod to have one of three entries: Most Recent Data First, Most Recent Data Last, and Alphabetically.
Is there a way for me to tack an "ORDER BY" clause to the end of this query that will vary its ordering method based on the contents of the "OrderByMethod" column? For example, in this query, I would want to have the ORDER BY clause contain whatever ordering rule is stored in Page 1's OrderByMethod column.
GET * FROM `Data` WHERE `Data`.`PageID`=1 ORDER BY xxxxxx;
Maybe a SELECT clause in the ORDER BY clause? I'm not sure how that would work though.
Thanks!
select Data.*
from Data
inner join Page on (Data.PageID=Page.PageID)
where Data.PageID=1
order by
if(Page.OrderByMethod='Most Recent Data First', now()-DataDate,
if(Page.OrderByMethod='Most Recent Data Last', DataDate-now(), DataName)
);
You can probably do this with the IF syntax to generate a column that you can then order by.
SELECT *, IF(Page.OrderBy = 'Alphabetically', Data.DataName, IF(Page.OrderBy = 'Most Recent Data First', NOW() - Data.DataDate, Data.DataDate - NOW())) AS OrderColumn
FROM Data
INNER JOIN Page ON Data.PageID = Page.PageID
WHERE Page.PageID = 1
ORDER BY OrderColumn
The direction of the ordering is determined in the calculation of the data instead of specifying a direction in the ORDER BY
Can you just append the order by clause to the select statement and rebind the table on postback?
If you want to use the content of the column in Page table as an expression in ORDER BY you have to do it using prepared statements. Let say, you store in OrderByMethod something like "field1 DESC, field2 ASC" and you want this string to be used as it is:
SET #order_by =(SELECT OrderByMethod FROM Page WHERE id = [value]);
SET #qr = CONCAT(your original query,' ORDER BY ', #order_by);
PREPARE stmt FROM #qr;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
If you want the result set to be sorted based on the value of OrderByMethod , you can use IF as it was already mentioned by others, or CASE :
...
ORDER BY
CASE OrderByMethod
WHEN 'val1' THEN field_name1
WHEN 'val2' THEN field_name2
....etc
END

Resources