89

I have a desktop application with a database embedded in it. When I execute my program I need to check that specific table exists, or create it if not.

Given a Connection object named conn for my database, how could I check this?

Dmitry
  • 1,117
  • 1
  • 9
  • 11

6 Answers6

123
DatabaseMetaData dbm = con.getMetaData();
// check if "employee" table is there
ResultSet tables = dbm.getTables(null, null, "employee", null);
if (tables.next()) {
  // Table exists
}
else {
  // Table does not exist
}
Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317
RealHowTo
  • 34,977
  • 11
  • 70
  • 85
  • 3
    Ensure that when you use table names that it is not using pattern chars like underscore, such as "employee_reports". I have been caught by situations like this as some metadata implementations use pattern matching. Best to double check the result set with a simple table.equals("employee_reports") statement in your if – Constantin Oct 18 '14 at 17:05
  • Same code works for postgres jdbc too.Thanks. – Ankur Srivastava Apr 12 '17 at 17:26
  • Please note that this method will trigger loading information about whole database for freshly created connection. Depending on database size, connection speed it may take a while 2-5 seconds or more. In our project with about 2000 integration tests it gave slow down for an hour (each test is using newly created scheme). – m1ld Oct 15 '20 at 09:51
73

You can use the available meta data:

  DatabaseMetaData meta = con.getMetaData();
  ResultSet res = meta.getTables(null, null, "My_Table_Name", 
     new String[] {"TABLE"});
  while (res.next()) {
     System.out.println(
        "   "+res.getString("TABLE_CAT") 
       + ", "+res.getString("TABLE_SCHEM")
       + ", "+res.getString("TABLE_NAME")
       + ", "+res.getString("TABLE_TYPE")
       + ", "+res.getString("REMARKS")); 
  }

See here for more details. Note also the caveats in the JavaDoc.

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • 15
    You're requesting all tables from the server and then browsing through those names locally. That is not very efficient if you just want to check if table 'X' exist. You would want to use the 3rd argument to the `getTables()` method !!! (rather than using 'null' as you do) – peterh Jul 07 '13 at 18:36
  • 1
    @nolan6000 - noted and amended. Thx – Brian Agnew Mar 30 '15 at 11:30
  • 2
    this solution doesn't work if the table name is provided with schema, so be careful with it. To provide the schema, you should use `meta.getTables("Schema_Name", null, "My_Table_Name", new String[] {"TABLE"}) ` – ikryvorotenko Dec 23 '16 at 16:12
  • @BrianAgnew Can I run this command even if I don't have retrieval permissions ? – Anup Kumar Gupta Aug 25 '17 at 09:21
  • 1
    @AnupKumarGupta: That depends on the DBMS you use, but probably yes. Usually just listing the tables available is not restriced. – sleske Oct 30 '18 at 09:12
  • In oracle, if table exists, then it returns correctly. Then I deleted the table manually, and executed the same code, still it is returning the table.. Any idea? Does oracle moving the object to some 'archive' kind of schema? – Bhushan Karmarkar Feb 28 '19 at 06:37
  • if you're here for `hsqldb` you can do a `CREATE TABLE IF NOT EXISTS newtable (column details...)` without having to check – Chidi Sep 03 '19 at 13:56
  • 1
    The note on the javadoc is kind of scary: `Note: Some databases may not return information for all tables.` Makes it hard to rely on unless you know your vendor _does_ fully support it. – Lucas Jun 09 '20 at 18:05
12

I don't actually find any of the presented solutions here to be fully complete so I'll add my own. Nothing new here. You can stitch this together from the other presented solutions plus various comments.

There are at least two things you'll have to make sure:

  1. Make sure you pass the table name to the getTables() method, rather than passing a null value. In the first case you let the database server filter the result for you, in the second you request a list of all tables from the server and then filter the list locally. The former is much faster if you are only searching for a single table.

  2. Make sure to check the table name from the resultset with an equals match. The reason is that the getTables() does pattern matching on the query for the table and the _ character is a wildcard in SQL. Suppose you are checking for the existence of a table named EMPLOYEE_SALARY. You'll then get a match on EMPLOYEESSALARY too which is not what you want.

Ohh, and do remember to close those resultsets. Since Java 7 you would want to use a try-with-resources statement for that.

Here's a complete solution:

public static boolean tableExist(Connection conn, String tableName) throws SQLException {
    boolean tExists = false;
    try (ResultSet rs = conn.getMetaData().getTables(null, null, tableName, null)) {
        while (rs.next()) { 
            String tName = rs.getString("TABLE_NAME");
            if (tName != null && tName.equals(tableName)) {
                tExists = true;
                break;
            }
        }
    }
    return tExists;
}

You may want to consider what you pass as the types parameter (4th parameter) on your getTables() call. Normally I would just leave at null because you don't want to restrict yourself. A VIEW is as good as a TABLE, right? These days many databases allow you to update through a VIEW so restricting yourself to only TABLE type is in most cases not the way to go. YMMV.

peterh
  • 18,404
  • 12
  • 87
  • 115
  • 1
    You may need to change `tName.equals(tableName)` to `tName.equals(tableName.toLowerCase())`, it wouldn't work if `tableName` were uppercase. – Searene Nov 30 '16 at 09:09
  • @Searene.The question isn't about any particular database. You are right that case needs to considered, but this depends on the database. Some databases you can even change, for example MS SQL server support both case-insensitive and case-sensitive depending on how you set the value of the 'COLLATION'. So your point is valid, it is just that is is difficult to come up with a general solution that will work with any scenario. – peterh Mar 04 '19 at 17:06
6

Adding to Gaby's post, my jdbc getTables() for Oracle 10g requires all caps to work:

"employee" -> "EMPLOYEE"

Otherwise I would get an exception:

java.sql.SqlExcepcion exhausted resultset

(even though "employee" is in the schema)

Ojonugwa Jude Ochalifu
  • 26,627
  • 26
  • 120
  • 132
wawiwa
  • 313
  • 1
  • 5
  • 9
1
    /**
 * Method that checks if all tables exist
 * If a table doesnt exist it creates the table
 */
public void checkTables() {
    try {
        startConn();// method that connects with mysql database
        String useDatabase = "USE " + getDatabase() + ";";
        stmt.executeUpdate(useDatabase);
        String[] tables = {"Patients", "Procedures", "Payments", "Procedurables"};//thats table names that I need to create if not exists
        DatabaseMetaData metadata = conn.getMetaData();

        for(int i=0; i< tables.length; i++) {
            ResultSet rs = metadata.getTables(null, null, tables[i], null);
            if(!rs.next()) {
                createTable(tables[i]);
                System.out.println("Table " + tables[i] + " created");
            }
        }
    } catch(SQLException e) {
        System.out.println("checkTables() " + e.getMessage());
    }
    closeConn();// Close connection with mysql database
}
Konstantin F
  • 180
  • 1
  • 15
0

If using jruby, here is a code snippet to return an array of all tables in a db.

require "rubygems"
require "jdbc/mysql"
Jdbc::MySQL.load_driver
require "java"

def get_database_tables(connection, db_name)
  md = connection.get_meta_data
  rs = md.get_tables(db_name, nil, '%',["TABLE"])

  tables = []
  count = 0
  while rs.next
    tables << rs.get_string(3)
  end #while
  return tables
end
DBulgatz
  • 39
  • 6