Как рассчитать размер таблиц в Oracle


128

Привыкнув к MSSQL (и потенциально испорченный им), мне интересно, как я могу достичь размера таблиц в Oracle 10g. Я погуглил, поэтому теперь понимаю, что у меня может быть не такой простой вариант, как sp_spaceused. Тем не менее потенциальные ответы, которые я получил, в большинстве случаев устарели или не работают. Вероятно, потому что я не администратор баз данных по схеме, с которой работаю.

Есть ли у кого-нибудь решения или рекомендации?


если процедура выдачи ответа испорчена, тогда возьмите ответы, которые вы получили отсюда, и оберните их в процедуру и назовите ее ... dun dun duh ... sp_spaceused. В этом действительно мало волшебства.

1
@MarkBrady Может быть, и не магия, но требуется тонна тайных знаний.
jpmc26,

Ответы:


201

Возможно, вам будет интересен этот запрос. Он сообщает вам, сколько места выделено для каждой таблицы, с учетом индексов и любых больших объектов в таблице. Часто вас интересует, «Сколько места занимает таблица заказа на закупку, включая любые индексы», а не только сама таблица. Вы всегда можете вникнуть в детали. Обратите внимание, что для этого требуется доступ к представлениям 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
;

1
Обратите внимание, что в этом ответе учитываются сегменты, при этом не проводится различие между пространством, которое используется в настоящее время, и пространством, которое использовалось ранее. Очевидно, что как только сегмент назначается таблице, он всегда назначается таблице, даже если пространство освобождено. Смотрите здесь . Я полагаю, вам нужно спуститься до уровня экстента, чтобы увидеть, сколько места фактически используется ?
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.
;

Примечание. Это оценки, которые стали более точными с помощью сбора статистики:

exec dbms_utility.analyze_schema(user,'COMPUTE');

2
Эти статистические данные могут быть null( num_rows, avg_row_len), вам необходимо провести некоторый анализ перед этим с помощью следующего утвержденияANALYZE TABLE your_table COMPUTE STATISTICS
Брайс

Трудно их анализировать, возможно, очень долго!
Brice

хорошая работа, когда я не могу проверить таблицу без табличного пространства
tungns304 09

30

Во-первых, я бы хотел предупредить, что сбор статистики таблиц для анализа пространства - это потенциально опасное занятие. Сбор статистики может изменить планы запросов, особенно если администратор баз данных настроил задание по сбору статистики, которое использует параметры, отличные от параметров по умолчанию, которые не используются вашим вызовом, и заставит Oracle повторно анализировать запросы, использующие рассматриваемую таблицу, что может быть производительностью ударить. Если администратор базы данных намеренно оставил некоторые таблицы без статистики (обычно, если вы OPTIMIZER_MODEвыбрали CHOOSE), сбор статистики может привести к тому, что Oracle прекратит использование оптимизатора на основе правил и начнет использовать оптимизатор на основе затрат для набора запросов, что может быть основной производительностью. головная боль, если это происходит неожиданно на производстве. Если ваша статистика точна, вы можете запросить USER_TABLES(или ALL_TABLESилиDBA_TABLES) напрямую без звонка GATHER_TABLE_STATS. Если ваша статистика неточна, вероятно, для этого есть причина, и вы не хотите нарушать статус-кво.

Во-вторых, наиболее близким эквивалентом sp_spaceusedпроцедуры SQL Server, вероятно, является DBMS_SPACEпакет Oracle . У Тома Кайта есть хорошая show_spaceпроцедура, которая обеспечивает простой интерфейс для этого пакета и выводит на печать информацию, аналогичную той, что sp_spaceusedвыводится.


8

Сначала соберите статистику оптимизатора в таблице (если вы еще этого не сделали):

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

ВНИМАНИЕ: как сказал Джастин в своем ответе, сбор статистики оптимизатора влияет на оптимизацию запросов и не должен выполняться без должной осторожности и внимания !

Затем найдите количество блоков, занятых таблицей, из сгенерированной статистики:

select blocks, empty_blocks, num_freelist_blocks
from   all_tables
where  owner = 'MYSCHEMA'
and    table_name = 'MYTABLE';
  • Общее количество блоков, выделенных для таблицы, составляет блоки + empty_blocks + num_freelist_blocks.

  • блоки - это количество блоков, которые фактически содержат данные.

Умножьте количество блоков на размер используемого блока (обычно 8 КБ), чтобы получить занимаемое пространство - например, 17 блоков x 8 КБ = 136 КБ.

Чтобы сделать это сразу для всех таблиц в схеме:

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

select table_name, blocks, empty_blocks, num_freelist_blocks
from   user_tables;

Примечание: изменения, внесенные в приведенное выше после прочтения этой ветки AskTom


7

Я изменил запрос WW, чтобы предоставить более подробную информацию:

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
/

6

Для суб-секционированных таблиц и индексов мы можем использовать следующий запрос



    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
    ;

5

IIRC, вам нужны таблицы DBA_TABLES, DBA_EXTENTS или DBA_SEGMENTS и DBA_DATA_FILES. Существуют также версии USER_ и ALL_ для таблиц, которые вы можете увидеть, если у вас нет прав администратора на машине.


4

Вот вариант ответа WW, он включает в себя разделы и подразделы, как предлагали другие выше, плюс столбец для отображения ТИПА: Таблица / Индекс / LOB и т. Д.

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;

3
select segment_name,segment_type,bytes/1024/1024 MB
from dba_segments
where segment_name='TABLENAME' and owner ='OWNERNAME' order by mb desc;

2

Я изменил запрос, чтобы получить размер схемы для каждого табличного пространства.

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
;

1

Зависит от того, что вы подразумеваете под «размером стола». Таблица не относится к конкретному файлу в файловой системе. Таблица будет находиться в табличном пространстве (возможно, в нескольких табличных пространствах, если она разбита на разделы, и, возможно, в нескольких табличных пространствах, если вы также хотите учитывать индексы в таблице). Табличное пространство часто будет содержать несколько таблиц и может быть распределено по нескольким файлам.

Если вы оцениваете, сколько места вам понадобится для будущего роста таблицы, тогда avg_row_len, умноженный на количество строк в таблице (или количество строк, которые вы ожидаете в таблице), будет хорошим ориентиром. Но Oracle оставит некоторое пространство свободным в каждом блоке, отчасти для того, чтобы строки могли `` расти '' при их обновлении, отчасти потому, что может оказаться невозможным уместить еще одну целую строку в этом блоке (например, блок 8K поместится только в 2 строки 3 КБ, хотя это был бы крайний пример, поскольку 3 КБ намного больше, чем большинство размеров строк). Так что BLOCKS (в USER_TABLES) может быть лучшим руководством.

Но если бы у вас было 200000 строк в таблице, вы удалили половину из них, тогда таблица все равно «владеет» тем же количеством блоков. Это не освобождает их для использования другими таблицами. Кроме того, блоки добавляются в таблицу не по отдельности, а в группах, называемых «экстентом». Таким образом, в таблице обычно будет EMPTY_BLOCKS (также в USER_TABLES).


1

Исправление для секционированных таблиц:

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
;

0

Простой выбор, который возвращает исходные размеры таблиц в зависимости от размера блока, также включает размер с индексом

select table_name, (nvl ((select sum (blocks) from dba_indexes a, dba_segments b, где a.index_name = b.segment_name и a.table_name = dba_tables.table_name), 0) + blocks) * 8192/1024 TotalSize, блоки * 8 tableSize из dba_tables в порядке 3


0

Я обнаружил, что это немного точнее:

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

7
Похоже на мой ответ?
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 ;

-2

есть еще одна опция, которая позволяет получить размер "выбора" с объединениями, а также размер таблицы как вариант

-- 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);

-3

У меня такой же вариант, как и у последних, который вычисляет сегменты данных таблицы, индексы таблиц и 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;

Источник .

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.