PLSQL - create type dynamically using function - sql

I would like to implement something like the following pseudo-code. Let say I have table named T_TMP_TABLE with column 'hour', 'day', and 'year'. The table name is used as an input parameter for create_types_fn function. Let's assume that I can get a list of column names and store it to column_name_array. Now, I need to create "custom type" using those column names. The reason is that the function will return table as an output, and the columns of the returned table would be the same ('hour', 'day', and 'year')
Briefly speaking, I have a table and I need output as table-format with same column names.
Am I able to do this? any suggestion or recommendation would be much appreciated!!
CREATE OR REPLACE FUNCTION create_types_fn (table_name in varchar2)
....
begin
array column_name_array = get_column_name_in_array_by_table_name(table_name)
CREATE OR REPLACE TYPE my_type AS OBJECT (
column_name_array(0) NUMBER,
column_name_array(1) NUMBER,
column_name_array(2) VARCHAR2(30)
);
CREATE OR REPLACE type my_table AS TABLE OF my_type ;
select * bulk collect into my_table ;
end
EDIT
Here is what I am trying to do
I am trying to compare two tables and return rows if there are any difference. So, I think the output should be table-format. Since every table has different column names, I think it would be nice if I can make generic function..

If you are trying to compare the data in two different tables, you would almost certainly want to use the dbms_comparison package rather than writing your own. That populates a generic structure rather than creating new types for each table.

Related

IS TABLE OF and AS TABLE OF when creating PL/SQL nested table collection types

I am curious about syntax usage with PL/SQL collection type creations. What is the difference between is table of and as table of when creating a PL/SQL nested table collection type, if any? I've seen only is table of in the Oracle help documentation but code I've come across uses both is table of and as table of when creating the collection type. I tried both and both statements appear to execute at the same speed and hold data as intended.
For example, for object type
create or replace type new_emp_ot is object
(
ssn number(9),
lname varchar2(200),
fname varchar2(200)
);
is there any discernible difference between how Oracle processes
create or replace type new_emp_nt as table of new_emp_ot;
and
create or replace type new_emp_nt is table of new_emp_ot;
?
IS and AS are interchangeable in the CREATE TYPE syntax, there is no difference in this case.
Although the syntax diagram is incorrect... it makes it look like IS/AS are optional, but you can't actually omit them.

Dynamic Oracle Table Function for use in Tableau

We have a large amount of data in an Oracle 11g server. Most of the engineers use Tableau for visualizing data, but there is currently not a great solution for visualizing straight from the Oracle server because of the structure of the database. Unfortunately, this cannot be changed, as it's very deeply integrated with the rest of our systems. There is a "dictionary" table, let's call it tab_keys:
name | key
---------------
AB-7 | 19756
BG-0 | 76519
FY-10 | 79513
JB-2 | 18765
...
...
And there are also the tables actually containing the data. Each entry in tab_keys has a corresponding data table named by prefixing the key with an identifier, in this case, we'll use "dat_". So AB-7 will store all its data in a table called dat_19756. These keys are not known to the user, and are only used for tracking "behind the scenes". The user only knows the AB-7 moniker.
Tableau allows communication with Oracle servers using standard SQL select statements, but because the user doesn't know the key value, they cannot write a SQL statement to query the data.
Tableau recently added the ability for users to query Oracle Table Functions, so I started going down the road of writing a table function to query for the key, and return a table of the results for Tableau to use. The problem is that each dat_ table is basically unique with a different numbers of columns, labels, number of records, and datatypes from the next dat_ table.
What is the right way to handle this problem? Can I:
1) Write a function (which tableau can call inline in regular SQL) to return a bonified table name which is dynamically generated? I tried this:
create or replace FUNCTION TEST_FUNC
(
V_NAME IN VARCHAR2
) RETURN user_tables.table_name%type AS
V_KEY VARCHAR(100);
V_TABLE user_tables.table_name%type;
BEGIN
select KEY into V_KEY from my_schema.tab_keys where NAME = V_NAME;
V_TABLE := dbms_assert.sql_object_name('my_schema.dat_' || V_KEY);
RETURN V_TABLE;
END TEST_FUNC;
and then SELECT * from TABLE(TEST_FUNC('AB-7')); but I get:
ORA-22905: cannot access rows from a non-nested table item
22905. 00000 - "cannot access rows from a non-nested table item"
*Cause: attempt to access rows of an item whose type is not known at
parse time or that is not of a nested table type
*Action: use CAST to cast the item to a nested table type
I couldn't figure out a good way to CAST the table as the table type I needed. Could this be done in the function before returning?
2) Write a table function? Tableau can supposedly query these like tables, but then I run into the problem of dynamically generating types (which I understand isn't easy) but with the added complication of this needing to be used by multiple users simultaneously, so each user would need a data type generated for them each time they connect to a table (if I'm understanding this correctly).
I have to think I'm missing something simple. How do I cast the return of this query as this other table's datatype?
There is no simple way to have a single generic function return a dynamically configurable nested table. With other products you could use a Ref Cursor (which maps to ODBC or JDBC ResultSet object) but my understanding is Tableau does not support that option.
One thing you can do is generate views from your data dictionary. You can use this query to produce a one-off script.
select 'create or replace view "' || name || '" as select * from dat_' || key || ';'
from tab_keys;
The double-quotes are necessary because AB-7 is not a valid object name in Oracle, due to the dash.
This would allow your users to query their data like this:
select * from "AB-7";
Note they would have to use the double-quotes too.
Obviously, any time you inserted a row in tab_keys you'd need to create the required view. That could be done through a trigger.
You can build dynamic SQL in SQL using the open source program Method4:
select * from table(method4.dynamic_query(
q'[
select 'select * from dat_'||key
from tab_keys
where name = 'AB-7'
]'
));
A
-
1
The program combines Oracle Data Cartridge Interface with ANYDATASET to create a function that can return dynamic types.
There might be a way to further simplify the interface but I haven't figured it out yet. These Oracle Data Cartridge Interface functions are very picky and are not easy to repackage.
Here's the sample schema I used:
create table tab_keys(name varchar2(100), key varchar2(100));
insert into tab_keys
select 'AB-7' , '19756' from dual union all
select 'BG-0' , '76519' from dual union all
select 'FY-10', '79513' from dual union all
select 'JB-2' , '18765' from dual;
create table dat_19756 as select 1 a from dual;
create table dat_76519 as select 2 b from dual;
create table dat_79513 as select 3 c from dual;
create table dat_18765 as select 4 d from dual;

Is it possible to look up a table-valued function's return columns in SAP HANA's dictionary views?

I've created a table-valued function in SAP HANA:
CREATE FUNCTION f_tables
RETURNS TABLE (
column_value INTEGER
)
LANGUAGE SQLSCRIPT
AS
BEGIN
RETURN SELECT 1 column_value FROM SYS.DUMMY;
END
Now I'd like to be able to discover the function's table type using the dictionary views. I can run this query here:
select *
from function_parameters
where schema_name = '[xxxxxxxxxx]'
and function_name = 'F_TABLES'
order by function_name, position;
Which will yield something like:
PARAMETER_NAME TABLE_TYPE_SCHEMA TABLE_TYPE_NAME
---------------------------------------------------------------------
_SYS_SS2_RETURN_VAR_ [xxxxxxxxxx] _SYS_SS_TBL_[yyyyyyy]_RET
Unfortunately, I cannot seem to be able to look up that _SYS_SS_TBL_[yyyyyyy]_RET table in SYS.TABLES (and TABLE_COLUMNS), SYS.VIEWS (and VIEW_COLUMNS), SYS.DATA_TYPES, etc. in order to find the definitions of the individual columns.
Note that explicitly named table types created using CREATE TYPE ... do appear in SYS.TABLES...
Is there any way for me to look formally look up a table-valued function's return columns? I'm not interested in parsing the source, obviously.
These kind of tables are internal row-store tables, therefore you can only find your _SYS_SS_TBL_[yyyyyyy]_RET table in SYS.RS_TABLES_. This will give you some basic information, including a column ID (CID). This value is important to find the column information.
For example, if your CID is 100, you can find column information in the RS_COLUMNS_ table with this query:
SELECT * FROM SYS.RS_COLUMNS_ WHERE CID = 100

DataSet's Table name from a Stored Procedure

I am using a stored procedure to fill a DataSet. What I need to do is force the name of the DataTable that is created when filled. There are multiple tables returned from the Stored Procedure. The last table is the one I need to make sure has a specific name when returned. It is created by returning a value of a variable and not pulling from any tables.
SELECT #Phone as My_800Number
How can I make this return as table called "D1Header"?
There is no ADO.NET Native way to do it; ADO.Net assign a generated name with a sequence number, according to this
You can workaround it... if you say you need the last table with a specific name, you can do:
if (ds.Tables.Count > 0) {
ds.Tables[ds.Tables.Count - 1].TableName = "name";
}
Could use an enum of the table names and reference that in your table reference rather than the table itself.
ds.tables(myEnum.Contacts).rows ?

#define Equivalent for Oracle SQL?

Is there a SQL equivalent to #define?
I am using NUMBER(7) for ID's in my DB and I'd like to do something like:
#define ID NUMBER(7)
Then I could use ID in the creation of my tables:
CREATE table Person (
PersonID ID,
Name VARCHAR2(31)
)
This will allow me to easily change the type of all of my IDs if I realize I need more/less space later.
In SQL Plus scripts you can use a substitution variable like this:
DEFINE ID = "NUMBER(7)"
CREATE table Person (
PersonID &ID.,
Name VARCHAR2(31)
);
Basic definitions (similar to the C #define) are called inquiry directives. the inquiry directive cannot be used to define macros.
You can read about inquiry directives here but I think that what you need it's a simple constant.
constant_name CONSTANT datatype := VALUE;
No, there's no way to make this sort of dynamic definition in Oracle. Although Oracle does have the concept of user-defined column types that let you define this globally in a single object, once you create a table that has a column of this type the definition is frozen and you'll get an "ORA-02303: cannot drop or replace a type with type or table dependents" if you try to redefine the type.
I'd Strongly recommend getting a copy of PowerDesigner, which you let you do this through the use of domains.
This code declare two constants. We aren't going to use the constants itself, but their types.
In the create table statement, we declare Name and Blood as "The same type of blood_type and name_type". If later we need more space, we only have to change the types of the constants and all the fields declared with %TYPE in the script will be affected. I hope it help you.
DECLARE
blood_type CONSTANT CHAR;
name_type CONSTANT varchar2(10);
BEGIN
create table patient (
Name name_type%TYPE,
Blood blood_type%TYPE
)
END;
/
EDITED: as dpbradley said there is a problem with this code. Oracle don't let create tables directly inside a begin-end block. The DBMS_SQL package has to be used to create a table dinamically and this would make the solution unreadable and uncomfortable. Sorry.