Comment calculer la taille des tables dans Oracle

128

Étant habitué (et potentiellement gâté par) MSSQL, je me demande comment je peux obtenir la taille des tables dans Oracle 10g. Je l'ai googlé donc je suis maintenant conscient que je n'ai peut-être pas une option aussi simple que sp_spaceused. Pourtant, les réponses potentielles que j'ai obtenues sont la plupart du temps dépassées ou ne fonctionnent pas. Probablement parce que je ne suis pas DBA sur le schéma avec lequel je travaille.

Quelqu'un aurait-il des solutions et / ou des recommandations?

Rollo Tomazzi
la source
si avoir un proc qui donne la réponse est gâché, alors prenez les réponses que vous avez obtenues d'ici et enveloppez-les dans une procédure et appelez-la ... dun dun duh ... sp_spaceused. Il y a vraiment peu de magie.
1
@MarkBrady Peut-être pas de la magie, mais une tonne de connaissances arcanes est nécessaire.
jpmc26

Réponses:

201

Vous pourriez être intéressé par cette requête. Il vous indique la quantité d'espace allouée à chaque table en tenant compte des index et des LOB de la table. Souvent, vous êtes intéressé de savoir "combien d'espaces la table de bon de commande prend, y compris les index" plutôt que simplement la table elle-même. Vous pouvez toujours vous plonger dans les détails. Notez que cela nécessite l'accès aux vues 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
;
WW.
la source
1
Notez que cette réponse compte les segments, ce qui ne fait pas la distinction entre l'espace actuellement utilisé et l'espace précédemment utilisé. Apparemment, une fois qu'un segment est affecté à une table, il est toujours affecté à une table, même si l'espace est libéré. Regardez ici . Je suppose que vous devez descendre au niveau de l'étendue pour voir combien d'espace est réellement utilisé ?
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.
;

Remarque: il s'agit d'estimations, rendues plus précises grâce à la collecte de statistiques:

exec dbms_utility.analyze_schema(user,'COMPUTE');
grokster
la source
2
Ces statistiques peuvent être null( num_rows, avg_row_len), vous devez faire une analyse avant via la déclaration suivanteANALYZE TABLE your_table COMPUTE STATISTICS
Brice
L'analyse difficile peut être très longue!
Brice
beau travail quand je ne peux pas vérifier une table sans tablespace
tungns304
30

Tout d'abord, je voudrais généralement avertir que la collecte de statistiques de table afin de faire une analyse d'espace est une chose potentiellement dangereuse à faire. La collecte de statistiques peut modifier les plans de requête, en particulier si l'administrateur de base de données a configuré un travail de collecte de statistiques qui utilise des paramètres non par défaut que votre appel n'utilise pas, et obligera Oracle à réexaminer les requêtes qui utilisent la table en question, ce qui peut être une performance frappé. Si l'administrateur de base de données a intentionnellement laissé certaines tables sans statistiques (courant si vous OPTIMIZER_MODEchoisissez), la collecte de statistiques peut amener Oracle à cesser d'utiliser l'optimiseur basé sur des règles et à commencer à utiliser l'optimiseur basé sur les coûts pour un ensemble de requêtes qui peuvent être une performance majeure mal de tête si cela est fait de manière inattendue en production. Si vos statistiques sont exactes, vous pouvez interroger USER_TABLES(ou ALL_TABLESouDBA_TABLES) directement sans appeler GATHER_TABLE_STATS. Si vos statistiques ne sont pas exactes, il y a probablement une raison à cela et vous ne voulez pas perturber le statu quo.

Deuxièmement, l'équivalent le plus proche de la sp_spaceusedprocédure SQL Server est probablement le DBMS_SPACEpackage d'Oracle . Tom Kyte a une show_spaceprocédure intéressante qui fournit une interface simple à ce package et imprime des informations similaires à celles sp_spaceusedimprimées.

Justin Cave
la source
8

Tout d'abord, rassemblez les statistiques de l'optimiseur sur la table (si vous ne l'avez pas déjà fait):

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

AVERTISSEMENT: comme Justin le dit dans sa réponse, la collecte des statistiques de l'optimiseur affecte l'optimisation des requêtes et ne doit pas être effectuée sans le soin et la considération !

Trouvez ensuite le nombre de blocs occupés par la table à partir des statistiques générées:

select blocks, empty_blocks, num_freelist_blocks
from   all_tables
where  owner = 'MYSCHEMA'
and    table_name = 'MYTABLE';
  • Le nombre total de blocs alloués à la table est de blocs + empty_blocks + num_freelist_blocks.

  • blocs est le nombre de blocs qui contiennent réellement des données.

Multipliez le nombre de blocs par la taille du bloc utilisé (généralement 8 Ko) pour obtenir l'espace consommé - par exemple 17 blocs x 8 Ko = 136 Ko.

Pour faire cela pour toutes les tables d'un schéma à la fois:

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

select table_name, blocks, empty_blocks, num_freelist_blocks
from   user_tables;

Remarque: les modifications apportées à ce qui précède après la lecture de ce fil AskTom

Tony Andrews
la source
7

J'ai modifié la requête de WW pour fournir des informations plus détaillées:

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
la source
6

Pour les tables et index sous-partitionnés, nous pouvons utiliser la requête suivante



    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
la source
5

IIRC les tables dont vous avez besoin sont DBA_TABLES, DBA_EXTENTS ou DBA_SEGMENTS et DBA_DATA_FILES. Il existe également des versions USER_ et ALL_ de celles-ci pour les tables que vous pouvez voir si vous ne disposez pas des autorisations d'administration sur la machine.

ConcernedOfTunbridgeWells
la source
4

Voici une variante de la réponse WW, elle comprend des partitions et des sous-partitions comme d'autres l'ont suggéré ci-dessus, plus une colonne pour afficher le 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;
SS64
la source
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
la source
2

J'ai modifié la requête pour obtenir la taille du schéma par 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
;
Najee Ghanim
la source
1

Cela dépend de ce que vous entendez par «taille de la table». Une table ne concerne pas un fichier spécifique du système de fichiers. Une table résidera sur un tablespace (éventuellement plusieurs tablespaces si elle est partitionnée, et éventuellement plusieurs tablespaces si vous souhaitez également prendre en compte les index sur la table). Un tablespace contient souvent plusieurs tables et peut être réparti sur plusieurs fichiers.

Si vous estimez l'espace dont vous aurez besoin pour la croissance future de la table, avg_row_len multiplié par le nombre de lignes de la table (ou le nombre de lignes que vous attendez dans la table) sera un bon guide. Mais Oracle laissera de l'espace libre sur chaque bloc, en partie pour permettre aux lignes de `` grossir '' si elles sont mises à jour, en partie parce qu'il ne sera peut-être pas possible d'ajuster une autre ligne entière sur ce bloc (par exemple, un bloc de 8K ne contiendrait que 2 lignes de 3K, bien que ce soit un exemple extrême car 3K est beaucoup plus grand que la plupart des tailles de lignes). Ainsi, BLOCKS (dans USER_TABLES) pourrait être un meilleur guide.

Mais si vous aviez 200 000 lignes dans une table, supprimiez la moitié d'entre elles, la table «posséderait» toujours le même nombre de blocs. Il ne les libère pas pour que d'autres tables les utilisent. De plus, les blocs ne sont pas ajoutés individuellement à une table, mais dans des groupes appelés «étendue». Donc, il y aura généralement EMPTY_BLOCKS (également dans USER_TABLES) dans une table.

Gary Myers
la source
1

Correction pour les tables partitionnées:

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
la source
0

Sélection simple qui renvoie les tailles brutes des tables, en fonction de la taille du bloc, inclut également la taille avec index

select table_name, (nvl ((select sum (blocks) from dba_indexes a, dba_segments b où a.index_name = b.segment_name et a.table_name = dba_tables.table_name), 0) + blocks) * 8192/1024 TotalSize, blocks * 8 tableSize à partir de dba_tables triés par 3

Noam
la source
0

J'ai trouvé que c'était un peu plus précis:

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
la source
7
Ressemble un peu à ma réponse?
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
la source
-2

il y a une autre option qui permet d'obtenir la taille "select" avec les jointures, et la taille de la table en option aussi

-- 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
la source
-3

J'ai la même variante que les derniers qui calculent les segments de données de table, les index de table et les champs 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;

Source .

user2498491
la source