Jak obliczyć rozmiar tabel w Oracle

130

Będąc przyzwyczajonym do MSSQL (i potencjalnie zepsutym przez), zastanawiam się, jak uzyskać rozmiar tabel w Oracle 10g. Przeszukałem go w Google, więc teraz jestem świadomy, że mogę nie mieć tak łatwej opcji jak sp_spaceused. Jednak potencjalne odpowiedzi, które otrzymałem, są w większości nieaktualne lub nie działają. Prawdopodobnie dlatego, że nie jestem DBA w schemacie, z którym pracuję.

Czy ktoś miałby rozwiązania i / lub zalecenia?

Rollo Tomazzi
źródło
jeśli procedura udzielania odpowiedzi jest zepsuta, weź odpowiedzi, które otrzymałeś stąd, zawiń je w procedurę i nazwij to ... dun dun duh ... sp_spaceused. Jest w tym naprawdę mało magii.
2
@MarkBrady Może nie magia, lecz ton wymagana jest od tajemnej wiedzy.
jpmc26

Odpowiedzi:

203

Możesz być zainteresowany tym zapytaniem. Informuje, ile miejsca jest przydzielone dla każdej tabeli, biorąc pod uwagę indeksy i wszelkie LOB w tabeli. Często interesuje Cię „Ile miejsca zajmuje tabela zamówień zakupu, w tym wszelkie indeksy”, a nie tylko sama tabela. Zawsze możesz zagłębić się w szczegóły. Należy pamiętać, że wymaga to dostępu do widoków DBA_ *.

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
;
W W.
źródło
1
Zwróć uwagę, że ta odpowiedź obejmuje segmenty, co nie rozróżnia miejsca, które jest aktualnie używane, od miejsca, które było wcześniej używane. Najwyraźniej po przypisaniu segmentu do tabeli jest on zawsze przypisywany do tabeli, nawet jeśli miejsce jest zwolnione. Zobacz tutaj . Myślę, że musisz zejść do poziomu, aby zobaczyć, ile miejsca jest faktycznie używane ?
jpmc26
44
-- 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.
;

Uwaga: są to szacunki, dokładniejsze dzięki statystykom gromadzenia:

exec dbms_utility.analyze_schema(user,'COMPUTE');
grokster
źródło
2
Te statystyki mogą być null( num_rows, avg_row_len), musisz wcześniej dokonać analizy za pomocą następującego oświadczeniaANALYZE TABLE your_table COMPUTE STATISTICS
Brice,
Trudna analiza może być bardzo długa!
Brice,
niezła praca, gdy nie mogę sprawdzić tabeli bez przestrzeni tabel
tungns
30

Po pierwsze, generalnie ostrzegam, że zbieranie statystyk tabeli w celu przeprowadzenia analizy przestrzeni jest potencjalnie niebezpieczną rzeczą. Gromadzenie statystyk może zmienić plany zapytań, szczególnie jeśli administrator skonfigurował zadanie gromadzenia statystyk, które używa parametrów innych niż domyślne, których nie używa Twoje wywołanie, i spowoduje, że Oracle ponownie przeanalizuje zapytania wykorzystujące daną tabelę, co może być wydajnością trafienie. Jeśli DBA celowo pozostawił niektóre tabele bez statystyk (często w przypadku OPTIMIZER_MODEWYBIERZ), gromadzenie statystyk może spowodować, że Oracle przestanie używać optymalizatora regułowego i zacznie używać optymalizatora kosztowego dla zestawu zapytań, które mogą być bardzo wydajne ból głowy, jeśli dzieje się to nieoczekiwanie w produkcji. Jeśli twoje statystyki są dokładne, możesz zapytać USER_TABLES(lub ALL_TABLESlubDBA_TABLES) bezpośrednio bez dzwonienia GATHER_TABLE_STATS. Jeśli twoje statystyki nie są dokładne, prawdopodobnie jest ku temu powód i nie chcesz naruszać status quo.

Po drugie, najbliższym odpowiednikiem sp_spaceusedprocedury SQL Server jest prawdopodobnie DBMS_SPACEpakiet Oracle . Tom Kyte ma przyjemną show_spaceprocedurę, która zapewnia prosty interfejs do tego pakietu i wypisuje informacje podobne do tych, które są sp_spaceuseddrukowane.

Justin Cave
źródło
8

Najpierw zbierz statystyki optymalizatora na stole (jeśli jeszcze tego nie zrobiłeś):

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

OSTRZEŻENIE: Jak mówi Justin w swojej odpowiedzi, zbieranie statystyk optymalizatora wpływa na optymalizację zapytań i nie powinno być wykonywane bez należytej uwagi i rozwagi !

Następnie znajdź liczbę bloków zajmowanych przez stół z wygenerowanych statystyk:

select blocks, empty_blocks, num_freelist_blocks
from   all_tables
where  owner = 'MYSCHEMA'
and    table_name = 'MYTABLE';
  • Całkowita liczba bloków przydzielonych do tabeli to bloki + empty_blocks + num_freelist_blocks.

  • bloki to liczba bloków, które faktycznie zawierają dane.

Pomnóż liczbę bloków przez rozmiar używanego bloku (zwykle 8 KB), aby uzyskać zajęte miejsce - np. 17 bloków x 8 KB = 136 KB.

Aby zrobić to jednocześnie dla wszystkich tabel w schemacie:

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

select table_name, blocks, empty_blocks, num_freelist_blocks
from   user_tables;

Uwaga: zmiany wprowadzone w powyższym po przeczytaniu tego wątku AskTom

Tony Andrews
źródło
7

Zmodyfikowałem zapytanie WW, aby podać bardziej szczegółowe informacje:

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
źródło
6

W przypadku tabel i indeksów podzielonych na partycje możemy użyć następującego zapytania



    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
źródło
5

IIRC potrzebne tabele to DBA_TABLES, DBA_EXTENTS lub DBA_SEGMENTS i DBA_DATA_FILES. Istnieją również wersje USER_ i ALL_ tych tabel, które możesz sprawdzić, jeśli nie masz uprawnień administracyjnych na komputerze.

ConcernedOfTunbridgeWells
źródło
4

Oto wariant odpowiedzi WWs, zawiera partycje i pod-partycje, jak sugerowali inni powyżej, oraz kolumnę pokazującą TYP: Tabela / Indeks / LOB itp.

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
źródło
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
źródło
2

Zmodyfikowałem zapytanie, aby uzyskać rozmiar schematu na przestrzeń tabel.

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
źródło
1

Zależy, co rozumiesz przez „rozmiar stołu”. Tabela nie odnosi się do konkretnego pliku w systemie plików. Tabela będzie znajdować się w obszarze tabel (prawdopodobnie w wielu obszarach tabel, jeśli jest podzielona na partycje, i prawdopodobnie w wielu obszarach tabel, jeśli chcesz również uwzględnić indeksy w tabeli). Obszar tabel często zawiera wiele tabel i może być rozłożony na wiele plików.

Jeśli szacujesz, ile miejsca będziesz potrzebować do przyszłego wzrostu tabeli, dobrym przewodnikiem będzie avg_row_len pomnożone przez liczbę wierszy w tabeli (lub liczbę wierszy, których oczekujesz w tabeli). Jednak Oracle pozostawi trochę wolnego miejsca na każdym bloku, częściowo po to, aby wiersze mogły „rosnąć”, jeśli zostaną zaktualizowane, częściowo dlatego, że może nie być możliwe zmieszczenie innego całego wiersza w tym bloku (np. Blok 8K zmieściłby się tylko w 2 wierszach 3K, chociaż byłby to skrajny przykład, ponieważ 3K jest dużo większe niż większość rozmiarów wierszy). Więc BLOCKS (w USER_TABLES) mogą być lepszym przewodnikiem.

Ale gdybyś miał 200 000 wierszy w tabeli i usunął połowę z nich, to tabela nadal „posiadała” taką samą liczbę bloków. Nie zwalnia ich do użycia w innych stołach. Ponadto bloki nie są dodawane do tabeli indywidualnie, ale w grupach zwanych „zakresem”. Więc generalnie w tabeli będzie EMPTY_BLOCKS (także w USER_TABLES).

Gary Myers
źródło
1

Poprawka dla podzielonych tabel:

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
źródło
0

Prosty wybór, który zwraca surowe rozmiary tabel na podstawie rozmiaru bloku, obejmuje również rozmiar z indeksem

select table_name, (nvl ((select sum (bloki) 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 z kolejności dba_tables o 3

Noam
źródło
0

Uważam, że jest to trochę dokładniejsze:

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
źródło
7
Wygląda trochę jak moja odpowiedź?
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
źródło
-2

jest jeszcze jedna opcja, która pozwala uzyskać "wybierz" rozmiar z łączeniami, a także rozmiar tabeli jako opcję

-- 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
źródło
-3

Mam ten sam wariant co ostatni, który oblicza segmenty danych tabeli, indeksy tabeli i pola blob:

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;

Źródło .

user2498491
źródło