Oracle SQL Find Highest ID Across All Tables - sql

Every one of my tables has an "id" field. I need to be able to find the highest ID across all of them, given that the list of tables might change.
Is there any way to get the list of tables in an oracle database, aggregate their rows (only ids), and then get the max()?
P.S. This is for updating a sequence which has gone out of whack.

Here is some simple dynamic SQL driving off the data dictionary:
SQL> set serveroutput on
SQL> declare
2 l_id pls_integer;
3 max_id pls_integer;
4 max_tab_name varchar2(30);
5 begin
6 max_id := 0;
7 for r in ( select table_name
8 from user_tab_columns
9 where column_name = 'ID' )
10 loop
11 execute immediate 'select max(id) from '||r.table_name
12 into l_id;
13 if l_id > max_id
14 then
15 max_id := l_id;
16 max_tab_name := r.table_name;
17 end if;
18 end loop;
19 dbms_output.put_line('Highest score = '||max_id||' table='||max_tab_name);
20 end;
21 /
Highest score = 2010070705 table=SESSIONS
PL/SQL procedure successfully completed.
SQL>
If the sequence services tables across several schemas, you will need to drive off ALL_TAB_COLUMNS and include OWNER in the query.

How about this?
SELECT MAX(ID)
FROM
(
SELECT MAX(ID) AS ID FROM CUSTOMER
UNION ALL
SELECT MAX(ID) AS ID FROM EMPLOYEE
UNION ALL
SELECT MAX(ID) AS ID FROM MANAGER
);
Repeat the UNION ALL for all tables that you need to search from.

how about querying the sequence that drives the id's for CURRVAL...
if you need to find out also what table that id is in, then construct a new table to track the id centrally and add triggers to populate on insert.

Related

Oracle - Anonymous Procedure to loop through multiple tables (dynamically) - Query returning multiple rows

I need to fire the same query on multiple tables. Query might return zero, one or more number of rows.
I can loop through the tables using EXECUTE IMMEDIATE but for returning multiple rows I would need a datatype so I think I would need to keep it as CURSOR.
for ease, lets say I need to execute below query on 2 tables - table1 and table2
Table1 has following columns
datetime
device_name
value1
value2
Table2 has following columns
datetime
device_name
value3
value4
Query to be executed on both the tables as below:
select datetime, count(*) from table_name group by datetime;
Whats the best approach here?
please note that I can't create any DB objects (proc/function). Has to be anonymous block only.
As long as the cursor structures are the same, you can loop through with some dynamic ref cursors, eg
SQL> set serverout on
SQL> declare
2 tablist sys.odcivarchar2list :=
3 sys.odcivarchar2list('ALL_OBJECTS','USER_OBJECTS');
4 rc sys_refcursor;
5
6 date_results sys.odcidatelist := sys.odcidatelist();
7 count_results sys.odcinumberlist := sys.odcinumberlist();
8 begin
9 for i in 1 .. tablist.count
10 loop
11 open rc for
12 replace(q'{select trunc(created,'YYYY'), count(*) from ### group by trunc(created,'YYYY') order by 1}', '###',tablist(i));
13 fetch rc bulk collect into date_results, count_results;
14 close rc;
15
16 dbms_output.put_line(tablist(i));
17 for c in 1 .. date_results.count
18 loop
19 dbms_output.put_line(rpad(date_results(c),20)||lpad(count_results(c),20));
20 end loop;
21 end loop;
22 end;
23 /
ALL_OBJECTS
01-JAN-17 67892
01-JAN-18 6228
USER_OBJECTS
01-JAN-18 1093
PL/SQL procedure successfully completed.

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.

PLSQL - Searching for record in a Nested Table that was Bulk Collected

I used bulk collect to fetch records into a nested table. I want to search for a record with exists method but it's not working out. I then found out the exists method uses index and does not look for the values. Do I need to go across each record and search for a match? Is there a shorter way to do it because I am going to use the same logic for large set of records?
I read in websites that bulk collect doesn't work properly with an associative array when using a varchar as a key so I used nested tables instead. Also, I don't want to read each record and store it in a hashmap as it degrades performance.
Create table sales(
name varchar2(100)
)
insert into sales(name) values('Test');
insert into sales(name) values('alpha');
insert into sales(name) values(null);
declare
type sales_tab is table of varchar2(1000);
t_sal sales_tab;
begin
select name bulk collect into t_sal from sales;
if(t_sal.exists('Test')) THEN
dbms_output.put_line('Test exists');
END IF;
dbms_output.put_line(t_sal.count);
end;
exists() function tells you if a particular element with integer or varchar2(for associative arrays index by varchar2 collections ) index of a collection exists. It does not test for membership. To be able to check if a collection contains an element with specific value member of condition can be used:
SQL> declare
2 type sales_tab is table of varchar2(1000);
3 t_sal sales_tab;
4 begin
5 select name
6 bulk collect into t_sal
7 from sales;
8
9 if('Test' member of t_sal) THEN
10 dbms_output.put_line('Test exists');
11 END IF;
12
13 dbms_output.put_line(t_sal.count);
14 end;
15 /
Test exists
3
PL/SQL procedure successfully completed

Arrays in Oracle SQL

Here's a simplified pseudo-code version of what I'd like to be able to do in PL-SQL (Oracle):
DECLARE
mylist as ARRAY
BEGIN
mylist (1) := '1'
mylist (2) := '3'
...
SELECT *
FROM aTable
WHERE aKey IN mylist;
END;
The SELECT should return the matching records for mylist(1), mylist(2) etc. It should be similar to ORing all the values, but of course we don't know in advance how many values we get.
How can I achieve this? I know that PL/SQL has some collection datatypes, but I can't seem to get them to work properly in SQL statements.
Thanks for any ideas.
This is easy to do with the TABLE() function. The one catch is that the array variable must use a type declared in SQL. This is because SELECT uses the SQL engine, so PL/SQL declarations are out of scope.
SQL> create or replace type numbers_nt as table of number
2 /
Type created.
SQL>
SQL> declare
2 l_array numbers_nt;
3 begin
4 l_array := numbers_nt (7521,7566,7654);
5 for r in ( select ename
6 from emp
7 where empno in ( select *
8 from table (l_array)
9 )
10 )
11 loop
12 dbms_output.put_line ( 'employee name = '||r.ename);
13 end loop;
14 end;
15 /
employee name = PADFIELD
employee name = ROBERTSON
employee name = BILLINGTON
PL/SQL procedure successfully completed.
SQL>
A couple of suggestions:
1.) There's a CAST SQL keyword that you can do that might do the job... it makes your collection be treated as if it were a table.
2.) Pipelined functions. Basically a function returns data that looks like a table.
This link summarises the options and has a number of code listings that explain them.
http://www.databasejournal.com/features/oracle/article.php/3352091/CASTing-About-For-a-Solution-Using-CAST-and-Table-Functions-in-PLSQL.htm