Oralce: Get segment size and amount of rows - oracle

I need a way in Oracle to query the size of the segments as well as the amount of rows for all segment types which are a 'Table'.
Is there a way to combine the statement below (which calculates the size) and the 'count' function?
SELECT s.segment_type, (s.bytes / 1024 / 1024) mb, s.segment_name, l.*, s.*
FROM dba_segments s, dba_lobs l
where s.segment_name = l.segment_name(+)
and s.owner='TEST' order by bytes desc

If you have Oracle 12c (please always provide your database version info), you can use the new inline functions.
with function fRowCount(aOwner in varchar2, aTableName in varchar2) return number
is
lCount number;
begin
execute immediate 'select count(*) from ' || aOwner || '.' || aTableName into lCount;
return lCount;
end;
SELECT case when segment_type = 'TABLE' then fRowCount(s.owner, s.segment_name) else null end rowcount,
s.segment_type, (s.bytes / 1024 / 1024) mb, s.segment_name, l.*, s.*
FROM dba_segments s, dba_lobs l
where s.segment_name = l.segment_name(+)
and s.owner='TEST' order by bytes desc
Please be really really careful with that. execute immediate without the option of proper binding variables and to be executed as a dba is a dangerous combination.

If you have gathered the stats at schema level then information you are seeking for must be present in dba_tables.
You can gather the statistics of the schema using the following:
EXEC DBMS_STATS.GATHER_SCHEMA_STATS('TEST');
After that, you can use the following query to fetch the desired result:
SELECT
S.SEGMENT_TYPE,
( S.BYTES / 1024 / 1024 ) MB,
DT.NUM_ROWS,
S.SEGMENT_NAME,
L.*,
S.*
FROM
DBA_SEGMENTS S
LEFT JOIN DBA_LOBS L ON ( S.SEGMENT_NAME = L.SEGMENT_NAME )
LEFT JOIN DBA_TABLES DT ON ( S.SEGMENT_NAME = DT.TABLE_NAME )
WHERE
S.OWNER = 'TEST'
ORDER BY
BYTES DESC
Note: Always use standard ANSI joins.
Cheers!!

Related

Oracle Query to find total size vs consumed vs available size including various schemas 11g

I have a requirement to come up with a break down of the below
overall DWH SIZE
used space
free space
break down of space per schema (example STAGE,EDW/CORE,MART....) (point 1,2,3 must be covered)
4.1 - I am interested to see specifically all the schemas break down by total table size vs used size
(tables), i have tried the below queries (1 to 5)
upon checking various posts it's bit confusing
some suggests checking the following tables
DATA_SIZE (to check the data files)
TEMP_SIZE (to check the temporaryfiles)
SYS.V_$LOG (to check the redo log files)
V$CONTROLFILE (to check the redo log files) DBA_SEGMENTS
i have tested the below queries
Query 1 - actual size of the database
SELECT SUM (bytes) / 1024 / 1024 / 1024 AS GB FROM dba_data_files;
RESULT - GB
GB
900 - only example
Query 2 - Gives the size occupied by data in this database or Database usage details
SELECT SUM (bytes)/1024/1024/1024 AS GB FROM dba_segments;
RESULT- GB
GB
900 - only example
Query 3 - overall database size in TB
SELECT
( SELECT SUM(BYTES)/1024/1024/1024/1024 DATA_SIZE FROM DBA_DATA_FILES ) +
( SELECT NVL(SUM(BYTES),0)/1024/1024/1024/1024 TEMP_SIZE FROM DBA_TEMP_FILES ) +
( SELECT SUM(BYTES)/1024/1024/1024/1024 REDO_SIZE FROM SYS.V_$LOG ) +
( SELECT SUM(BLOCK_SIZE*FILE_SIZE_BLKS)/1024/1024/1024/1024 CONTROLFILE_SIZE FROM V$CONTROLFILE) "SIZE IN TB"
FROM
DUAL
RESULT- TB
SIZE IN TB
100 - only example
Query 4 - Database Size in TB with use space and free space Oracle DB
select round(sum(used.bytes) / 1024 / 1024 / 1024/1024 ) || 'TB' "Database Size"
, round(sum(used.bytes) / 1024 / 1024 / 1024/1024 ) -
round(free.p / 1024 / 1024 / 1024/1024) || 'TB' "Used space"
, round(free.p / 1024 / 1024 / 1024/1024) || 'TB' "Free space"
from (select bytes
from v$datafile
union all
select bytes
from v$tempfile
union all
select bytes
from v$log) used
, (select sum(bytes) as p
from dba_free_space) free
group by free.p
/
RESULT- TB
Database Size Used space Free space
100 90 10
Query 5 - QUERY TO GET SIZE OF ALL TABLES IN AN ORACLE DATABASE SCHEMA
SELECT * FROM
(
SELECT
OWNER,
OBJECT_NAME,
OBJECT_TYPE,
TABLE_NAME,
--ROUND(BYTES)/1024/1024 AS MB,
ROUND(BYTES) / 1024 / 1024 / 1024 AS GB,
--ROUND(100*RATIO_TO_REPORT(ROUND(BYTES) / 1024 / 1024 / 1024) OVER(),2) AS GB_PERCENT,
ROUND(100*RATIO_TO_REPORT(BYTES) OVER (), 2) PERCENTAGE,
TABLESPACE_NAME,
EXTENTS,
INITIAL_EXTENT,
ROUND(SUM(BYTES/1024/1024/1024) OVER (PARTITION BY TABLE_NAME)) AS TOTAL_TABLE_GB
--ROUND(SUM(BYTES)/1024/1024/1024) OVER (PARTITION BY TABLE_NAME)) AS TOTAL_TABLE_GB
FROM
(
--TABLES
SELECT OWNER, SEGMENT_NAME AS OBJECT_NAME, 'TABLE' AS OBJECT_TYPE,
SEGMENT_NAME AS TABLE_NAME, BYTES,
TABLESPACE_NAME, EXTENTS, INITIAL_EXTENT
FROM DBA_SEGMENTS /*DBA_SEGMENTS*/
WHERE SEGMENT_TYPE IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
UNION ALL
--INDEXES
SELECT I.OWNER, I.INDEX_NAME AS OBJECT_NAME, 'INDEX' AS OBJECT_TYPE,
I.TABLE_NAME, S.BYTES,
S.TABLESPACE_NAME, S.EXTENTS, S.INITIAL_EXTENT
FROM DBA_INDEXES I /*DBA_INDEXES*/
, DBA_SEGMENTS S /*DBA_SEGMENTS*/
WHERE S.SEGMENT_NAME = I.INDEX_NAME
AND S.OWNER = I.OWNER
AND S.SEGMENT_TYPE IN ('INDEX', 'INDEX PARTITION', 'INDEX SUBPARTITION')
--LOB SEGMENTS
UNION ALL
SELECT L.OWNER, L.COLUMN_NAME AS OBJECT_NAME, 'LOB_COLUMN' AS OBJECT_TYPE,
L.TABLE_NAME, S.BYTES,
S.TABLESPACE_NAME, S.EXTENTS, S.INITIAL_EXTENT
FROM DBA_LOBS L, /*DBA_LOBS*/
DBA_SEGMENTS S /*DBA_SEGMENTS*/
WHERE S.SEGMENT_NAME = L.SEGMENT_NAME
AND S.OWNER = L.OWNER
AND S.SEGMENT_TYPE = 'LOBSEGMENT'
--LOB INDEXES
UNION ALL
SELECT L.OWNER, L.COLUMN_NAME AS OBJECT_NAME, 'LOB_INDEX' AS OBJECT_TYPE,
L.TABLE_NAME, S.BYTES,
S.TABLESPACE_NAME, S.EXTENTS, S.INITIAL_EXTENT
FROM DBA_LOBS L, /*DBA_LOBS*/
DBA_SEGMENTS S /*DBA_SEGMENTS*/
WHERE S.SEGMENT_NAME = L.INDEX_NAME
AND S.OWNER = L.OWNER
AND S.SEGMENT_TYPE = 'LOBINDEX'
)
WHERE OWNER IN UPPER('&SCHEMA_NAME')
)
--WHERE TOTAL_TABLE_MB > 10
ORDER BY TOTAL_TABLE_GB DESC, GB DESC
/
EXPECTED_RESULTS
OWNER OBJECT_TYPE TOTAL_SPACE_GB TOTAL_SPACE_USED_GB PERCENTAGE_GB
DWH_STAGE TABLE 400 200 50
DWH_EDW TABLE 800 400 50
DWH_MART TABLE 1600 800 50
could you please suggest how to achieve this ?
This shows space within table segments if that is what you want but I think you probably want free space by tablespace instead.
select t.owner,'TABLE' OBJECT_TYPE ,t.TOTAL_SPACE_GB, u.TOTAL_SPACE_USED_GB,(u.TOTAL_SPACE_USED_GB/t.TOTAL_SPACE_GB)*100 PERCENTAGE_USED
from
(select owner,sum(bytes)/(1024*1024*1024) TOTAL_SPACE_GB
from dba_segments
where segment_type like 'TABLE%'
group by owner) t,
(select owner, sum(NUM_ROWS*AVG_ROW_LEN)/(1024*1024*1024) TOTAL_SPACE_USED_GB
from dba_tables
group by owner) u
where
t.owner=u.owner
order by owner;
Bobby

Pipeline table function

I would like to understand why pipelined function is not returning any results
Any ideas what i am doing wrong here.
Giving credit where due to the Original Author
CREATE OR REPLACE PACKAGE MANAGE_SPACE AS
--Author Tanmay
g_tblspce_threshold number := 80;
TYPE tblespaces_record IS RECORD(
tablespace_name VARCHAR2(30),
percentage_used NUMBER
);
TYPE tblespaces_table IS TABLE OF tblespaces_record;
function list_tblspcs_excd_thresld
return tblespaces_table PIPELINED ;
END MANAGE_SPACE;
Package body
CREATE OR REPLACE PACKAGE BODY MANAGE_SPACE
AS
--Author Tanmay
FUNCTION list_tblspcs_excd_thresld
RETURN tblespaces_table PIPELINED
AS
tblspaces tblespaces_record;
BEGIN
for x in (SELECT a.tablespace_name tablespace_name,
ROUND (((c.BYTES - NVL (b.BYTES, 0)) / c.BYTES) * 100) percentage_used
into tblspaces
FROM dba_tablespaces a,
(SELECT tablespace_name,
SUM (BYTES) BYTES
FROM dba_free_space
GROUP BY tablespace_name
) b,
(SELECT COUNT (1) DATAFILES,
SUM (BYTES) BYTES,
tablespace_name
FROM dba_data_files
GROUP BY tablespace_name
) c
WHERE b.tablespace_name(+) = a.tablespace_name
AND c.tablespace_name(+) = a.tablespace_name
AND ROUND (((c.BYTES - NVL (b.BYTES, 0)) / c.BYTES) * 100) > g_tblspce_threshold
ORDER BY NVL (((c.BYTES - NVL (b.BYTES, 0)) / c.BYTES), 0) DESC)
loop
PIPE ROW (tblspaces);
end loop;
return;
END list_tblspcs_excd_thresld;
END MANAGE_SPACE;
executing this package does not return any rows
SQL> select * from table(MANAGE_SPACE.list_tblspcs_excd_thresld())
2
SQL> /
TABLESPACE_NAME PERCENTAGE_USED
------------------------------ ---------------
What am i doing wrong
You need to populate an array. The easiest way to do this is to use the BULK COLLECT syntax. Then loop round the array and pipe out rows.
Here is my revised version of your package.
CREATE OR REPLACE PACKAGE BODY MANAGE_SPACE
AS
FUNCTION list_tblspcs_excd_thresld
RETURN tblespaces_table PIPELINED
AS
-- collection type not record type
tblspaces tblespaces_table;
BEGIN
select *
-- populate the collection
bulk collect into tblspaces
from
(SELECT a.tablespace_name tablespace_name,
ROUND (((c.BYTES - NVL (b.BYTES, 0)) / c.BYTES) * 100) percentage_used
FROM dba_tablespaces a,
(SELECT tablespace_name,
SUM (BYTES) BYTES
FROM dba_free_space
GROUP BY tablespace_name
) b,
(SELECT COUNT (1) DATAFILES,
SUM (BYTES) BYTES,
tablespace_name
FROM dba_data_files
GROUP BY tablespace_name
) c
WHERE b.tablespace_name(+) = a.tablespace_name
AND c.tablespace_name(+) = a.tablespace_name
AND ROUND (((c.BYTES - NVL (b.BYTES, 0)) / c.BYTES) * 100) > g_tblspce_threshold
ORDER BY NVL (((c.BYTES - NVL (b.BYTES, 0)) / c.BYTES), 0) DESC);
-- loop round the collection
for i in 1..tblspaces.count()
loop
PIPE ROW (tblspaces(i));
end loop;
return;
END list_tblspcs_excd_thresld;
END MANAGE_SPACE;
Here is the output:
SQL> select * from table(MANAGE_SPACE.list_tblspcs_excd_thresld())
2 /
TABLESPACE_NAME PERCENTAGE_USED
------------------------------ ---------------
SYSTEM 100
SYSAUX 91
APEX_2614203650434107 90
EXAMPLE 87
USERS 82
SQL>
#a_horse_with_no_name makes a good point regarding memory consumption. Collections are held in the session's memory space, the PGA. In this particular case it is unlikely that a database would have enough tablespaces to blow the PGA limit. But for other queries which might have much larger result sets there is the BULK COLLECT ... LIMIT syntax, which allows us to fetch manageable chunks of the result set into our collection. Find out more.

How to Get all table space name, Allocated size, Free size, Capacity from single query?

How can i get the information about all tables space in my database in below format.
TABLESPACE_NAME | FILE_NAME | ALLOCATED_MB | FREE_MB | CAPACITY |
Is there way to store daily size of all table-space in another table automatically ?. Actually i need to prepare checklist regarding table space on daily basis. So I wan't to create front end which email me the table space size details automatically on the basis of that table which store the information about table space size on daily basis..
Try the below query to get all tablespace details in oracle. Assuming that you have the necessary privileges to access dba tables.
SELECT a.file_name,
substr(A.tablespace_name,1,14) tablespace_name,
trunc(decode(A.autoextensible,'YES',A.MAXSIZE-A.bytes+b.free,'NO',b.free)/1024/1024) free_mb,
trunc(a.bytes/1024/1024) allocated_mb,
trunc(A.MAXSIZE/1024/1024) capacity,
a.autoextensible ae
FROM (
SELECT file_id, file_name,
tablespace_name,
autoextensible,
bytes,
decode(autoextensible,'YES',maxbytes,bytes) maxsize
FROM dba_data_files
GROUP BY file_id, file_name,
tablespace_name,
autoextensible,
bytes,
decode(autoextensible,'YES',maxbytes,bytes)
) a,
(SELECT file_id,
tablespace_name,
sum(bytes) free
FROM dba_free_space
GROUP BY file_id,
tablespace_name
) b
WHERE a.file_id=b.file_id(+)
AND A.tablespace_name=b.tablespace_name(+)
ORDER BY A.tablespace_name ASC;
Select a.tablespace_name,a.file_name,a.bytes/1024/1024 TABLESPACE_SIZE_MB,
Sum(b.bytes)/1024/1024 FREE_IN_MB from dba_free_space b,dba_data_files a
Where a.tablespace_name = b.tablespace_name
AND a.file_id = b.file_id
GROUP by a.tablespace_name, a.file_name,a.bytes/1024/1024
Order by a.tablespace_name, a.file_name;
you can run this query this may help.
Above are useful. Hope this may also helpful here:
https://ora-data.blogspot.in/2016/12/how-to-find-details-of-tablespace.html
Check the Tablespace details with different command, above command may not work:
SQL>select round((bytes/1024)/1024,0) "Used Space(MB)",
round(total,0) "Allocated size(MB)",
round(max,0) "Maximum allowable(MB)",
round(max-(BYTES/1024)/1024,0) "Effective free(MB)",
round(((max-(BYTES/1024)/1024)/max)*100,2) "FREE(%)"
from SYS.SM$TS_USED,
(select sum((BYTES/1024)/1024) total, sum((decode(MAXBYTES,0,bytes,maxbytes)/1024)/1024) max
from dba_data_files where tablespace_name='&1') where tablespace_name='&1';
In Oracle refer below link :
How do I calculate tables size in Oracle
https://forums.oracle.com/thread/2160787
COLUMN TABLE_NAME FORMAT A32
COLUMN OBJECT_NAME FORMAT A32
COLUMN OWNER FORMAT A10
SELECT
owner, table_name, TRUNC(sum(bytes)/1024/1024) Meg
FROM
(SELECT segment_name table_name, owner, bytes
FROM dba_segments
WHERE segment_type = 'TABLE'
UNION ALL
SELECT i.table_name, i.owner, s.bytes
FROM dba_indexes i, dba_segments s
WHERE s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type = 'INDEX'
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.segment_name
AND s.owner = l.owner
AND s.segment_type = 'LOBSEGMENT'
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.index_name
AND s.owner = l.owner
AND s.segment_type = 'LOBINDEX')
WHERE owner in UPPER('&owner')
GROUP BY table_name, owner
HAVING SUM(bytes)/1024/1024 > 10 /* Ignore really small tables */
ORDER BY SUM(bytes) desc
;
In SQL refer below
Get size of all tables in database

how to make selecting random rows in oracle faster with table with millions of rows

Is there a way to make selecting random rows faster in oracle with a table that has million of rows. I tried to use sample(x) and dbms_random.value and its taking a long time to run.
Thanks!
Using appropriate values of sample(x) is the fastest way you can. It's block-random and row-random within blocks, so if you only want one random row:
select dbms_rowid.rowid_relative_fno(rowid) as fileno,
dbms_rowid.rowid_block_number(rowid) as blockno,
dbms_rowid.rowid_row_number(rowid) as offset
from (select rowid from [my_big_table] sample (.01))
where rownum = 1
I'm using a subpartitioned table, and I'm getting pretty good randomness even grabbing multiple rows:
select dbms_rowid.rowid_relative_fno(rowid) as fileno,
dbms_rowid.rowid_block_number(rowid) as blockno,
dbms_rowid.rowid_row_number(rowid) as offset
from (select rowid from [my_big_table] sample (.01))
where rownum <= 5
FILENO BLOCKNO OFFSET
---------- ---------- ----------
152 2454936 11
152 2463140 32
152 2335208 2
152 2429207 23
152 2746125 28
I suspect you should probably tune your SAMPLE clause to use an appropriate sample size for what you're fetching.
Start with Adam's answer first, but if SAMPLE just isn't fast enough, even with the ROWNUM optimization, you can use block samples:
....FROM [table] SAMPLE BLOCK (0.01)
This applies the sampling at the block level instead of for each row. This does mean that it can skip large swathes of data from the table so the sample percent will be very rough. It's not unusual for a SAMPLE BLOCK with a low percentage to return zero rows.
Here's the same question on AskTom:
http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:6075151195522
If you know how big your table is, use sample block as described above. If you don't, you can modify the routine below to get however many rows you want.
Copied from: http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:6075151195522#56174726207861
create or replace function get_random_rowid
( table_name varchar2
) return urowid
as
sql_v varchar2(100);
urowid_t dbms_sql.urowid_table;
cursor_v integer;
status_v integer;
rows_v integer;
begin
for exp_v in -6..2 loop
exit when (urowid_t.count > 0);
if (exp_v < 2) then
sql_v := 'select rowid from ' || table_name
|| ' sample block (' || power(10, exp_v) || ')';
else
sql_v := 'select rowid from ' || table_name;
end if;
cursor_v := dbms_sql.open_cursor;
dbms_sql.parse(cursor_v, sql_v, dbms_sql.native);
dbms_sql.define_array(cursor_v, 1, urowid_t, 100, 0);
status_v := dbms_sql.execute(cursor_v);
loop
rows_v := dbms_sql.fetch_rows(cursor_v);
dbms_sql.column_value(cursor_v, 1, urowid_t);
exit when rows_v != 100;
end loop;
dbms_sql.close_cursor(cursor_v);
end loop;
if (urowid_t.count > 0) then
return urowid_t(trunc(dbms_random.value(0, urowid_t.count)));
end if;
return null;
exception when others then
if (dbms_sql.is_open(cursor_v)) then
dbms_sql.close_cursor(cursor_v);
end if;
raise;
end;
/
show errors
Below Solution to this question is not the exact answer but in many scenarios you try to select a row and try to use it for some purpose and then update its status with "used" or "done" so that you do not select it again.
Solution:
Below query is useful but that way if your table is large, I just tried and see that you definitely face performance problem with this query.
SELECT * FROM
( SELECT * FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1
So if you set a rownum like below then you can work around the performance problem. By incrementing rownum you can reduce the possiblities. But in this case you will always get rows from the same 1000 rows. If you get a row from 1000 and update its status with "USED", you will almost get different row everytime you query with "ACTIVE"
SELECT * FROM
( SELECT * FROM table
where rownum < 1000
and status = 'ACTIVE'
ORDER BY dbms_random.value )
WHERE rownum = 1
update the rows status after selecting it, If you can not update that means another transaction has already used it. Then You should try to get a new row and update its status. By the way, getting the same row by two different transaction possibility is 0.001 since rownum is 1000.
Someone told sample(x) is the fastest way you can.
But for me this method works slightly faster than sample(x) method.
It should take fraction of the second (0.2 in my case) no matter what is the size of the table. If it takes longer try to use hints (--+ leading(e) use_nl(e t) rowid(t)) can help
SELECT *
FROM My_User.My_Table
WHERE ROWID = (SELECT MAX(t.ROWID) KEEP(DENSE_RANK FIRST ORDER BY dbms_random.value)
FROM (SELECT o.Data_Object_Id,
e.Relative_Fno,
e.Block_Id + TRUNC(Dbms_Random.Value(0, e.Blocks)) AS Block_Id
FROM Dba_Extents e
JOIN Dba_Objects o ON o.Owner = e.Owner AND o.Object_Type = e.Segment_Type AND o.Object_Name = e.Segment_Name
WHERE e.Segment_Name = 'MY_TABLE'
AND(e.Segment_Type, e.Owner, e.Extent_Id) =
(SELECT MAX(e.Segment_Type) AS Segment_Type,
MAX(e.Owner) AS Owner,
MAX(e.Extent_Id) KEEP(DENSE_RANK FIRST ORDER BY Dbms_Random.Value) AS Extent_Id
FROM Dba_Extents e
WHERE e.Segment_Name = 'MY_TABLE'
AND e.Owner = 'MY_USER'
AND e.Segment_Type = 'TABLE')) e
JOIN My_User.My_Table t
ON t.Rowid BETWEEN Dbms_Rowid.Rowid_Create(1, Data_Object_Id, Relative_Fno, Block_Id, 0)
AND Dbms_Rowid.Rowid_Create(1, Data_Object_Id, Relative_Fno, Block_Id, 32767))
Version with retries when no rows returned:
WITH gen AS ((SELECT --+ inline leading(e) use_nl(e t) rowid(t)
MAX(t.ROWID) KEEP(DENSE_RANK FIRST ORDER BY dbms_random.value) Row_Id
FROM (SELECT o.Data_Object_Id,
e.Relative_Fno,
e.Block_Id + TRUNC(Dbms_Random.Value(0, e.Blocks)) AS Block_Id
FROM Dba_Extents e
JOIN Dba_Objects o ON o.Owner = e.Owner AND o.Object_Type = e.Segment_Type AND o.Object_Name = e.Segment_Name
WHERE e.Segment_Name = 'MY_TABLE'
AND(e.Segment_Type, e.Owner, e.Extent_Id) =
(SELECT MAX(e.Segment_Type) AS Segment_Type,
MAX(e.Owner) AS Owner,
MAX(e.Extent_Id) KEEP(DENSE_RANK FIRST ORDER BY Dbms_Random.Value) AS Extent_Id
FROM Dba_Extents e
WHERE e.Segment_Name = 'MY_TABLE'
AND e.Owner = 'MY_USER'
AND e.Segment_Type = 'TABLE')) e
JOIN MY_USER.MY_TABLE t ON t.ROWID BETWEEN Dbms_Rowid.Rowid_Create(1, Data_Object_Id, Relative_Fno, Block_Id, 0)
AND Dbms_Rowid.Rowid_Create(1, Data_Object_Id, Relative_Fno, Block_Id, 32767))),
Retries(Cnt, Row_Id) AS (SELECT 1, gen.Row_Id
FROM Dual
LEFT JOIN gen ON 1=1
UNION ALL
SELECT Cnt + 1, gen.Row_Id
FROM Retries
LEFT JOIN gen ON 1=1
WHERE Retries.Row_Id IS NULL AND Retries.Cnt < 10)
SELECT *
FROM MY_USER.MY_TABLE
WHERE ROWID = (SELECT Row_Id
FROM Retries
WHERE Row_Id IS NOT NULL)
Can you use pseudorandom rows?
select * from (
select * from ... where... order by ora_hash(rowid)
) where rownum<100

How do I calculate tables size in Oracle

Being used to (and potentially spoiled by) MSSQL, I'm wondering how I can get at tables size in Oracle 10g.
I have googled it so I'm now aware that I may not have as easy an option as sp_spaceused. Still the potential answers I got are most of the time outdated or don't work. Probably because I'm no DBA on the schema I'm working with.
Would anyone have solutions and or recommendations?
You might be interested in this query. It tells you how much space is allocated for each table taking into account the indexes and any LOBs on the table. Often you are interested to know "How much spaces the the Purchase Order table take, including any indexes" rather than just the table itself. You can always delve into the details. Note that this requires access to the DBA_* views.
COLUMN TABLE_NAME FORMAT A32
COLUMN OBJECT_NAME FORMAT A32
COLUMN OWNER FORMAT A10
SELECT
owner,
table_name,
TRUNC(sum(bytes)/1024/1024) Meg,
ROUND( ratio_to_report( sum(bytes) ) over () * 100) Percent
FROM
(SELECT segment_name table_name, owner, bytes
FROM dba_segments
WHERE segment_type IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
UNION ALL
SELECT i.table_name, i.owner, s.bytes
FROM dba_indexes i, dba_segments s
WHERE s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type IN ('INDEX', 'INDEX PARTITION', 'INDEX SUBPARTITION')
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.segment_name
AND s.owner = l.owner
AND s.segment_type IN ('LOBSEGMENT', 'LOB PARTITION')
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.index_name
AND s.owner = l.owner
AND s.segment_type = 'LOBINDEX')
WHERE owner in UPPER('&owner')
GROUP BY table_name, owner
HAVING SUM(bytes)/1024/1024 > 10 /* Ignore really small tables */
ORDER BY SUM(bytes) desc
;
-- Tables + Size MB
select owner, table_name, round((num_rows*avg_row_len)/(1024*1024)) MB
from all_tables
where owner not like 'SYS%' -- Exclude system tables.
and num_rows > 0 -- Ignore empty Tables.
order by MB desc -- Biggest first.
;
--Tables + Rows
select owner, table_name, num_rows
from all_tables
where owner not like 'SYS%' -- Exclude system tables.
and num_rows > 0 -- Ignore empty Tables.
order by num_rows desc -- Biggest first.
;
Note: These are estimates, made more accurate with gather statistics:
exec dbms_utility.analyze_schema(user,'COMPUTE');
First off, I would generally caution that gathering table statistics in order to do space analysis is a potentially dangerous thing to do. Gathering statistics may change query plans, particularly if the DBA has configured a statistics gathering job that uses non-default parameters that your call is not using, and will cause Oracle to re-parse queries that utilize the table in question which can be a performance hit. If the DBA has intentionally left some tables without statistics (common if your OPTIMIZER_MODE is CHOOSE), gathering statistics can cause Oracle to stop using the rule-based optimizer and start using the cost-based optimizer for a set of queries which can be a major performance headache if it is done unexpectedly in production. If your statistics are accurate, you can query USER_TABLES (or ALL_TABLES or DBA_TABLES) directly without calling GATHER_TABLE_STATS. If your statistics are not accurate, there is probably a reason for that and you don't want to disturb the status quo.
Second, the closest equivalent to the SQL Server sp_spaceused procedure is likely Oracle's DBMS_SPACE package. Tom Kyte has a nice show_space procedure that provides a simple interface to this package and prints out information similar to what sp_spaceused prints out.
First, gather optimiser stats on the table (if you haven't already):
begin
dbms_stats.gather_table_stats('MYSCHEMA','MYTABLE');
end;
/
WARNING: As Justin says in his answer, gathering optimiser stats affects query optimisation and should not be done without due care and consideration!
Then find the number of blocks occupied by the table from the generated stats:
select blocks, empty_blocks, num_freelist_blocks
from all_tables
where owner = 'MYSCHEMA'
and table_name = 'MYTABLE';
The total number of blocks allocated to the table is blocks + empty_blocks + num_freelist_blocks.
blocks is the number of blocks that actually contain data.
Multiply the number of blocks by the block size in use (usually 8KB) to get the space consumed - e.g. 17 blocks x 8KB = 136KB.
To do this for all tables in a schema at once:
begin
dbms_stats.gather_schema_stats ('MYSCHEMA');
end;
/
select table_name, blocks, empty_blocks, num_freelist_blocks
from user_tables;
Note: Changes made to the above after reading this AskTom thread
I modified the WW's query to provide more detailed information:
SELECT * FROM (
SELECT
owner, object_name, object_type, table_name, ROUND(bytes)/1024/1024 AS meg,
tablespace_name, extents, initial_extent,
ROUND(Sum(bytes/1024/1024) OVER (PARTITION BY table_name)) AS total_table_meg
FROM (
-- Tables
SELECT owner, segment_name AS object_name, 'TABLE' AS object_type,
segment_name AS table_name, bytes,
tablespace_name, extents, initial_extent
FROM dba_segments
WHERE segment_type IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
UNION ALL
-- Indexes
SELECT i.owner, i.index_name AS object_name, 'INDEX' AS object_type,
i.table_name, s.bytes,
s.tablespace_name, s.extents, s.initial_extent
FROM dba_indexes i, dba_segments s
WHERE s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type IN ('INDEX', 'INDEX PARTITION', 'INDEX SUBPARTITION')
-- LOB Segments
UNION ALL
SELECT l.owner, l.column_name AS object_name, 'LOB_COLUMN' AS object_type,
l.table_name, s.bytes,
s.tablespace_name, s.extents, s.initial_extent
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.segment_name
AND s.owner = l.owner
AND s.segment_type = 'LOBSEGMENT'
-- LOB Indexes
UNION ALL
SELECT l.owner, l.column_name AS object_name, 'LOB_INDEX' AS object_type,
l.table_name, s.bytes,
s.tablespace_name, s.extents, s.initial_extent
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.index_name
AND s.owner = l.owner
AND s.segment_type = 'LOBINDEX'
)
WHERE owner = UPPER('&owner')
)
WHERE total_table_meg > 10
ORDER BY total_table_meg DESC, meg DESC
/
IIRC the tables you need are DBA_TABLES, DBA_EXTENTS or DBA_SEGMENTS and DBA_DATA_FILES. There are also USER_ and ALL_ versions of these for tables you can see if you don't have administration permissions on the machine.
For sub partitioned tables and indexes we can use the following query
SELECT owner, table_name, ROUND(sum(bytes)/1024/1024/1024, 2) GB
FROM
(SELECT segment_name table_name, owner, bytes
FROM dba_segments
WHERE segment_type IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
UNION ALL
SELECT i.table_name, i.owner, s.bytes
FROM dba_indexes i, dba_segments s
WHERE s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type IN ('INDEX', 'INDEX PARTITION', 'INDEX SUBPARTITION')
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.segment_name
AND s.owner = l.owner
AND s.segment_type = 'LOBSEGMENT'
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.index_name
AND s.owner = l.owner
AND s.segment_type = 'LOBINDEX')
WHERE owner in UPPER('&owner')
GROUP BY table_name, owner
HAVING SUM(bytes)/1024/1024 > 10 /* Ignore really small tables */
ORDER BY SUM(bytes) DESC
;
Heres a variant on WWs answer, it includes partitions and sub-partitions as others above have suggested, plus a column to show the TYPE: Table/Index/LOB etc
SELECT
owner, "Type", table_name "Name", TRUNC(sum(bytes)/1024/1024) Meg
FROM
( SELECT segment_name table_name, owner, bytes, 'Table' as "Type"
FROM dba_segments
WHERE segment_type in ('TABLE','TABLE PARTITION','TABLE SUBPARTITION')
UNION ALL
SELECT i.table_name, i.owner, s.bytes, 'Index' as "Type"
FROM dba_indexes i, dba_segments s
WHERE s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type in ('INDEX','INDEX PARTITION','INDEX SUBPARTITION')
UNION ALL
SELECT l.table_name, l.owner, s.bytes, 'LOB' as "Type"
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.segment_name
AND s.owner = l.owner
AND s.segment_type IN ('LOBSEGMENT','LOB PARTITION','LOB SUBPARTITION')
UNION ALL
SELECT l.table_name, l.owner, s.bytes, 'LOB Index' as "Type"
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.index_name
AND s.owner = l.owner
AND s.segment_type = 'LOBINDEX')
WHERE owner in UPPER('&owner')
GROUP BY table_name, owner, "Type"
HAVING SUM(bytes)/1024/1024 > 10 /* Ignore really small tables */
ORDER BY SUM(bytes) desc;
select segment_name,segment_type,bytes/1024/1024 MB
from dba_segments
where segment_name='TABLENAME' and owner ='OWNERNAME' order by mb desc;
I modified the query to get the schema size per tablespace ..
SELECT owner,
tablespace_name,
TRUNC (SUM (bytes) / 1024 / 1024) Meg,
ROUND (ratio_to_report (SUM (bytes)) OVER () * 100) Percent
FROM (SELECT tablespace_name, owner, bytes
FROM dba_segments
WHERE segment_type IN
('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
UNION ALL
SELECT i.tablespace_name, i.owner, s.bytes
FROM dba_indexes i, dba_segments s
WHERE s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type IN
('INDEX', 'INDEX PARTITION', 'INDEX SUBPARTITION')
UNION ALL
SELECT l.tablespace_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.segment_name
AND s.owner = l.owner
AND s.segment_type IN ('LOBSEGMENT', 'LOB PARTITION')
UNION ALL
SELECT l.tablespace_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.index_name
AND s.owner = l.owner
AND s.segment_type = 'LOBINDEX')
WHERE owner IN UPPER ('&owner')
GROUP BY owner, tablespace_name
--HAVING SUM(bytes)/1024/1024 > 10 /* Ignore really small tables */
ORDER BY tablespace_name -- desc
;
Depends what you mean by "table's size".
A table doesn't relate to a specific file on the file system. A table will reside on a tablespace (possibly multiple tablespaces if it is partitioned, and possibly multiple tablespaces if you also want to take into account indexes on the table).
A tablespace will often have multiple tables in it, and may be spread across multiple files.
If you are estimating how much space you'll need for the table's future growth, then avg_row_len multiplied by the number of rows in the table (or number of rows you expect in the table) will be a good guide.
But Oracle will leave some space free on each block, partly to allow for rows to 'grow' if they are updated, partly because it may not be possible to fit another entire row on that block (eg an 8K block would only fit 2 rows of 3K, though that would be an extreme example as 3K is a lot bigger than most row sizes).
So BLOCKS (in USER_TABLES) might be a better guide.
But if you had 200,000 rows in a table, deleted half of them, then the table would still 'own' the same number of blocks. It doesn't release them up for other tables to use.
Also, blocks are not added to a table individually, but in groups called an 'extent'. So there are generally going to be EMPTY_BLOCKS (also in USER_TABLES) in a table.
Correction for partitioned tables:
SELECT owner, table_name, ROUND(sum(bytes)/1024/1024/1024, 2) GB
FROM
(SELECT segment_name table_name, owner, bytes
FROM dba_segments
WHERE segment_type IN ('TABLE', 'TABLE PARTITION', 'TABLE SUBPARTITION')
UNION ALL
SELECT i.table_name, i.owner, s.bytes
FROM dba_indexes i, dba_segments s
WHERE s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type IN ('INDEX', 'INDEX PARTITION', 'INDEX SUBPARTITION')
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.segment_name
and s.owner = l.owner
AND s.segment_type in ('LOBSEGMENT', 'LOB PARTITION', 'LOB SUBPARTITION')
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.index_name
AND s.owner = l.owner
AND s.segment_type = 'LOBINDEX')
WHERE owner in UPPER('&owner')
GROUP BY table_name, owner
HAVING SUM(bytes)/1024/1024 > 10 /* Ignore really small tables */
order by sum(bytes) desc
;
Simple select that returns the raw sizes of the tables, based on the block size, also includes size with index
select table_name,(nvl
((
select sum( blocks)
from dba_indexes a,dba_segments b
where a.index_name=b.segment_name
and a.table_name=dba_tables.table_name
),0)+blocks)*8192/1024 TotalSize,blocks*8 tableSize
from dba_tables
order by 3
I found this to be a little more accurate:
SELECT
owner, table_name, TRUNC(sum(bytes)/1024/1024/1024) GB
FROM
(SELECT segment_name table_name, owner, bytes
FROM dba_segments
WHERE segment_type in ('TABLE','TABLE PARTITION')
UNION ALL
SELECT i.table_name, i.owner, s.bytes
FROM dba_indexes i, dba_segments s
WHERE s.segment_name = i.index_name
AND s.owner = i.owner
AND s.segment_type in ('INDEX','INDEX PARTITION')
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.segment_name
AND s.owner = l.owner
AND s.segment_type IN ('LOBSEGMENT','LOB PARTITION')
UNION ALL
SELECT l.table_name, l.owner, s.bytes
FROM dba_lobs l, dba_segments s
WHERE s.segment_name = l.index_name
AND s.owner = l.owner
AND s.segment_type = 'LOBINDEX')
---WHERE owner in UPPER('&owner')
GROUP BY table_name, owner
HAVING SUM(bytes)/1024/1024 > 10 /* Ignore really small tables */
ORDER BY SUM(bytes) desc
select segment_name as tablename, sum(bytes/ (1024 * 1024 * 1024)) as tablesize_in_GB
From dba_segments /* if looking at tables not owned by you else use user_segments */
where segment_name = 'TABLE_WHOSE_SIZE_I_WANT_TO_KNOW'
and OWNER = 'WHO OWNS THAT TABLE' /* if user_segments is used delete this line */
group by segment_name ;
there one more option that allows to get "select" size with joins, and table size as option too
-- 1
EXPLAIN PLAN
FOR
SELECT
Scheme.Table_name.table_column1 AS "column1",
Scheme.Table_name.table_column2 AS "column2",
Scheme.Table_name.table_column3 AS "column3",
FROM Scheme.Table_name
WHERE ;
SELECT * FROM TABLE (DBMS_XPLAN.display);
I have the same variant as the last ones which calculates segments of table data, table indexes and blob-fields:
CREATE OR REPLACE FUNCTION
SYS.RAZMER_TABLICY_RAW(pNazvanie in varchar, pOwner in varchar2)
return number
is
val number(16);
sz number(16);
begin
sz := 0;
--Calculate size of table data segments
select
sum(t.bytes) into val
from
sys.dba_segments t
where
t.segment_name = upper(pNazvanie)
and
t.owner = upper(pOwner);
sz := sz + nvl(val,0);
--Calculate size of table indexes segments
select
sum(s.bytes) into val
from
all_indexes t
inner join
dba_segments s
on
t.index_name = s.segment_name
where
t.table_name = upper(pNazvanie)
and
t.owner = upper(pOwner);
sz := sz + nvl(val,0);
--Calculate size of table blob segments
select
sum(s.bytes) into val
from
all_lobs t
inner join
dba_segments s on t.segment_name = s.segment_name
where
t.table_name = upper(pNazvanie)
and
t.owner = upper(pOwner);
sz := sz + nvl(val,0);
return sz;
end razmer_tablicy_raw;
Source.

Resources