Wie berechne ich die Tabellengröße in Oracle?

128

Ich bin an MSSQL gewöhnt (und möglicherweise von MSSQL verwöhnt) und frage mich, wie ich in Oracle 10g die Tabellengröße erreichen kann. Ich habe es gegoogelt, daher ist mir jetzt bewusst, dass ich möglicherweise keine so einfache Option wie sp_spaceused habe. Trotzdem sind die möglichen Antworten, die ich erhalten habe, meistens veraltet oder funktionieren nicht. Wahrscheinlich, weil ich kein DBA für das Schema bin, mit dem ich arbeite.

Hätte jemand Lösungen und / oder Empfehlungen?

Rollo Tomazzi
quelle
Wenn ein Proc die Antwort verdirbt, nehmen Sie die Antworten, die Sie von hier erhalten haben, und wickeln Sie sie in eine Prozedur ein und nennen Sie sie ... dun dun duh ... sp_spaceused. Es ist wirklich wenig Magie.
1
@MarkBrady Vielleicht nicht Magie, sondern eine Tonne arkane Wissen erforderlich ist .
jpmc26

Antworten:

201

Diese Abfrage könnte Sie interessieren. Hier erfahren Sie, wie viel Speicherplatz für jede Tabelle unter Berücksichtigung der Indizes und aller LOBs in der Tabelle zugewiesen ist. Oft sind Sie daran interessiert zu wissen, "wie viel Speicherplatz die Bestellungstabelle einschließlich etwaiger Indizes belegt" und nicht nur die Tabelle selbst. Sie können jederzeit in die Details eintauchen. Beachten Sie, dass hierfür Zugriff auf die DBA_ * -Ansichten erforderlich ist.

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
;
WW.
quelle
1
Beachten Sie, dass diese Antwort Segmente zählt, bei denen nicht zwischen aktuell verwendetem Speicherplatz und zuvor verwendetem Speicherplatz unterschieden wird. Sobald ein Segment einer Tabelle zugewiesen ist, wird es anscheinend immer einer Tabelle zugewiesen, selbst wenn der Speicherplatz freigegeben wird. Siehe hier . Ich denke, Sie müssen bis zur Ausdehnung gehen, um zu sehen, wie viel Platz tatsächlich genutzt wird ?
jpmc26
43
-- 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.
;

Hinweis: Dies sind Schätzungen, die mit Erfassungsstatistiken genauer gemacht wurden:

exec dbms_utility.analyze_schema(user,'COMPUTE');
Grokster
quelle
2
Diese Statistiken können null( num_rows, avg_row_len) sein, müssen Sie einige Analyse vor über die folgende Aussage machenANALYZE TABLE your_table COMPUTE STATISTICS
Brice
Eine schwierige Analyse kann sehr lang sein!
Brice
Gute Arbeit, wenn ich keine Tabelle
ohne
30

Zunächst einmal würde ich generell darauf hinweisen, dass das Sammeln von Tabellenstatistiken zur Durchführung von Raumanalysen eine potenziell gefährliche Sache ist. Das Sammeln von Statistiken kann Abfragepläne ändern, insbesondere wenn der DBA einen Job zum Sammeln von Statistiken konfiguriert hat, der nicht standardmäßige Parameter verwendet, die Ihr Aufruf nicht verwendet, und Oracle veranlasst, Abfragen, die die betreffende Tabelle verwenden, erneut zu analysieren, was eine Leistung sein kann schlagen. Wenn der DBA absichtlich einige Tabellen ohne Statistiken belassen hat (häufig, wenn Sie OPTIMIZER_MODEAUSGEWÄHLT haben), kann das Sammeln von Statistiken dazu führen, dass Oracle den regelbasierten Optimierer nicht mehr verwendet und den kostenbasierten Optimierer für eine Reihe von Abfragen verwendet, die eine erhebliche Leistung erbringen können Kopfschmerzen, wenn es unerwartet in der Produktion gemacht wird. Wenn Ihre Statistiken korrekt sind, können Sie direkt abfragen, ohne anzurufenUSER_TABLES (oder ALL_TABLESoderDBA_TABLESGATHER_TABLE_STATS . Wenn Ihre Statistiken nicht korrekt sind, gibt es wahrscheinlich einen Grund dafür und Sie möchten den Status Quo nicht stören.

Zweitens sp_spaceusedist das DBMS_SPACEPaket von Oracle wahrscheinlich das nächste Äquivalent zur SQL Server- Prozedur . Tom Kyte hat eine nette show_spaceProzedur , die eine einfache Schnittstelle zu diesem Paket bietet und Informationen druckt, die denen ähneln, die sp_spaceusedausgedruckt werden.

Justin Cave
quelle
8

Sammeln Sie zunächst die Optimierungsstatistiken auf dem Tisch (falls Sie dies noch nicht getan haben):

begin
   dbms_stats.gather_table_stats('MYSCHEMA','MYTABLE');
end;
/

WARNUNG: Wie Justin in seiner Antwort sagt, wirkt sich das Sammeln von Optimierungsstatistiken auf die Abfrageoptimierung aus und sollte nicht ohne Sorgfalt und Rücksichtnahme erfolgen !

Ermitteln Sie dann aus den generierten Statistiken die Anzahl der von der Tabelle belegten Blöcke:

select blocks, empty_blocks, num_freelist_blocks
from   all_tables
where  owner = 'MYSCHEMA'
and    table_name = 'MYTABLE';
  • Die Gesamtzahl der der Tabelle zugewiesenen Blöcke beträgt Blöcke + leere Blöcke + num_freelist_blocks.

  • Blöcke ist die Anzahl der Blöcke, die tatsächlich Daten enthalten.

Multiplizieren Sie die Anzahl der Blöcke mit der verwendeten Blockgröße (normalerweise 8 KB), um den verbrauchten Speicherplatz zu erhalten - z. B. 17 Blöcke x 8 KB = 136 KB.

So tun Sie dies für alle Tabellen in einem Schema gleichzeitig:

begin
    dbms_stats.gather_schema_stats ('MYSCHEMA');
end;
/

select table_name, blocks, empty_blocks, num_freelist_blocks
from   user_tables;

Hinweis: Nach dem Lesen dieses AskTom-Threads wurden Änderungen an den oben genannten vorgenommen

Tony Andrews
quelle
7

Ich habe die WW-Abfrage geändert, um detailliertere Informationen bereitzustellen:

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
/
Sergey Stadnik
quelle
6

Für unterpartitionierte Tabellen und Indizes können wir die folgende Abfrage verwenden



    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
    ;
rratina
quelle
5

IIRC Die Tabellen, die Sie benötigen, sind DBA_TABLES, DBA_EXTENTS oder DBA_SEGMENTS und DBA_DATA_FILES. Es gibt auch USER_- und ALL_-Versionen für Tabellen, die Sie sehen können, wenn Sie keine Administratorrechte auf dem Computer haben.

ConcernedOfTunbridgeWells
quelle
4

Hier ist eine Variante der WWs-Antwort, die Partitionen und Unterpartitionen enthält, wie andere oben vorgeschlagen haben, sowie eine Spalte, in der der TYP angezeigt wird: Tabelle / Index / LOB usw.

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;
SS64
quelle
3
select segment_name,segment_type,bytes/1024/1024 MB
from dba_segments
where segment_name='TABLENAME' and owner ='OWNERNAME' order by mb desc;
Bronx
quelle
2

Ich habe die Abfrage geändert, um die Schemagröße pro Tabellenbereich zu erhalten.

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
;
Najee Ghanim
quelle
1

Kommt darauf an, was du mit "Tabellengröße" meinst. Eine Tabelle bezieht sich nicht auf eine bestimmte Datei im Dateisystem. Eine Tabelle befindet sich in einem Tabellenbereich (möglicherweise mehrere Tabellenbereiche, wenn sie partitioniert ist, und möglicherweise mehrere Tabellenbereiche, wenn Sie auch Indizes für die Tabelle berücksichtigen möchten). Ein Tablespace enthält häufig mehrere Tabellen und kann auf mehrere Dateien verteilt sein.

Wenn Sie schätzen, wie viel Speicherplatz Sie für das zukünftige Wachstum der Tabelle benötigen, ist avg_row_len multipliziert mit der Anzahl der Zeilen in der Tabelle (oder der Anzahl der Zeilen, die Sie in der Tabelle erwarten) ein guter Leitfaden. Oracle lässt jedoch auf jedem Block etwas Speicherplatz frei, teilweise, damit Zeilen bei einer Aktualisierung "wachsen" können, teilweise, weil möglicherweise keine weitere ganze Zeile in diesen Block eingefügt werden kann (z. B. würde ein 8-KB-Block nur für 2 Zeilen passen von 3K, obwohl dies ein extremes Beispiel wäre, da 3K viel größer ist als die meisten Zeilengrößen). BLOCKS (in USER_TABLES) könnten daher eine bessere Anleitung sein.

Wenn Sie jedoch 200.000 Zeilen in einer Tabelle hätten und die Hälfte davon löschen würden, würde die Tabelle immer noch die gleiche Anzahl von Blöcken "besitzen". Sie werden nicht für andere Tabellen freigegeben. Außerdem werden Blöcke nicht einzeln zu einer Tabelle hinzugefügt, sondern in Gruppen, die als "Extent" bezeichnet werden. Daher wird es in der Regel EMPTY_BLOCKS (auch in USER_TABLES) in einer Tabelle geben.

Gary Myers
quelle
1

Korrektur für partitionierte Tabellen:

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
;
rratina
quelle
0

Die einfache Auswahl, die die Rohgrößen der Tabellen basierend auf der Blockgröße zurückgibt, enthält auch die Größe mit Index

Wählen Sie Tabellenname, (nvl ((Summe (Blöcke) aus dba_indexes a, dba_segments b auswählen, wobei a.index_name = b.segment_name und a.table_name = dba_tables.table_name), 0) + Blöcke) * 8192/1024 TotalSize, Blöcke * 8 tableSize von dba_tables nach 3 sortieren

Noam
quelle
0

Ich fand das etwas genauer:

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
Geoffrey Musafu
quelle
7
Sieht irgendwie nach meiner Antwort aus?
WW.
0
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 ;
Vijay Chettiar
quelle
-2

Es gibt noch eine Option, mit der Sie die Größe mit Joins auswählen und die Tabellengröße als Option auswählen können

-- 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);
mrvlad
quelle
-3

Ich habe die gleiche Variante wie die letzten, die Segmente von Tabellendaten, Tabellenindizes und Blob-Feldern berechnet:

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;

Quelle .

user2498491
quelle