web-dev-qa-db-fra.com

Comment calculer la taille des tables dans Oracle

É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 du fait que je n'ai peut-être pas une option aussi facile que sp_spaceused. Néanmoins, les réponses potentielles que j'ai obtenues sont la plupart du temps obsolètes 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?

121
Rollo Tomazzi

Vous pourriez être intéressé par cette requête. Il vous indique combien d’espace est alloué pour chaque table en tenant compte des index et des objets LOB de la table. Vous êtes souvent intéressé par connaître "La quantité d'espace occupée par la table Commande d'achat, y compris tous les index" plutôt que par la table elle-même. Vous pouvez toujours plonger dans les détails. Notez que cela nécessite un 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
;
196
WW.
-- 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 avec les statistiques de collecte:

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

Tout d'abord, je tiens à souligner que la collecte de statistiques sous forme de tables afin d'analyser l'espace est une opération potentiellement dangereuse. 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 utilisant des paramètres non définis par défaut que votre appel n'utilise pas, et obligera Oracle à ré-analyser les requêtes utilisant la table en question, ce qui peut être une performance. frappé. Si l'administrateur de base de données a volontairement laissé certaines tables sans statistiques (commun si votre _OPTIMIZER_MODE_ est CHOOSE), la collecte de statistiques peut amener Oracle à cesser d'utiliser l'optimiseur basé sur des règles et à utiliser l'optimiseur basé sur les coûts pour un ensemble de requêtes qui peut être un casse-tête majeur de performance si cela se fait de manière inattendue en production. Si vos statistiques sont exactes, vous pouvez interroger _USER_TABLES_ (ou _ALL_TABLES_ ou _DBA_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 procédure SQL Server _sp_spaceused_ est probablement le package _DBMS_SPACE_ d'Oracle. Tom Kyte a une procédure Nice show_space qui fournit une interface simple à ce paquetage et affiche des informations similaires à celles imprimées par _sp_spaceused_.

29
Justin Cave

Tout d’abord, rassemblez les statistiques de l’optimiseur sur la table (si ce n’est déjà fait):

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

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

Recherchez 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 le suivant: blocs + empty_blocks + num_freelist_blocks.

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

Multipliez le nombre de blocs par la taille de bloc utilisée (généralement 8 Ko) pour obtenir l'espace utilisé - par ex. 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: modifications apportées à ce qui suit après la lecture de ce fil de discussion AskTom

8
Tony Andrews

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

Pour les index et les tables sous-partitionnées, 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
    ;

6
rratina

Les tables IIRC 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.

Voici une variante de la réponse WW, elle inclut des partitions et des sous-partitions comme suggéré par les précédents, 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;
3
ss64
select segment_name,segment_type,bytes/1024/1024 MB
from dba_segments
where segment_name='TABLENAME' and owner ='OWNERNAME' order by mb desc;
3
bronx

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
;
1
rratina

J'ai modifié la requête pour obtenir la taille du schéma par espace de table.

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
Najee Ghanim

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éside sur un espace de table (éventuellement plusieurs espaces de table si elle est partitionnée et éventuellement plusieurs espaces de table si vous souhaitez également prendre en compte les index de la table). Un tablespace contient souvent plusieurs tables et peut être réparti sur plusieurs fichiers.

Si vous estimez la quantité d'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 attendu dans la table), constitue un bon indicateur. Mais Oracle laissera de l'espace libre sur chaque bloc, en partie pour permettre aux lignes de "grandir" si elles sont mises à jour, en partie parce qu'il pourrait ne pas être possible d'ajuster une autre ligne entière sur ce bloc (par exemple, un bloc de 8 Ko 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 rangées). BLOCKS (dans USER_TABLES) pourrait donc être un meilleur guide.

Mais si vous aviez 200 000 lignes dans une table et en supprimiez la moitié, la table "posséderait" toujours le même nombre de blocs. Cela ne les libère pas pour que d'autres tables puissent les utiliser. De plus, les blocs ne sont pas ajoutés à une table individuellement, mais par groupes appelés "étendue". Ainsi, il y aura généralement EMPTY_BLOCKS (également dans USER_TABLES) dans une table.

1
Gary Myers

J'ai trouvé cela 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
0
Geoffrey Musafu
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 ;
0
Vijay Chettiar

Une 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.nom_table), 0) + blocs) * 8192/1024 TotalSize, blocs * 8 tableSize de dba_tables ordre par 3

0
Noam