variable as column name in where clause in Oracle PL/SQL - sql

I am working on PL/SQL code where I need to perform a select query using variable as column name in where clause. Column names are stored in a table as varchar and I am using a loop to pass those column names to my select statement.
Please find sample code segment I am trying to run:
set serveroutput on;
declare
var varchar2(100);
counter number;
begin
var:='description';
select count(*)
into counter
from nodetable
where var like '%Ship%';
dbms_output.put_line(counter);
end;
Output:
anonymous block completed
0
However the result should be 86.
Oracle is comparing last condition as two string and not column=string.
Please let me know if this is even feasible in oracle or if there is a workaround for it.
Regards
Ankit

You have to use dynamic SQL, preferrably with bind-variables:
EXECUTE IMMEDIATE
'select count(*) from nodetable where '||var||' like :p1'
INTO counter
USING '%Ship%';

Try this
declare
var varchar2(100);
counter number;
begin
var:='description';
EXECUTE IMMEDIATE
'select count(*)
into counter
from nodetable
where '||var||' like ''%Ship%'' ';
dbms_output.put_line(counter);
end;
You need to be carefull with the colon's (').

I agreed with previous answer in implementation, but i strictly recommend you to change your technical requirements, because you can't use bind variables for this, and it's potential place for injection. For example, if someone will edit value in your table which stores column names, to something like that: "description = inject_function or description". Then your dynamic sql block will execute this statement:
select count(*) from nodetable where description = inject_function or description like '%Ship%
and example implementation of function
create function inject_function
return varchar2
is pragma autonomous_transaction;
begin
delete * from most_important_table;
commit;
return to_char(null);
exception when others then
rollback;
return to_char(null);
end;

Related

how to get multiple records by passing parameter to where clause in oracle pl/sql

table :
create table emp
(
E_ID number,
E_NAME varchar2(30)
);
select * from emp;
101 name1
102 name2
My code:
declare
v1 varchar2(30) := '101,102';
begin
for i in (select e_id,e_name
from emp
where e_id in (v1)) loop
dbms_output.put_line(i.e_id);
end loop;
end;
/
ISSUE:
Getting ORA -01722:invalid number
Please help to understand this issue and suggest me the solution.
It is syntax error.
E_ID is of number type and you are comparing it will v1 which is varchar2 type.
Welcome to SO. A great place to ask questions: I can see what you're trying to do. Syntactically, you'd be forgiven for trying to query your table using the "IN" clause, but as others have said, this can not be done where you have committed your numeric values into a varchar2. In anycase, an array or a collection, (even if you had created one) it isn't an easy option here. But you do have a variety of solutions open to you:
1/ Place your numbers into an Array and use a condition in your loop and check that e_id forms part of the your array. But in-elegant!
2/ Create a global temporary table and add your numbers in and add the table into your query, specify a join.
3/ Create some dynamic PL/SQL using a Ref Cursor. I've included an example for you below, using your table (emp) and values. In this case, you'd be able to build up your string according to the values you want to query. See below. The varchar2 string: sqlString can be manipulated however you want. Paste into a test-harness and see. Hope it helps
declare
type refCursor is ref cursor;
tableCursor refCursor;
emp_record emp%rowtype;
sqlString varchar2(200);
begin
-- Dynamic SQL statement with placeholder:
sqlString := 'SELECT * FROM emp WHERE e_id in
(101, 102)';
-- Open cursor:
open tableCursor for sqlString;
-- Fetch rows from result set one at a time:
loop
fetch tableCursor
into emp;
exit when tableCursor%notfound;
dbms_output.put_line(emp.e_id);
end loop;
-- Close cursor:
close tableCursor;
end;

PL/SQL equivalent of SELECT statement

What would be the PL/SQL equivalent of this SQL query:
SELECT * FROM table(OWNER.PACKAGE.get_exam('123456789'));
This is the Function that I am trying to call:
FUNCTION get_exam(id IN VARCHAR2)
RETURN ab_assign_v1
IS
CURSOR c_exams(cid VARCHAR2) IS
SELECT t_api_exam_v1(
sei.person_id, --unique id
l.description --loc description
)
FROM my_view sei
JOIN loc l
ON sei.loc_code = l.loc_code
v_collection ab_assign_v1;
BEGIN
OPEN c_exams(id);
FETCH c_exams BULK COLLECT INTO v_collection;
CLOSE c_exams;
RETURN v_collection;
EXCEPTION
WHEN OTHERS THEN
error_a1.raise_error(SQLCODE, SQLERRM);
END get_exam;
Hope this helps.
DECLARE
lv <COLLECTION_NAME>;
BEGIN
lv:= OWNER.PACKAGE.get_exam('123456789');
dbms_output.put_line(lv.COUNT);
END;
/
Assuming that you want to return the result of a function :
select owner.package.get_exam('123456789') from table
Your function returns a nested table type. You simply need to declare a variable of that type, and assign to it as you would if it were a scalar:
declare
l_coll your_collection_type;
begin
l_coll := OWNER.PACKAGE.get_exam('123456789');
end;
/
In this example your_collection_type is a placeholder for whatever object your function actually returns.
" I am getting this error: PLS-00201: identifier 'ab_assign_v1' must be declared "
ab_assign_v1 is the type used by your function. From the code posted in your revised question it seems that type is in the same schema which owns the package with the function. However your original pseudo-code prefixes the call with the schema name. So, putting two and two together, you need to revise the variable declaration to include the schema too. (You may need to grant EXECUTE on it too, if you haven't done this already).
declare
l_coll OWNER.ab_assign_v1;
begin
l_coll := OWNER.PACKAGE.get_exam('123456789');
end;
/

Test if anything was updated in dynamic query

I need to construct and execute an UPDATE statement dynamically. Then I need to test whether anything was updated at all. My code is as follows:
DECLARE
v_table_name text;
v_column_name text;
v_debug_flag boolean;
v_upd_stmt text;
BEGIN
select nm_table_name, replace(nm_table_name, 'CDB_', 'ID_')
into strict v_table_name, v_column_name
from m_entity e
where id=12;
v_upd_stmt := format('update %s set id_lock_usr =null, dt_lock=null where %s=$1 returning id_lock_usr',
v_table_name,
v_column_name);
execute v_upd_stmt using p_id;
END
How to know if anything was updated?
How to know if anything was updated ?
Various options. In a plpgsql function, you can check the special variable FOUND to see if the last SQL command affected any rows.
IF FOUND THEN ...
However, For dynamic queries with EXECUTE use GET DIAGNOSTICS instead. The manual:
Note in particular that EXECUTE changes the output of GET DIAGNOSTICS,
but does not change FOUND.
Related:
Dynamic SQL (EXECUTE) as condition for IF statement
Aside:
I see lingering problems with properly escaped identifiers (especially with upper case table names like you have), possibly even SQL injection. Fix with:
DECLARE
v_table_name text;
v_column_name text;
v_id_lock_usr integer; -- guessing the data type
i integer;
BEGIN
SELECT nm_table_name, replace(nm_table_name, 'CDB_', 'ID_')
INTO strict v_table_name, v_column_name
FROM m_entity e
WHERE id = 12;
EXECUTE format('UPDATE %I SET id_lock_usr = null, dt_lock = null
WHERE %I = $1 RETURNING id_lock_usr'
, v_table_name,
, v_column_name)
INTO v_id_lock_usr; -- to store the *single* result from RETURNING
GET DIAGNOSTICS i = ROW_COUNT;
IF i > 0 THEN
-- do something
END IF;
END
Note %I instead of %s.
In your case, if id_lock_usr returned by your query is NOT NULL (reliably), you might just test the result directly instead:
IF v_id_lock_usr IS NOT NULL ...
And you might want to schema-qualify table names to avoid ambiguities. But you must escape schema_name.table_name as two separate identifiers ..
Related:
Are PostgreSQL column names case-sensitive?
Define table and column names as arguments in a plpgsql function?
Table name as a PostgreSQL function parameter

PL/SQL query with parameter

I am familiar with MSSQL and using a parameter within the query, but I am not sure how I would do this within PL/SQL.
DECLARE
LSITEID NUMBER := 100001;
BEGIN
SELECT * from invoicehead ih
JOIN sitemaster sm on sm.SITEIID = ih.SITEIID
JOIN invoiceline il on il.invoiceIID = ih.invoiceIID
WHERE
ih.StartDate BETWEEN '2015-12-01' AND '2016-03-07'
AND SITEIID IN ( LSITEID)
END;
Right now I am testing this within Pl/SQL. But essentially I would be passing in the query with the parameter from MSSQL Linked Server OPENQuery.
How I can run the above query in PL/SQL with the parameter?
There is plenty of other resource for finding an answer, e.g. here (Tutorialspoint) or specifically here (plsql-tutorial). But perhaps I have missed your point.
To not remain on merely citing links, your query could look like this:
DECLARE
LSITEID integer;
BEGIN
LSITEID := 100001;
-- dostuff
END;
Two things to note: First, in a declare part (as I have learnt it) you should avoid assigning values. Second, if you intend to pass in different parameters you could/should use a procedure.
In PL/SQL you just use the name of the argument. In the following example, the argument is P_VALUE, note select statement says where dummy = p_value.
DECLARE
FUNCTION dummycount (p_value IN DUAL.dummy%TYPE)
RETURN INTEGER
AS
l_ret INTEGER;
BEGIN
SELECT COUNT (*) c
INTO l_ret
FROM DUAL
WHERE dummy = p_value;
RETURN l_ret;
END dummycount;
BEGIN
DBMS_OUTPUT.put_line ('A: ' || dummycount (p_value => 'A'));
DBMS_OUTPUT.put_line ('X: ' || dummycount (p_value => 'X'));
END;
This results in the following output:
A: 0
X: 1

How to output multiple rows from an existing table in a stored procedure using oracle sql developer?

I have an existing Customers table and want to output each row from this table using a stored procedure. There is no input criteria, just need to output all of the records.
The stored procedure should basically be equivalent to:
"SELECT C_ID, LAST, FIRST, DOB, DPHONE, EMAIL FROM customers;"
This seems simple but I can't figure it out. All my searches haven't worked out for this case.
How would one accomplish this?
EDIT: Answered my question below. Very simple.
In Oracle your options are:
1. Use a function and return a REF CURSOR
2. Use a procedure and use a REF CURSOR as an OUT parameter
3. Use a PIPELINED function
4. Use a function and return a collection.
read this documentation
Overview of Table Functions
see similar question here Return collection from packaged function for use in select
simple sample of such function
CREATE FUNCTION StockPivot(p refcur_pkg.refcur_t) RETURN TickerTypeSet
PIPELINED IS
out_rec TickerType := TickerType(NULL,NULL,NULL);
in_rec p%ROWTYPE;
BEGIN
LOOP
FETCH p INTO in_rec;
EXIT WHEN p%NOTFOUND;
-- first row
out_rec.ticker := in_rec.Ticker;
out_rec.PriceType := 'O';
out_rec.price := in_rec.OpenPrice;
PIPE ROW(out_rec);
-- second row
out_rec.PriceType := 'C';
out_rec.Price := in_rec.ClosePrice;
PIPE ROW(out_rec);
END LOOP;
CLOSE p;
RETURN;
END;
/
Nevermind! Just got it. It is pretty simple. I just did this:
CREATE OR REPLACE PROCEDURE CustomerReport AS
BEGIN
FOR cus IN (SELECT C_ID, LAST, FIRST, DOB, DPHONE, EMAIL FROM customers)
LOOP
dbms_output.put_line(cus.C_ID||' '||cus.FIRST||' '||cus.LAST||' '||cus.DOB||' '||cus.DPHONE||' '||cus.EMAIL);
END LOOP;
END;