tags:

views:

2745

answers:

6

I have a set of tables in Oracle and I would like to identify the table that contains the maximum number of rows.

So if, A has 200 rows, B has 345 rows and C has 120 rows I want to be able to identify table B.

Is there a simple query I can run to achieve this?

Edit: There are 100 + tables so I am looking for something generic.

+1  A: 
select max(select count(*) from A union select count(*) from B...)

should work.

edit: if you want something dynamic, you can build a string in PL/SQL with each "count(*)" subquery (for example, listing the table names from USER_TABLES), and then execute te main query with:

execute immediate 'select max('||subquery||')'
friol
I have a lot of tables. I basically want to run in on the complete set of tables. I do not want to do it manually.
Preets
This technique wouldn't indicate which table had the maximum number of rows.
Dave Costa
Ops, you're right, it would indicate only the maximum number of rows. But anyway, I hope the OP could find a good algorithm... that isn't that hard, looking at our suggestions.
friol
+5  A: 

Given that you said you were using Oracle I would just query the meta-data.

select table_name, max(num_rows) from all_tables where table_name in ('A', 'B', 'C');

Just saw your edit. Just run the above without the where clause and it will return the largest table in the database. Only problem may be that you might get a SYS$ table or something. Alternately if you are just doing this for your own knowledge just do

select table_name, num_rows from all_tables order by num_rows;

and you'll see what the biggest are.

Dan
If I'm not wrong, "num_rows" in ALL_TABLES is just an *estimate* of number of rows, calculated by the statistics engine.
friol
I don't think it's an "estimate" but it is not updated in real-time, that is true. Preets didn't say what this is for so I'm assuming she needs the easiest way to find largest table interactively. I'd be curious to see if it works out for Preets.
Dan
Preets
Be aware that this is only an approximation, Oracle sometimes gathers statistics and during this gathering it will update num_rows. Ask your DBA whether and when statistics are gathered. However it is fast, select num_rows from all_tables is faster as select count(*) from <<table_name>>.
tuinstoel
You can tell whether it is an estimate or not from user_tables, and also the last date on which the table was analyzed. Be wary of tables with old or no statistics. Another aproach would be to estimate based on the segment size from user_segments -- it's not the row count, but at least it's correct.
David Aldridge
+1  A: 

The table in your schema that has max rows:

with data as 
(
 select table_name,
        to_number(extractvalue(xmltype(
                  dbms_xmlgen.getxml (
                 ' select count(*) c from ' || table_name)),
                  '/ROWSET/ROW/C')) countrows
 from   user_tables
)
select table_name, countrows
from   data 
where  countrows = (select max(countrows)
                    from   data);

dbms_xmlgen.getxml(' select .... ') is extremely flexible.

tuinstoel
This syntax is interesting! Wont be using it for _this_ particular case, but it seems very useful.
Preets
+1  A: 

Here's another method, likely to be much slower than simply getting ALL_TABLES.NUM_ROWS, but it doesn't depend on statistics having been gathered and gives exact, current values -- although how current depends on how long it takes to run!

-- For running in SQLPlus you need this to see the output.
-- If running in Toad or similar tool, output is enabled by default

    SET SERVEROUTPUT ON SIZE 100000

    DECLARE
      l_rows  INTEGER;
      l_max_rows  INTEGER := 0;
      l_table_name  all_tables.table_name%TYPE := NULL;
    BEGIN
      FOR table_record IN (SELECT table_name FROM all_tables) LOOP

        EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM '||table_record.table_name
          INTO l_rows;

        IF l_rows > l_max_rows THEN
          l_max_rows := l_rows;
          l_table_name := table_record.table_name;
        END IF;
      END LOOP;

      IF l_table_name IS NULL THEN
        dbms_output.put_line( 'All tables are empty' );
      ELSE
        dbms_output.put_line( 'Table ' || table_record.table_name || 
                              ' has ' || TO_CHAR(l_max_rows) || ' rows'
                            );
      END IF;
    END;
    /
Dave Costa
+1  A: 

You could get the same result with one trawl of the data like so:

SELECT     DISTINCT
           FIRST_VALUE ( t.owner )
             OVER ( ORDER BY t.num_rows DESC NULLS LAST )
                                                 owner,
           FIRST_VALUE ( t.table_name )
             OVER ( ORDER BY t.num_rows DESC NULLS LAST )
                                                 table_name,
           FIRST_VALUE ( t.num_rows )
             OVER ( ORDER BY t.num_rows DESC NULLS LAST )
                                                 num_rows
FROM       all_tables                            t
+1  A: 

David Aldridge correctly points out that querying all_tables could give incorrect results due to missing or stale table statistics. But there is also a problem with using user_segments; Deleted blocks beneath the high water mark would still be counted for the size of the table.

Example:

SQL>create table t as select * from all_objects

Table created.

SQL>select blocks, bytes from user_segments where segment_name = 'T';

BLOCKS      BYTES


   768    6291456

SQL>delete from t

52676 rows deleted.

SQL>commit;

Commit complete.

SQL>select count(*) from t;

COUNT(*)

     0

SQL>select blocks, bytes from user_segments where segment_name = 'T';

BLOCKS      BYTES


   768    6291456

SQL>truncate table t;

Table truncated.

SQL>select blocks, bytes from user_segments where segment_name = 'T';

BLOCKS      BYTES


     8      65536
RussellH