How to call an indirect table in FROM - sql

Please tell me what I am doing wrong. I have tried the following:
SELECT *
FROM 'A'||(TO_CHAR(SYSDATE, 'YYMMDD'))||'B'
SELECT *
FROM CAST('A'||(TO_CHAR(SYSDATE, 'YYMMDD'))||'B' AS varchar2(8))
SELECT *
FROM table( CAST('A'||(TO_CHAR(SYSDATE, 'YYMMDD'))||'B' AS varchar2(8)))
DEFINE tName = CAST('A'||(TO_CHAR(SYSDATE, 'YYMMDD'))||'B' AS varchar2(8))
SELECT *
FROM &tName
SET tName AS CAST('A'||(TO_CHAR(SYSDATE, 'YYMMDD'))||'B' AS varchar2(8))
SELECT *
FROM &tName
SET #tName AS CAST('A'||(TO_CHAR(SYSDATE, 'YYMMDD'))||'B' AS varchar2(8))
SELECT *
FROM #tName
SELECT *
FROM
(
SELECT CAST('A'||(TO_CHAR(SYSDATE, 'YYMMDD'))||'B' AS varchar2(8))
FROM DUAL
)
(Note: I have also tried adding the owner before the table in all of the examples above, as shown in the second example below)
I have verified that
SELECT CAST('A'||(TO_CHAR(SYSDATE, 'YYMMDD'))||'B' AS varchar2(8))
FROM DUAL
displays A170429B
SELECT CAST('owner.A'||(TO_CHAR(SYSDATE, 'YYMMDD'))||'B' AS varchar2(8))
FROM DUAL
displays owner.A170429B
but I can't get the FROM statement to see it as a Table
The database creates a new table every day; which, is why I am trying to do this.

As others have observed, partitioning is the elegant way to arrange this. But Partitioning remains a licensed extra on the Enterprise Edition,and that's expensive. So here's a couple of cheaper options.
The first option is to do what you are trying to do now, and query each table by name. For this approach to work you need to use dynamic SQL.
Here are some tables - t170428, t170429, t170430 - which all look like this
create table t170428 (
id number not null primary key
, type varchar2(10)
, col1 varchar2(10)
, col2 number
, col3 date not null
)
/
To query them we need a SQL type with a signature that matches the tables' projection:
create or replace type tyymmdd_t as object (
id number
, type varchar2(10)
, col1 varchar2(10)
, col2 number
, col3 date
);
/
create or replace type tyymmdd_nt as table of tyymmdd_t
/
Here is a function which dynamic builds a table name from a passed date and returns a nested table of rows from that table:
create or replace function get_date_table
( p_target_date in date)
return tyymmdd_nt
is
return_value tyymmdd_nt;
begin
execute immediate
' select tyymmdd_t(id, type, col1, col2, col3) from t'
||to_char(p_target_date, 'yymmdd')
bulk collect into return_value;
return return_value;
end;
/
To query a table we use the table() function like this:
SQL> select * from table(get_date_table(sysdate));
ID TYPE COL1 COL2 COL3
---------- ---------- ---------- ---------- ---------
9 D2 SUN 1 30-APR-17
10 D2 SUN 2 30-APR-17
SQL> select * from table(get_date_table(date'2017-04-28'));
ID TYPE COL1 COL2 COL3
---------- ---------- ---------- ---------- ---------
1 D1 FRI 1 28-APR-17
2 D1 FRI 2 28-APR-17
3 D1 FRI 3 28-APR-17
4 D1 FRI 4 28-APR-17
5 D1 FRI 5 28-APR-17
SQL> select * from table(get_date_table(sysdate+1));
select * from table(get_date_table(sysdate+1))
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at "FOX.GET_DATE_TABLE", line 7
SQL>
The second option is Partition Views. This is an old technique (from the previous millennium!) which allows us to build a view over multiple tables using the UNION ALL operator and get many benefits of Partitioning such as partition pruning - provided you're using a really old version of Oracle. Partition Views were deprecated in 8.0 and Oracle stopped supporting them in 9iR2. The documentation dates back to Oracle7 Find out more.
Anyway, the principle of partitioned views is this:
Enforce check constraints on the "partition key"
Build indexes on the partition key columns
Gather stats
Build the view
Like the table projection, the constraints and indexes must be the same for all tables.
alter table t170428 add constraint t170428_ptn_key_ck check (col3 = date '2017-04-28');
alter table t170429 add constraint t170429_ptn_key_ck check (col3 = date '2017-04-29');
alter table t170430 add constraint t170430_ptn_key_ck check (col3 = date '2017-04-30');
create unique index t170428_ptn_idx on t170428(col3, id) compress 1;
create unique index t170429_ptn_idx on t170429(col3, id) compress 1;
create unique index t170430_ptn_idx on t170430(col3, id) compress 1;
exec dbms_stats.gather_table_stats('FOX', 'T170428', cascade=>true)
exec dbms_stats.gather_table_stats('FOX', 'T170429', cascade=>true)
exec dbms_stats.gather_table_stats('FOX', 'T170430', cascade=>true)
create or replace view v_all_the_dates as
select * from t170428
union all
select * from t170429
union all
select * from t170430
/
Because Oracle don't support Partition Views in later versions of the database this approach won't give you partition pruning. But it could still be quite efficient providing you are rigorous about the indexing and check constraints.
A third option which might fit is an external table. The creation of dated tables suggests a daily load. If these just act as staging tables for data which arrive as files you could use an external table to access the data. The table would be a stable structure; all you would need to change is the location of the daily feed file. Find out more.

DBMS_XMLGEN can create a completely dynamic SQL statement without any additional privileges.
(But before you use the difficult SQL below, I recommend you try again to get more access to the database and use one of APC's solutions. Write access to the vendor's application schema is not required. The objects can be created in a separate user schema that will not interfere with the application.)
First, create a test table with today's date in the name
begin
execute immediate '
create table a'||to_char(sysdate, 'YYMMDD')||'b
(
column1 number,
column2 number
)';
end;
/
Add a test row. One potential problem with this solution is that when the inner query returns no rows it throw an error ORA-06502: PL/SQL: numeric or value error/nORA-06512: at "SYS.XMLTYPE", line 272/nORA-06512: at line 1.
begin
execute immediate 'insert into a'||to_char(sysdate, 'YYMMDD')||'b values (1,2)';
commit;
end;
/
This query reads from the table without knowing the table's name. It does however require knowing the table's columns. (If the vendor creates different columns in each table that's a more difficult problem that will definitely require custom objects.)
--Step 3: Convert XML back into a row.
select column1, column2
from
(
--Step 2: Create an XML result set of a dynamic query.
select
xmltype(dbms_xmlgen.getxml(
(
--Step 1: SELECT statement that generates a SELECT statement.
select
'select column1, column2
from a'||to_char(sysdate, 'YYMMDD')||'b'
from dual
)
)) xml_results
from dual
)
cross join
xmltable
(
'/ROWSET/ROW'
passing xml_results
columns
column1 number path 'COLUMN1',
column2 number path 'COLUMN2'
);
Results:
COLUMN1 COLUMN2
------- -------
1 2

Related

Oracle Object : How to show all the fields in select query?

I have the following Oracle Object :
CREATE TYPE person_typ AS OBJECT (
Id NUMBER,
first_name VARCHAR2(20),
last_name VARCHAR2(25));
And a table :
CREATE TABLE MyTable (
contact person_typ,
contact_date DATE );
I would like to make a select query to show all the fields of the Person_Typ object without specifiying their names.
When I do
select * from MyTable
The column contact is shown as [unsupported data type], I have to use instead :
select T.contact.ID, T.contact.First_name, T.contact.last_name from MyTable T
Is there another way to show the values of the object without specifying the column names ?
Thanks,
Cheers,
I don't use SQL Developer, but according to this article Showing TYPE’d Column Values in SQL Developer you could use option:
Preferences / Database / Advanced / Display Struct Value in Grid
Also you can query user_type_attr (or all_type_attr) to obtain column names. Then copy/paste select part from output and run it or create view as proposed by #sep. Here is my test data and code block:
insert into mytable values (person_typ(1, 'Paulina', 'Thomson'), date '2017-12-17');
insert into mytable values (person_typ(7, 'Keanu', 'Stevens'), date '2017-12-28');
declare
v_sql varchar2(32000);
begin
select listagg('T.CONTACT.'||attr_name||' '||attr_name, ', ')
within group (order by attr_no)
into v_sql
from user_type_attrs
where type_name = 'PERSON_TYP';
v_sql := 'SELECT '||v_sql||' FROM MYTABLE T';
dbms_output.put_line(v_sql);
execute immediate 'CREATE OR REPLACE VIEW VW_CONTACTS AS '||v_sql;
end;
select * from vw_contacts;
Result:
ID FIRST_NAME LAST_NAME
------ -------------------- -------------------------
1 Paulina Thomson
7 Keanu Stevens
I had the same problem and I created a view with all the fields. In your case:
CREATE VIEW v_myTable AS
select T.contact.ID, T.contact.First_name, T.contact.last_name
from MyTable T
and use the view instead of the table
SELECT * from v_myTable

If the first field doesn't exists in a table then look at a different field in the same table

Is there a way to select a field from a table and if that field doesn't exist then select a different field from the same table? example:
SELECT MY_FIELD from MY_TABLE
error: "MY_FIELD": invalid identifier
is there any way to check if it exists and if it does then use that field for the query, if it doesn't exist then use example:
SELECT my_field2 from client.
My problem is
I am writing a report that will be used on two databases, but the field names on occasion can be named slightly different depending on the database.
What you really need to do is talk to your management / development leads about why the different databases are not harmonized. But, since this is a programming site, here is a programming answer using dynamic SQL.
As has been pointed out, you could create views in the different databases to provide yourself with a harmonized layer to query from. If you are unable to create views, you can do something like this:
create table test ( present_column NUMBER );
insert into test select rownum * 10 from dual connect by rownum <= 5;
declare
l_rc SYS_REFCURSOR;
begin
BEGIN
OPEN l_rc FOR 'SELECT missing_column FROM test';
EXCEPTION
WHEN others THEN
OPEN l_rc FOR 'SELECT present_column FROM test';
END;
-- This next only works in 12c and later
-- In earlier versions, you've got to process l_rc on your own.
DBMS_SQL.RETURN_RESULT(l_rc);
end;
This is inferior to the other solutions (either harmonizing the databases or creating views). For one thing, you get no compile time checking of your queries this way.
That won't compile, so - I'd say not. You might try with dynamic SQL which reads contents of the USER_TAB_COLUMNS and create SELECT statement on-the-fly.
Depending on reporting tool you use, that might (or might not) be possible. For example, Apex offers (as reports's source) a function that returns query, so you might use it there.
I'd suggest a simpler option - create views on both databases which have unified column names, so that your report always selects from the view and works all the time. For example:
-- database 1:
create view v_client as
select client_id id,
client_name name
from your_table;
-- database 2:
create view v_client as
select clid id,
clnam name
from your_table;
-- reporting tool:
select id, name
from v_client;
This can be done in a single SQL statement using DBMS_XMLGEN.GETXML, but it gets messy. It would probably be cleaner to use dynamic SQL or a view, but there are times when it's difficult to create supporting objects.
Sample table:
--Create either table.
create table my_table(my_field1 number);
insert into my_table values(1);
insert into my_table values(2);
create table my_table(my_field2 number);
insert into my_table values(1);
insert into my_table values(2);
Query:
--Get the results by converting XML into rows.
select my_field
from
(
--Convert to an XMLType.
select xmltype(clob_results) xml_results
from
(
--Conditionally select either MY_FIELD1 or MY_FIELD2, depending on which exists.
select dbms_xmlgen.GetXML('select my_field1 my_field from my_table') clob_results
from user_tab_columns
where table_name = 'MY_TABLE'
and column_name = 'MY_FIELD1'
--Stop transformations from running the XMLType conversion on nulls.
and rownum >= 1
union all
select dbms_xmlgen.GetXML('select my_field2 my_field from my_table') clob_results
from user_tab_columns
where table_name = 'MY_TABLE'
and column_name = 'MY_FIELD2'
--Stop transformations from running the XMLType conversion on nulls.
and rownum >= 1
)
--Only convert non-null values.
where clob_results is not null
)
cross join
xmltable
(
'/ROWSET/ROW'
passing xml_results
columns
my_field number path 'MY_FIELD'
);
Results:
MY_FIELD
--------
1
2
Here's a SQL Fiddle if you want to see it running.

Nested SELECT statement in FROM clause

I want to get data from table which name is keeping in another table. Trying to get this as described below leads to getting result from nested SELECT only
select * from (select value from ex_scheme.ex_tab where name = 'ex_name.current_table_name')
I mean, I've got equivalent result as from just
select value from ex_scheme.ex_tab where name = 'ex_name.current_table_name'
query.
UPDATED
Ok, lets double-check if I was correctly understood.
I have to see one table data (lets call this table "table1"). I need to know this table name. And I know where its name is keeping. It is in another table (call it "names_table") in column "name" (row with column value = 'table1'). And I can get it by query
select name from names_table where value = 'table1'
If you know in advance the column and its type, you can build some dynamic SQL to dynamically query a table or another.
For example, say you have tables like the following:
create table table1(col) as (select 1 from dual);
create table table2(col) as (select 2 from dual);
create table tab_of_tabs (tab_name) as (select 'TABLE1' from dual);
You can use dynamic SQL to build a query that scans a table whose name is the result of a query:
SQL> declare
2 vSQL varchar2(1000);
3 vResult number;
4 begin
5 select 'select sum(col) from ' || tab_name -- build the query
6 into vSQL
7 from tab_of_tabs;
8 --
9 execute immediate vSQL into vResult; -- run the query
10 --
11 dbms_output.put_line('Result: ' || vResult);
12 end;
13 /
Result: 1
PL/SQL procedure successfully completed.
SQL>
If I understand correctly, you could use a nested query in a where clause. For example,
select * from table1 where table1.name in (select name from table2);
This assumes there's a column "name" in table1. The result of this query should return the rows in table1 that are in table2.
try giving alias
select n.* from (select value from ex_scheme.ex_tab where name = 'ex_name.current_table_name') n;
Update:
It is in another table (call it "names_table") in column "name" (row
with column value = 'table1').
this query will work
select n.* from (select name from ex_scheme.ex_tab where name = 'ex_name.current_table_name') n;
sub query fetches name of table from another table .

Is it possible to get overview of a CTE?

In sql developer I can do
desc table
and get
describe table
Name Null Type
---------------------- ---- -------------
DATE_TIME DATE
KEY VARCHAR2(11)
Is something like this possible with a CTE? For example if there are 10 columns in table, I will get the description of all of them. But what if I want the description of only 2 columns.
with alias as (select col1, col2 from table) desc alias
This doesn't work.
You could apply a trick:
CREATE VIEW dummy AS
WITH ..
SELECT ..
And then:
DESC dummy
No, you can't do that.
A CTE is not stored anywhere in the system catalogs. It's essentially the same as a derived table:
with alias as (select col1, col2 from table)
select * from alias
is the same as
select *
from ( select col1, col2 from table) alias
And you can't describe that derived table either.
The only way you can do something remotely similar is to select from the CTE but with a where condition that selects not rows.
with alias as (select col1, col2 from table)
select * from alias
where 0 = 42;
Then you would at least see the column names and data types (the details of that depend on the SQL client you are using)
You can do this without creating a view, with something a bit more complicated:
create table yourTable(
DATE_TIME DATE,
KEY VARCHAR2(11)
);
You can use DBMS_SQL to describe the columns of a select query:
SQL> DECLARE
2 l_Cursor INTEGER DEFAULT dbms_sql.open_cursor;
3 l_Stmt VARCHAR2(4000);
4 l_colCnt NUMBER DEFAULT 0;
5 l_descTbl dbms_sql.desc_tab;
6 BEGIN
7 dbms_sql.parse(l_Cursor, 'SELECT key FROM yourTable', dbms_sql.native);
8 dbms_sql.describe_columns(l_Cursor, l_colCnt, l_descTbl);
9 --
10 FOR i IN 1..l_colCnt LOOP
11 dbms_output.put_line('Column '||l_descTbl(i).col_name || ' - type '||l_descTbl(i).col_type);
12 END LOOP;
13 --
14 dbms_sql.close_cursor(l_Cursor);
15 END;
16 /
Column KEY - type 1
PL/SQL procedure successfully completed.
The result must be interpreted by this table:
Datatype Number
VARCHAR2 1
NVARCHAR2 1
NUMBER 2
INTEGER 2
LONG 8
ROWID 11
DATE 12
RAW 23
LONG RAW 24
CHAR 96
NCHAR 96
MLSLABEL 106
So, it is saying that the column KEY has type 1, that is a VARCHAR2 .
The same way you can have more informations, about size for example.

How can we figure out that a column in my oracle table is being populated/updated by a trigger of another table?

Consider a scenario, where there are two tables "A" and "B".
Table "A" has a trigger "Ta" [written long before me joining this project and thus I'm completely unaware of the trigger], which updates a column named "colB" in table "B".
Now, since I'm mostly using table "B" and concerned about the way "colB" is getting, I won't know if trigger "Ta" is updating this column.
So my question is, is there a direct oracle query/way to find if a column in one table is getting updated by any trigger running on another table?
Thanks in advance for educating me on this.
Regards
a.b
SELECT *
FROM
user_sources
WHERE
type = 'TRIGGER'
AND UPPER(text) LIKE '%UPDATE A%';
But it won't work if the query is in two lines such as :
UPDATE
A
SET
...
because text matches to a given line in the corresponding object.
Oracle fine grained dependency tracking knows which columns are used. Unfortunately, there is no way to track if that dependency is for reading or writing. And there is no default DBA_DEPENDENCY_COLUMNS view to find this information.
But luckily Rob van Wijk has created such a view. His blog has some more information, including the grants and create view statement, about half-way down the page.
Example:
drop table a;
drop table b;
create table a(colA number);
create table b(colB number, read_only number, not_used number);
create or replace trigger Ta
after update or insert or delete on a
begin
update b set colB = read_only;
end;
/
--What triggers are referencing B's columns?
select owner, name, type, referenced_column
from dba_dependency_columns
where referenced_owner = user
and referenced_name = 'B'
and type = 'TRIGGER';
OWNER NAME TYPE REFERENCED_COLUMN
----- ---- ---- -----------------
JHELLER TA TRIGGER COLB
JHELLER TA TRIGGER READ_ONLY
The view uses several undocumented tables and some advanced SQL features. This view would not be a good idea on a production server. But it is probably much more accurate than any solution that involves parsing SQL.
Simple example:
create table table_a(
id number primary key,
val varchar2( 100 )
);
create table table_b(
len number
);
insert into table_b values ( 0 );
set define off
create or replace trigger after_table_a
after insert on table_a for each row
begin
UpDate
table_B
set len = len + length( :new.val );
end;
/
insert into table_a values ( 1, 'Ala ma kota');
insert into table_a values ( 2, 'As to ali pies');
commit;
select * from table_b;
LEN
----------
25
And the query:
select trigger_name,
regexp_substr( trigger_body, 'update\s+table_b',1,1,'i') update_command
from (
select ut.trigger_name,
dbms_metadata.GET_DDL('TRIGGER', ut.trigger_name) trigger_body
from user_dependencies ud
join user_triggers ut on ( ud.type = 'TRIGGER'
and ut.trigger_name = ud.name
and ut.table_name <> ud.referenced_name )
where ud.referenced_name = 'TABLE_B'
)
where regexp_instr( trigger_body, 'update\s+table_b',1,1,0,'i') > 0 ;
TRIGGER_NAME UPDATE_COMMAND
------------- ------------------
AFTER_TABLE_A UpDate
table_B