Oracle PL/SQL. SELECT with AND condicionated [duplicate] - oracle

This question already has answers here:
PL/SQL optional where [duplicate]
(2 answers)
Stored Procedure with optional "WHERE" parameters
(6 answers)
PL/SQL Optional parameters in where [duplicate]
(2 answers)
Closed 11 months ago.
I want to do a Query like this:
v_name VARCHAR2(60); SELECT * FROM book WHERE name = v_name
But if "v_name" is NULL, the program must be:
SELECT * FROM book
I don't wan't to set the filter in the WHERE condition. If v_name is NULL i dont want to get all the recodrs with name=NULL, but ALL the records.
I have tried this:
SELECT * FROM book WHERE name (CASE WHEN v_name IS NULL THEN name ELSE v_name END);
But it doesn't work.
Thanks.

One way to match null with all books when v_name is null or match only a book (assuming NAME is unique) when v_name is not null is to do it by typing fewer characters:
select *
from BOOK
where v_name is null
or NAME = v_name
But for commercial apps, I think doing things like this is unwise. It's reasonable to force the user to think.

Here is your needed query structure;
SELECT
*
FROM
book
WHERE (v_name is null) or
(v_name is not null and name = v_name);

I think NVL function can help you here -
v_name VARCHAR2(60);
SELECT *
FROM book
WHERE name = NVL(v_name, name);
So, If v_name would be NULL, column would compare with itself and fulfills your purpose.

Related

LISTAGG 4000 Character Limit - Result of string concatenation is too long [duplicate]

This question already has answers here:
Oracle - convert column values to comma separated values as CLOB without using XMLAGG
(1 answer)
ORA-64451: Conversion of special character to escaped character failed
(1 answer)
Closed 8 months ago.
select t.name, listagg(t.text)
from user_source t
group by t.name;
I am trying to execute the code above but since varchar2 is limited by 4000 chars it throws error. I tried to convert listagg to xml but I could not solve the
ORA-64451: Conversion of special character to escaped character failed.
error. I also tried the answers from other posts from various websites including stackoverflow.
I do not want to truncate the string, also I can't change MAX_STRING_SIZE parameter.
This example below throws ORA-64451 as well. I tried but could not solve the problem.
select rtrim(
xmlagg(
xmlelement(e, to_clob(t.TEXT), '; ').extract('//text()')
).GetClobVal(),
',')
from user_source t;
The best solution I know is posted somewhere in the Internet... You could probably just google for it. It basically consist of few steps:
Creating a collection type to store each text value to concatenate
create or replace type string_array_t as table of VARCHAR2(4000);
Creating a PL/SQL function which takes string_array_t as parameter and returns concatenated text as CLOB:
create or replace function
string_array2clob(
p_string_array string_array_t
,p_delimiter varchar2 default ','
) RETURN CLOB IS
v_string CLOB;
BEGIN
-- inside is a loop over p_string_array to concatenate all elements
--
-- below is just a draft because in real you should use a while loop
-- to handle sparse collection and you should put some checks to handle not initialized collection
-- and other important cases
-- furthermore it's better to create additional varchar2 variable as a buffer
-- and convert that buffer to clob when it's full for a better performance
for indx in p_string_array.first..p_string_array.last loop
v_string := v_string || to_clob(p_string_array(indx) || p_delimiter);
end loop;
RETURN substr(v_string, 1, nvl(length(v_string),0) - nvl(length(p_delimiter),0));
END string_array2clob;
/
Aggregate query as usual but using cast and collect instead of listagg and at the end convert it to clob with function from step above:
select t.name, string_array2clob(cast(collect(t.text order by t.line) as string_array_t ), p_delimiter => chr(10)) as text
from user_source t
group by t.name;
If your query is not just an example of concept and really you're trying to get a source of some object in database, then you should read about dbms_metadata.get_ddl function. It's made for it.

Oracle RETURNING from a INSERT .. SELECT [duplicate]

This question already has answers here:
Is it possible to return the Primary Key on an Insert as select statement - Oracle?
(2 answers)
Closed 11 months ago.
Is it possible to insert a row into a table from a SELECT statement (as opposed to VALUES syntax) and use the RETURNING clause to retrieve a column value?
Using the SELECT syntax fails with ORA-00933: SQL command not properly ended:
DECLARE
l_id users.id%TYPE;
BEGIN
INSERT INTO users (id, name)
SELECT users_seq.nextval, 'foo'
FROM DUAL
RETURNING id INTO l_id;
END;
/
It works fine with the VALUES syntax of course:
DECLARE
l_id users.id%TYPE;
BEGIN
INSERT INTO users (id, name)
VALUES (users_seq.nextval, 'foo')
RETURNING id INTO l_id;
END;
/
According to the documentation here https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/INSERT.html#GUID-903F8043-0254-4EE9-ACC1-CB8AC0AF3423 (search for single_table_insert) it is not supported in normal SQL, and I believe not in PL/SQL either.

Function returning varchar2 gives wrong result [duplicate]

This question already has answers here:
PL/SQL rewrite concatenated query with 'IN' clause
(2 answers)
Closed 9 years ago.
I want to build query dynamically as per criterias selected from gui. Here is my oracle package,
CREATE OR REPLACE PACKAGE TestPkg
AS
g_lastnamelist VARCHAR2(50);
FUNCTION getLastName return VARCHAR2;
FUNCTION buildQuery(p_lastnamelist VARCHAR2);
END;
CREATE OR REPLACE PACKAGE BODY TestPkg
AS
FUNCTION getLastName return VARCHAR2
IS
BEGIN
RETURN replace(g_lastnamelist, '''', '');
END;
FUNCTION buildQuery(p_lastnamelist VARCHAR2);
IS
m_query varchar2(1000);
BEGIN
g_lastnamelist := p_lastnamelist;
m_query := 'SELECT * FROM emp WHERE last_name IN(TestPkg.getLastName)';
END;
END;
here if i use 'SELECT * FROM emp WHERE last_name IN('||p_lastnamelist||')'; then it returns me correct record but if i use 'SELECT * FROM emp WHERE last_name IN(TestPkg.getLastName)'; like this, then it fails. Whats the reason.
Thanks in advance.
I'm assuming you're passing in a comma-separated list of quoted values, for example p_lastnamelist is 'Smith','Jones', as that's the only way your first query will work, and explains the replace call. (It helps if you show the input, output and any errors you get, so we don't have to guess and assume). When that's used in the queries they end up looking significantly different. The first one:
'SELECT * FROM emp WHERE last_name IN('||p_lastnamelist||')'
becomes:
SELECT * FROM emp WHERE last_name IN('Smith','Jones')
... which will match on any records with either of those last names. But the second one:
'SELECT * FROM emp WHERE last_name IN(TestPkg.getLastName)'
becomes, after you've stripped the quotes from the value:
SELECT * FROM emp WHERE last_name IN('Smith,Jones')
... which is looking for a single value, and would only match if you had a record with last name Smith,Jones, which is unlikely. Whatever is returned by TestPkg.getLastName will be treated a single string value. It has to be since the function returns a varchar2. But that's also the case if you use a bind variable as Egor suggested.

Oracle-sql query [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I want to use single cursor to fetch single record or all records using where condition, e.g. : student is table and sid is an attribute.
I have two cursors,
DECLARE S1 CURSOR FOR SELECT * FROM Student;
and
Declare S2 Cursor for select * from Student where sid=11
My query is on how to combine these two conditions to use only one cursor. I need this as I have two functions m_viewStudent and m_viewallStudents, for this I would like to use only one cursor to display the requested details from table.
so how can I achieve this ?
Try this:
select *
from student
where sid = 11
or not exists (select 1 from student where sid = 11)
Here is a sqlfiddle demo
UPDATE
If you want to use the same cursor for different functions then you can do it like this:
create package p is
procedure one_sid(in_sid number);
procedure all_sid;
end p;
/
create package body p is
cursor c(p_sid number) is
select *
from student
where sid = p_sid or p_sid is null;
procedure one_sid(in_sid number) is
begin
open c(in_sid);
close c;
end;
procedure all_sid is
begin
open c(null);
close c;
end;
end p;
/
You can change the cursor to
SELECT * FROM students WHERE sid like '%' || variable_from_function || '%';
This will ensure that when function m_viewStudent calls this, only the sid (value sent to this function variable_from_function) record is picked. When m_viewallStudents calls this, all records get picked as variable_from_function would be null.
Assuming your procedure variable is $var, try this:
Declare S2 Cursor for select * from Student
where sid=$var
or $var is null
And pass in a null when you want them all, or make the default value of the parameter null and pass in no parameter
The best way to do this is with a Cursor Variable, more commonly called a Ref Cursor. This is basically a pointer to a result set. The advantage of a Ref Cursor is that we can vary the select statement, like this:
create or replace package student_utils is
-- a hard-types ref cursor
type stud_cur is ref cursor return students%rowtype;
function get_students
( p_sid in students.sid%type := null )
return stud_cur;
end;
Note that I have made a couple of presumptions about how you are intending to use the code. Using a package allows us to define a hard-typed ref cursor, which means that it can only be used for queries which match the projection of the STUDENTS table. (If you don't have an actual STUDENTS table you can use of of the views or define a Pl/SQL Record instead.)
create or replace package body student_utils is
function get_students
( p_sid in students.sid%type := null )
return stud_cur
is
return_value stud_cur;
begin
if p_sid is null
then
open return_value for
select * from m_viewallStudents;
else
open return_value for
select * from m_viewStudent
where sid = p_sid;
end if;
return return_value;
end;
end;
These queries are hard-coded but we can also open Ref Cursors with dynamic SQL, which is a powerful technique.
Read the documentation to find out more.

WHERE condition for "starts with any of the values in a CSV list" [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
how to convert csv to table in oracle
I have a query in PL/SQL that is built to handle input in a variable as a "starts-with" filter:
WHERE product_group LIKE strProductGroup || '%'
As a new "feature", the input variable could contain comma separated values. So, where before I would expect something like "ART", I could now see "ART,DRM".
I'd like to avoid building this query as a string and using EXECUTE IMMEDIATE, if possible. Can anyone think of a way to write a WHERE condition that is the equivalent of saying "starts with any of the values in a CSV list" in Oracle 10g?
Assuming that you don't have any restrictions on creating a couple of additional objects (a collection type and a function), you can parse the list into a collection that you can reference in your query. Tom Kyte has a good discussion on this in his variable "IN" list thread.
If you use Tom's myTableType and in_list function, for example
SQL> create or replace type myTableType as table
of varchar2 (255);
2 /
Type created.
ops$tkyte#dev8i> create or replace
function in_list( p_string in varchar2 ) return myTableType
2 as
3 l_string long default p_string || ',';
4 l_data myTableType := myTableType();
5 n number;
6 begin
7 loop
8 exit when l_string is null;
9 n := instr( l_string, ',' );
10 l_data.extend;
11 l_data(l_data.count) :=
ltrim( rtrim( substr( l_string, 1, n-1 ) ) );
12 l_string := substr( l_string, n+1 );
13 end loop;
14
15 return l_data;
16 end;
17 /
Then you can search for equality relatively easily.
WHERE product_group IN (SELECT column_value
FROM TABLE( in_list( strProductGroup )))
But you want to do a LIKE which is a bit more challenging since you can't do a LIKE on an in-list. You could, however, do something like
select *
from emp e,
(select '^' || column_value search_regexp
from table( in_list( 'KIN,BOB' ))) a
where regexp_like( e.ename, a.search_regexp )
This will search the EMP table for any employees where the ENAME begins with either KIN or BOB. In the default SCOTT.EMP table, this will return just one row, the row where the ENAME is "KING"
I found another post that gave me an idea. In my specific case, the values in the input will all be 3 characters, so I can do the following:
AND SUBSTR(product_group, 0, 3) IN
(SELECT regexp_substr(strProductGroup, '[^,]+', 1, LEVEL)
FROM dual
CONNECT BY LEVEL <= length(regexp_replace(strProductGroup, '[^,]+')) + 1)
I like this solution, because it does not require additional types or functions, but is pretty limited to my specific case.

Resources