Oracle Function: Replicate wm_concat - oracle

I currently am working on a project within Crystal Reports that refuses to use the undocumented function WM_CONCAT, which is allowable within Oracle 10g.
Here is the WM_CONCAT header information
WM_CONCAT(p1 IN VARCHAR2) RETURN VARCHAR2
To use WM_CONCAT I pass it the following: WM_CONCAT(column1); This function seems to accept a column of type varchar2, and returns a comma delimited list of values from the column. I currently have a custom version of this function that works (on my work computer), but it is not optimal and lacks re-usability. Could anyone provide a good, re-usable function like WM_CONCAT that I could use?

Do you get an error message when you use wm_concat?
Unlike functions like to_char, it is owned by wmsys and you might need to use wmsys.wm_concat to use it. (unless you create the necessary synonyms of course).
Now for the actual question,
This technique is called string aggregation.
You could find a lot of other alternatives here.
http://www.oracle-base.com/articles/10g/StringAggregationTechniques.php
For other methods, Search for "stragg" on http://asktom.oracle.com
Another useful link : http://www.orafaq.com/node/2290
This is probably the most used one.
A lot of teams write their own custom functions which more or less do the same.
CREATE OR REPLACE FUNCTION get_employees (p_deptno in emp.deptno%TYPE)
RETURN VARCHAR2
IS
l_text VARCHAR2(32767) := NULL;
BEGIN
FOR cur_rec IN (SELECT ename FROM emp WHERE deptno = p_deptno) LOOP
l_text := l_text || ',' || cur_rec.ename;
END LOOP;
RETURN LTRIM(l_text, ',');
END;
/
SHOW ERRORS
while this solution works for varchar2 and number, the best generic solution can be built using Oracle ODCIAggregate interface.
http://download-west.oracle.com/docs/cd/B14117_01/appdev.101/b10800/dciaggfns.htm#sthref462
Implementation for the same is at the first link above at www.oracle-base.com

I've solved this using a technique similar to the last one in the oracle-base article: define a custom TABLE type and write a function to aggregate a value of that type into a string. I called my function joinstr and then you can call it as follows:
SELECT joinstr(CAST(MULTISET(SELECT column1 FROM table1) AS my_string_table_type), ',')
FROM DUAL
Note: I was on 9i until recently and haven't looked into COLLECT yet.

Related

Using WITH FUNCTION in Oracle FOR LOOP CURSOR

I'm using Oracle Database 19c Enterprise Edition.
For various reasons, our core database devs write almost everything using procedures and not functions.
I thought it would be fun to wrap a procedure or two into a function that returns the one or two data items we need in our SQL.
Instead of creating a regular function like a normal person would do, I found that I could declare functions using the WITH statement right in my SQL.
This works great as I'll show in my first example below, but when I try to use it in a FOR LOOP cursor it gives me an ORA-06550 error (missing keyword) right after the WITH keyword.
Working SQL
with
function foobar(in_txt1 varchar2, in_txt2 varchar2) return varchar2 as
begin
return in_txt1 || in_txt2;
end foobar;
select foobar('foo','bar') mytxt from dual;
Broken SQL
begin
for cur in (
with
function foobar(in_txt1 varchar2, in_txt2 varchar2) return varchar2 as
begin
return in_txt1 || in_txt2;
end foobar;
select foobar('foo','bar') mytxt from dual
)
loop
dbms_ouput.put_line(cur.mytxt);
end loop;
end;
Oracle SQL Developer doesn't seem show any errors before I run the code. Does anyone know how to get this to work?
Yes, I know that I could just run the procedure inside the loop, but this is more for a learning experience rather than production code.
Edit: It works if I use dynamic SQL, but that's not something I like to use. I think this just isn't supported at this time. I'll close this question if others agree this can't be done yet.
Thanks!

Oracle 12c table function to select subset of rows with FOR UPDATE SKIP LOCKED

I have a requirement to return a subset of rows from a table using FOR UPDATE SKIP LOCKED. Based on application parameters this subset may or may not be ordered by a column. I can't use ROWNUM since the numbers are assigned before SKIP LOCKED happens, so using cursors and FETCH ... LIMIT seems to be the way to go.
That works using an anonymous PL/SQL block, but I need to expose the data back to the java application. The most straightforward way would be to use a table function, so I can just do SELECT * FROM table(my_function(<params>)).
I tried a standard table function returning a collection first, but I got the error [902] ORA-00902: invalid datatype. This is roughly what I had in place:
Package specification:
CREATE OR REPLACE PACKAGE ACTIVITY_UTILS AS
TYPE ActivityList IS TABLE OF ACTIVITY_TABLE%ROWTYPE;
FUNCTION activity_batch(batch_size IN INTEGER, order_by_source IN VARCHAR2)
RETURN ActivityList;
END ACTIVITY_UTILS;
Package body:
CREATE OR REPLACE PACKAGE BODY ACTIVITY_UTILS AS
FUNCTION activity_batch(batch_size IN INTEGER, order_by_source IN VARCHAR2)
RETURN ActivityList
IS
batch ActivityList := ActivityList();
selectStatement VARCHAR2(200);
TYPE CursorType IS REF CURSOR;
activitiesCursor CursorType;
BEGIN
IF UPPER(order_by_source) = 'TRUE' THEN
selectStatement := 'SELECT * FROM ACTIVITY_TABLE ORDER BY source FOR UPDATE SKIP LOCKED';
ELSE
selectStatement := 'SELECT * FROM ACTIVITY_TABLE FOR UPDATE SKIP LOCKED';
OPEN activitiesCursor FOR selectStatement;
FETCH activitiesCursor BULK COLLECT INTO batch LIMIT batch_size;
CLOSE activitiesCursor;
RETURN batch;
END activity_batch;
While debugging the ORA-00902 error I ran into this question:
Return collection from packaged function for use in select
My (limited) understanding was I was trying to use a PL/SQL type on plain SQL, which is not allowed. I tried using a pipelined table function, as mentioned in the answer, but then I got the error ORA-14551: cannot perform a DML operation inside a query.
This seemed odd, is SELECT ... FOR UPDATE considered DML? At any rate, I noticed I could workaround this by using pragma autonomous_transaction, but that defeats the purpose of having FOR UPDATE SKIP LOCKED.
My question is, is this requirement achievable at all using functions, or would I have to use a procedure with an OUT parameter?
Option 1: create a function (or procedure) that returns a cursor and let your java application fetch it normally.
Option 2: Use Implicit statement results: in this case your java application can run something like call proc() where proc returns implicit statement results.
PS. It's not a good idea to hide DML under SQL select...

construct an MCD (merise) where a table contain columns from the content of another table

i'am facing a problem while trying to make a conception for a school project.and i have two question.
1/
is it possible with oracle to do something like this : https://mariadb.com/kb/en/dynamic-columns/
2/
how can i make a modelisation for that with an MCD (method merise ).
thanks
Quote from your link: "It works by storing a set of columns in a blob and having a small set of functions to manipulate it.". I don't see an obvious reason why you couldn't write something similar for Oracle (or any other relational database).
Alternatively, take a look at EAV, which is a more traditional solution for dynamic columns.
Sorry, I'm not familiar with Merise.
1/ Oracle is not made for creating dynamic columns, but you can do it by using PL/SQL functions (but not while querying with SQL):
create or replace procedure add_table_column(table_name varchar2, column_name varchar2, column_type varchar2)
is
v_script varchar2(4000);
begin
v_script := 'alter table '||table_name||' add '||column_name||' '||column_type;
execute immediate v_script;
end;
/
You can call it this way:
begin
add_table_column('toto','test','number');
end;
/
2/ Merise method is not made for "dynamic objects" as you must begin with a complete data dictionary.
How is your object dynamic, and why do you have to add columns?
You can always get a solution to work by using "regular" tables and relationships.
It sounds like you should start by analyzing the technical implementation in mind. MERISE says to analyze your data model first and start to think about technical solutions after.

Finding the datatype of a cursor or table column in a block

Is is possible to find out the datatype of a column of a cursor or variable within block without using system tables? While I understand that I can use the system tables to find out this information it would be a lot slower.
Something like,
declare
my_column_data_type varchar2(30);
begin
my_column_data_type := all_tables.table_name%type;
dbms_output.put_line(my_column_data_type);
end;
I can't find any way of doing it without resorting to dbms_sql, which would be overkill for my eventual purpose.
But, Oracle already has all the information to hand. If I were to try to assign a varchar2 to a number then it would complain instantly so it knows what the datatypes are.
And, yes I know the number of versions of Oracle are ridiculous but that's the amount we've got at the moment... 9i is dying shortly in favour of 11 but this code'll run on 9i immediately if I can find an answer! But I included 11 as I can wait for a better solution if needs be,
Use the dump function and compare the result with this code.
DUMP returns a VARCHAR2 value containing the datatype code, length in bytes, and internal representation of expr.
It sounds as if you want a self describing object. Meaning programmatically find the type of a variable without selecting from some metadata view. Just ask the object, what are you?
It seems unnecessary for most situations as in most cases we already know the type (strongly typed). For example, a procedures parameters will typically specify the type (number, varchar2, whatever). Local variables will typically specify the type or tie themselves to a database object type via %type notation.
There are some situations where weakly typed objects are needed or useful, such as a weakly typed cursor variable that can be used for any query. An overly simplistic example:
create or replace procedure get_data(o_cur OUT SYS_REFCURSOR) as
begin
OPEN o_cur FOR
-- without changing parameter, this could select from any table
select * from emp;
end;
Now the problem is that you may have errors (at runtime) if someone codes the cursor to be used with another table (I chose a terrible procedure name on purpose). Something like:
declare
l_cur sys_refcursor;
l_row dept%rowtype;
begin
get_data(l_cur);
-- oops, I thought this was dept data when I coded it, Oracle didn't complain at compile time
LOOP
fetch l_cur
into l_row;
exit when l_cur%notfound;
-- do something here
END LOOP;
close l_cur;
end;
This is also why I prefer strongly typed cursors and avoid this situation.
Anyway, in the case of a self-describing object, you can use SYS.ANYDATA built in type (similarly, SYS.ANYDATASET for generic collection types). This was introduced with 9i I believe. For example, this procedure takes some data and branches logic based on the type:
CREATE OR REPLACE procedure doStuffBasedOnType(i_data in sys.anydata) is
l_type SYS.ANYTYPE;
l_typecode PLS_INTEGER;
begin
-- test type
l_typecode := i_data.GetType (l_type);
CASE l_typecode
when Dbms_Types.Typecode_NUMBER then
-- do something with number
dbms_output.put_line('You gave me a number');
when Dbms_Types.TYPECODE_DATE then
-- do something with date
dbms_output.put_line('You gave me a date');
when Dbms_Types.TYPECODE_VARCHAR2 then
-- do something with varchar2
dbms_output.put_line('You gave me a varchar2');
else
-- didn't code for this type...
dbms_output.put_line('wtf?');
end case;
end;
Here you have your programatic branching based on the type. And to use it:
declare
l_data sys.anydata;
begin
l_data := sys.anydata.convertvarchar2('Heres a string');
doStuffBasedOnType(l_data);
end;
-- output: "You gave me a varchar2"
Hope that wasn't too long winded a response ;)

Getting results in a result set from dynamic SQL in Oracle

This question is similar to a couple others I have found on StackOverflow, but the differences are signficant enough to me to warrant a new question, so here it is:
I want to obtain a result set from dynamic SQL in Oracle and then display it as a result set in a SqlDeveloper-like tool, just as if I had executed the dynamic SQL statement directly. This is straightforward in SQL Server, so to be concrete, here is an example from SQL Server that returns a result set in SQL Server Management Studio or Query Explorer:
EXEC sp_executesql N'select * from countries'
Or more properly:
DECLARE #stmt nvarchar(100)
SET #stmt = N'select * from countries'
EXEC sp_executesql #stmt
The question "How to return a resultset / cursor from a Oracle PL/SQL anonymous block that executes Dynamic SQL?" addresses the first half of the problem--executing dynamic SQL into a cursor. The question "How to make Oracle procedure return result sets" provides a similar answer. Web search has revealed many variations of the same theme, all addressing just the first half of my question. I found this post explaining how to do it in SqlDeveloper, but that uses a bit of functionality of SqlDeveloper. I am actually using a custom query tool so I need the solution to be self-contained in the SQL code. This custom query tool similarly does not have the capability to show output of print (dbms_output.put_line) statements; it only displays result sets. Here is yet one more possible avenue using 'execute immediate...bulk collect', but this example again renders the results with a loop of dbms_output.put_line statements. This link attempts to address the topic but the question never quite got answered there either.
Assuming this is possible, I will add one more condition: I would like to do this without having to define a function or procedure (due to limited DB permissions). That is, I would like to execute a self-contained PL/SQL block containing dynamic SQL and return a result set in SqlDeveloper or a similar tool.
So to summarize:
I want to execute an arbitrary SQL statement (hence dynamic SQL).
The platform is Oracle.
The solution must be a PL/SQL block with no procedures or functions.
The output must be generated as a canonical result set; no print statements.
The output must render as a result set in SqlDeveloper without using any SqlDeveloper special functionality.
Any suggestions?
The closest thing I could think of is to create a dynamic view for which permission is required. This will certainly involve using a PL/SQL block and a SQL query and no procedure/function. But, any dynamic query can be converted and viewed from the Result Grid as it's going to be run as a select query.
DEFINE view_name = 'my_results_view';
SET FEEDBACK OFF
SET ECHO OFF
DECLARE
l_view_name VARCHAR2(40) := '&view_name';
l_query VARCHAR2(4000) := 'SELECT 1+level as id,
''TEXT''||level as text FROM DUAL ';
l_where_clause VARCHAR2(4000):=
' WHERE TRUNC(1.0) = 1 CONNECT BY LEVEL < 10';
BEGIN
EXECUTE IMMEDIATE 'CREATE OR REPLACE VIEW '
|| l_view_name
|| ' AS '
|| l_query
|| l_where_clause;
END;
/
select * from &view_name;
You seem to be asking for a chunk of PL/SQL code that will take an arbitrary query returning result set of undetermined structure and 'forward/restructure' that result set in some way such that is can easily be rendered by some "custom GUI tool".
If so, look into the DBMS_SQL for dynamic SQL. It has a DESCRIBE_COLUMNS procedure which returns the columns from a dynamic SELECT statement. The steps you would need are,
Parse the statement
Describe the result set (column names and data types)
Fetch each row, and for each column, call the datatype dependent function to return that value into a local variable
Place those local variables into a defined structure to return to the calling environment (eg consistent column names [such as col_1, col_2] probably all of VARCHAR2)
As an alternative, you could try building the query into an XMLFOREST statement, and parse the results out of the XML.
Added :
Unlike SQL Server, an Oracle PL/SQL call will not 'naturally' return a single result set. It can open up one or more ref cursors and pass them back to the client. It then becomes the client's responsibility to fetch records and columns from those ref cursors. If your client doesn't/can't deal with that, then you cannot use a PL/SQL call.
A stored function can return a pre-defined collection type, which can allow you to do something like "select * from table(func_name('select * from countries'))". However the function cannot do DML (update/delete/insert/merge) because it blows away any concept of consistency for that query. Plus the structure being returned is fixed so that
select * from table(func_name('select * from countries'))
must return the same set of columns (column names and data types) as
select * from table(func_name('select * from persons'))
It is possible, using DBMS_SQL or XMLFOREST, for such a function to take a dynamic query and restructure it into a pre-defined set of columns (col_1, col_2, etc) so that it can be returned in a consistent manner. But I can't see what the point of it would be.
Try try these.
DECLARE
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
emp_record employees%ROWTYPE;
v_stmt_str VARCHAR2(200);
v_e_job employees.job%TYPE;
BEGIN
-- Dynamic SQL statement with placeholder:
v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';
-- Open cursor & specify bind argument in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO emp_record;
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
declare
v_rc sys_refcursor;
begin
v_rc := get_dept_emps(10); -- This returns an open cursor
dbms_output.put_line('Rows: '||v_rc%ROWCOUNT);
close v_rc;
end;
Find more examples here. http://forums.oracle.com/forums/thread.jspa?threadID=886365&tstart=0
In TOAD when executing the script below you will be prompted for the type of v_result. From the pick list of types select cursor, the results are then displayed in Toad's data grid (the excel spreadsheet like result). That said, when working with cursors as results you should always write two programs (the client and the server). In this case 'TOAD' will be the client.
DECLARE
v_result sys_refcursor;
v_dynamic_sql VARCHAR2 (4000);
BEGIN
v_dynamic_sql := 'SELECT * FROM user_objects where ' || ' 1 = 1';
OPEN :v_result FOR (v_dynamic_sql);
END;
There may be a similar mechanism in Oracle's SQL Developer to prompt for the binding as well.

Resources