31

I want to check if a table with a specific name exists in a database I've connected to using PHP and PDO.

It has to work on all database backends, like MySQL, SQLite, etc.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Andre
  • 313
  • 1
  • 3
  • 4

15 Answers15

46

Here's a complete function for checking if a table exists.

/**
 * Check if a table exists in the current database.
 *
 * @param PDO $pdo PDO instance connected to a database.
 * @param string $table Table to search for.
 * @return bool TRUE if table exists, FALSE if no table found.
 */
function tableExists($pdo, $table) {

    // Try a select statement against the table
    // Run it in try-catch in case PDO is in ERRMODE_EXCEPTION.
    try {
        $result = $pdo->query("SELECT 1 FROM {$table} LIMIT 1");
    } catch (Exception $e) {
        // We got an exception (table not found)
        return FALSE;
    }

    // Result is either boolean FALSE (no table found) or PDOStatement Object (table found)
    return $result !== FALSE;
}

Note: PDO will only throw exceptions if it is told to, by default it is silent and throws no exceptions. Thats why we need to check the result as well. See PDO error handling at php.net

Community
  • 1
  • 1
exstral
  • 639
  • 1
  • 5
  • 8
  • 2
    This doesn't protect against SQL injection. – livefree75 Jun 06 '13 at 16:45
  • 6
    @livefree75 Perhaps you could be more specific? I am not seeing any client-supplied fields, here... perhaps I am unsure of the problem that you are seeing in his/her code? – Steve Aug 07 '13 at 18:45
  • 4
    @livefree75 Why it should protect? if it is in db layer, the protection should not be inserted there. protection and security does not mean insertion of filter\sanitize function everywhere. – Ran Bar-Zik Aug 10 '13 at 08:42
  • 3
    If you want to sanitize, do this at the start of the function: `$table = preg_replace('/[^\da-z_]/i', '', $table);` – rybo111 Mar 17 '16 at 13:32
14

Before I go on, I do realise this is a MySQL-specific solution.

While all the solutions mentioned here may work, I (personally) like to keep PDO from throwing exceptions (personal preference, that's all).

As such, I use the following to test for table creation instead:

SHOW TABLES LIKE 'some_table_of_mine';

There's no error state generated if the table doesn't exist, you simply get a zero resultset. Works fast and consistently for me.

Nathan Crause
  • 874
  • 10
  • 7
10

Do:

select 1 from your_table

and then catch the error. If you don't get any error, but resultset with one column containing "1", then the table exists.

Milan Babuškov
  • 59,775
  • 49
  • 126
  • 179
4

Once you have your database handle via PDO, you can do this:

$tableExists = gettype($dbh->exec("SELECT count(*) FROM $table")) == 'integer';

Or wrap it in a function.

I tried messing around with try/catch at first, but even if the table did Not exist, there was no exception. Finally ended up with checking for the data type of the returned value from the dbh exec call. It's either an integer, if there is a match on the select count (even if there count is 0, or a boolean of false if there were no results.

I think this should work with all the database types that PDO supports, since the syntax is really simple.

Prof. Falken
  • 24,226
  • 19
  • 100
  • 173
targnation
  • 917
  • 11
  • 11
  • 2
    What happen if my table has 1 million rows ? – Stephan Feb 11 '14 at 06:38
  • If the table uses the MyISAM engine this operation is very fast because the value is stored as metadata (http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_count) – Purefan Apr 18 '16 at 14:52
  • MySQL has been moving away from MyISAM for a long time, and it's far from the only database that makes counting rows expensive. This also doesn't handle PDO in an exception mode. – David Timothy Strauss Nov 07 '16 at 22:25
3

As part of your project, create a schema view.

For Oracle it would be something like

SELECT TABLE_NAME FROM ALL_TABLES

For Mysql:

SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema = 'mydbname'

ETC..

And then run a query in your code against the view.

Equistatic
  • 416
  • 1
  • 3
  • 8
  • The question was about PDO. Please, update the answer accordingly. – sitilge Dec 16 '15 at 10:41
  • 2
    @sitilge PDO is orthogonal from the problem at hand. It is merely the tool to run a query. This is an ok answer. – Félix Adriyel Gagnon-Grenier Nov 07 '16 at 23:21
  • @FélixGagnon-Grenier indeed, it is a tool. However, the OP, most likely, would like to receive a `this is how you do it with PDO` answer, doesn't it? – sitilge Nov 07 '16 at 23:42
  • @DavidTimothyStrauss I rollbacked your edit as it is totally inappropriate. Not only is it invalid sql code, there was nothing about how to actually concatenate said variable in the query. – Félix Adriyel Gagnon-Grenier Nov 08 '16 at 01:28
  • 1
    @sitilge well, maybe. I don't really strongly disagree, it's just that "*this is how you do it with PDO*" is explained in every thousand of tutorial out there: it's named running an sql query. If someone can't understand that SQL code is actually meant to be run, be it by PDO or something else, there is not much hope they will know how to use a detailed answer either... – Félix Adriyel Gagnon-Grenier Nov 08 '16 at 01:31
  • @FélixGagnon-Grenier Oh, indeed you're right about the syntax; I had mistakenly assumed you were answering the question, which is (1) how to check if a specific table exists and (2) doing it with PDO. Anything satisfying part #1 would have to include the specific table name being looked for. Your code doesn't do that; it just lists all tables. Creating a view is also entirely irrelevant to the question. As for concatenation, your answer doesn't even begin to address how to do this in PHP, which is what the question asked for. – David Timothy Strauss Nov 08 '16 at 02:56
  • This answer fails on a third part of the question, too, which is portability. Not only is the answer non-portable; it doesn't even answer how to handle SQLite, which the question specifically wanted. – David Timothy Strauss Nov 08 '16 at 03:03
  • "'this is how you do it with PDO' is explained in every thousand of tutorial out there: it's named running an sql query." > Actually, properly doing it with PDO requires catching the precise error codes relevant to a missing table from a PDOException, which is different from running a normal query you expect to succeed. If you catch all exceptions (or even PDOExceptions), you will detect a "missing table" if, say, the DB connection has broken. – David Timothy Strauss Nov 08 '16 at 03:06
1

You might be able to avoid having to rely on an error by using a query along the lines of "SHOW TABLES LIKE 'your_table'" and then counting the rows. I've been using this method successfully with MySQL and PDO but have yet to test it with other DBs

  • Indeed, that would not work with other databases (though other databases often have equivalents to MySQL's information schema and `SHOW TABLES`). – David Timothy Strauss Nov 07 '16 at 22:21
  • @DavidTimothyStrauss You seem to have an interest into improving this question and it's answer, which is marvelous, but careful with your edit suggestions. You are changing content from other people, at one point literally rewriting the answer. If you feel an answer is *wrong*, the correct thing to do is to downvote it, and comment so that op can fix it, not fix it using your own way of seeing how that should be. Thanks! – Félix Adriyel Gagnon-Grenier Nov 07 '16 at 23:18
1

At first, I was using the accepted answer, but then I noticed it was failing with empty tables. Here is the code I'm using right now:

function DB_table_exists($db, $table){
    GLOBAL $db;
    try{
        $db->query("SELECT 1 FROM $db.$table");
    } catch (PDOException $e){
        return false;
    }
    return true;
}

This code is an extract of my extension class for PDO. It will produce an error (and return false) if the table doesn't exist, but will succeed if the table exists and/or is empty.

Gwyneth Llewelyn
  • 796
  • 1
  • 11
  • 27
Yuri
  • 3,082
  • 3
  • 28
  • 47
1

If you have other major actions to do within the same statement, you can use the e->errorInfo

try{                
    //Your major statements here
}
catch(PDOException $e){
    if($e->errorInfo[1] == 1146){
        //when table doesn't exist
    }      
}
ZHANG WEI
  • 39
  • 3
0

This complete function is very similar to esbite's answer, but includes code to protect from SQL injection. Also, you may not get consistent results from the accepted answer when the table in question is empty.

/**
 * This function checks if the table exists in the passed PDO database connection
 * @param PDO $pdo - connection to PDO database table
 * @param type $tableName 
 * @return boolean - true if table was found, false if not
 */
function tableExists(PDO $pdo, $tableName) {
    $mrSql = "SHOW TABLES LIKE :table_name";
    $mrStmt = $pdo->prepare($mrSql);
    //protect from injection attacks
    $mrStmt->bindParam(":table_name", $tableName, PDO::PARAM_STR);

    $sqlResult = $mrStmt->execute();
    if ($sqlResult) {
        $row = $mrStmt->fetch(PDO::FETCH_NUM);
        if ($row[0]) {
            //table was found
            return true;
        } else {
            //table was not found
            return false;
        }
    } else {
        //some PDO error occurred
        echo("Could not check if table exists, Error: ".var_export($pdo->errorInfo(), true));
        return false;
    }
}
RightHandedMonkey
  • 1,718
  • 20
  • 25
  • 1
    This would only work with MySQL. Checking for the existence of a table should not require protection from SQL injection because table checks should only be done using programmer-specified values or known-safe generated names. If code allows unprivileged users to create tables with arbitrary names, there's already a bigger problem. – David Timothy Strauss Nov 07 '16 at 22:19
0

I recommend you to use DESCRIBE

example query:

DESCRIBE `users`

example php&pdo:

$tblname = 'users'; //table name
$x = $db->prepare("DESCRIBE `$tblname`");
$x->execute();
$row = $x->fetch();
if ($row) {
    print 1; //table exists
}else{
    print 0; //table not exists
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Pausi
  • 134
  • 2
  • 7
0

A simple PDO two liner that works with MySQL (not sure about other DBs):

$q = $pdo->query("SHOW TABLES LIKE '{$table}'");
$tableExists = $q->fetchColumn();
Picard
  • 3,745
  • 3
  • 41
  • 50
-1

I do a few things in my web apps with CodeIgniter to check that the database exists (and is useful), any of these can work:

@$this->load->database();
$v = @$this->db->version()
$tables = @$this->db->list_tables();

Adding the @ will suppress errors if you have them enabled in your PHP setup, and checking the results of version() and list_tables() can be used to not only determine if your DB is around (but that it's sane too).

Bruce Alderson
  • 1,517
  • 13
  • 19
  • Aye, but you will need to use the CodeIgniter framework for that (not stated by the OP). Also, at least in 2020, you could simply use [if ($db->tableExists('table_name') {...})](https://codeigniter.com/user_guide/database/metadata.html#determine-if-a-table-exists). – Gwyneth Llewelyn May 29 '20 at 14:57
-1

Do a query where you ask the database to create a table if it doesn't exist:

$string = "CREATE TABLE IF NOT EXISTS " .$table_name . " int(11) NOT NULL AUTO_INCREMENT,
  `id` int(3) NOT NULL,
  `blabla` int(2) NOT NULL,
  `blabla1` int(2) NOT NULL,
  `blabla3` varchar(3) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 
AUTO_INCREMENT=1 ";

$sql = $conection->prepare($string);
$sql->execute();
Edugra17
  • 1
  • 2
  • 1
    Seems to be overkill... surely a `create table` is computationally more expensive than a simple `select`, as others suggested? Also, the OP wanted a solution that works on all backends — yours will only work with MySQL/MariaDB. – Gwyneth Llewelyn May 29 '20 at 14:53
-1

This seems to work at least with SQLite3 without exceptions, etc:

        $select =
            "SELECT CASE WHEN EXISTS (SELECT * FROM SQLITE_MASTER WHERE TYPE = 'table' AND NAME = :tableName) THEN 1 ELSE 0 END AS TABLE_EXISTS;";
  • You mean, this will _only_ work under SQLite, since other RDBMS do not have an `SQLITE_MASTER` table... as the name implies The OP asked for a database-independent solution... – Gwyneth Llewelyn May 29 '20 at 14:59
  • 1
    True, I just seem to have answered for the SQLite part as I was working with PDO SQLite at the time. PHP is not really my 'thing' but selecting one (1) from a non-existing table should cause an exception and thus a need for exception handling with the code as said in the accepted answer – Petteri Kautonen May 30 '20 at 17:44
-2

Here's what worked for me. It was a combination of several answers:

$table_name = 'your_table_here'; 
$test = "SELECT 1 FROM " . $table_name . " LIMIT 1";
$test = $db->query($test); //$db needs to be PDO instance

if($test)
{
    return 1; //Table exists
}
else
{
    return 0; //No table in database
}
boomx09
  • 13
  • 4