MySQL, vérifier si une colonne existe dans une table avec SQL

130

J'essaye d'écrire une requête qui vérifiera si une table spécifique dans MySQL a une colonne spécifique, et sinon - la crée. Sinon, ne faites rien. C'est vraiment une procédure simple dans n'importe quelle base de données d'entreprise, mais MySQL semble être une exception.

J'ai pensé à quelque chose comme

IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS 
           WHERE TABLE_NAME='prefix_topic' AND column_name='topic_last_update') 
BEGIN 
ALTER TABLE `prefix_topic` ADD `topic_last_update` DATETIME NOT NULL;
UPDATE `prefix_topic` SET `topic_last_update` = `topic_date_add`;
END;

fonctionnerait, mais cela échoue gravement. Y a-t-il un moyen?

clops
la source
Pourquoi ne pas simplement le créer? S'il existe, la création échouera mais vous ne vous en souciez pas.
Brian Hooper
la création a lieu dans une transaction et l'échec mettra fin à toute la transaction, triste mais vrai
clops
@haim - merci pour les headups, mais la requête suggérée dans votre lien ne fonctionne que dans une procédure :(
clops
2
Les instructions DDL provoquent une validation implicite dans la transaction en cours. dev.mysql.com/doc/refman/5.0/en/implicit-commit.html
Mchl

Réponses:

259

Cela fonctionne bien pour moi.

SHOW COLUMNS FROM `table` LIKE 'fieldname';

Avec PHP, ce serait quelque chose comme ...

$result = mysql_query("SHOW COLUMNS FROM `table` LIKE 'fieldname'");
$exists = (mysql_num_rows($result))?TRUE:FALSE;
Mfoo
la source
8
La question n'est pas de savoir comment faire cela en utilisant php + sql ou java + sql, c'est comment faire cela en utilisant pure sql / mysql d'où le
vote négatif
61
Vous répondez à la question + quelques informations qui m'ont aidé et probablement d'autres, +1
Francisco Presencia
@Mfoo Merci Mfoo, tu as sauvé ma journée! Fonctionne comme du charme. Une des meilleures solutions en dehors de la création de procédures pour cette tâche.
webblover
8
Yura: La réponse pure SQL / MySQL est la première partie où il dit utiliser "SHOW COLUMNS FROM table LIKE 'fieldname'". Vous pouvez ignorer le code PHP, ce n'est qu'un exemple d'une façon de récupérer et d'interpréter le résultat si vous utilisez PHP.
orrd
1
@codenamezero Je suppose que vous faites référence à l'utilisation de MSSQL, cette question concernait MySQL. se référer à cet article pour MSSQL
Mfoo
158

@julio

Merci pour l'exemple SQL. J'ai essayé la requête et je pense qu'elle a besoin d'une petite modification pour qu'elle fonctionne correctement.

SELECT * 
FROM information_schema.COLUMNS 
WHERE 
    TABLE_SCHEMA = 'db_name' 
AND TABLE_NAME = 'table_name' 
AND COLUMN_NAME = 'column_name'

Cela a fonctionné pour moi.

Merci!

Iain
la source
Il ressort de mes recherches limitées que tous les environnements d'hébergement n'ont pas de base de données information_schema. Tous les environnements cpanel que j'ai utilisés l'ont. Cette solution fournie dépend de ce DB existant.
cardi777
2
Cette réponse est meilleure que d'utiliser "SHOW COLUMNS" car elle utilise la méthode standard ANSI pour obtenir des informations de table, contrairement à SHOW COLUMNS qui est spécifique à MySQL. Cette solution fonctionnera donc avec d'autres bases de données. Utiliser "information_schema" semble étrange et vous pourriez penser que ce ne serait pas la manière standard SQL de le faire, mais c'est le cas.
Orrd
5
Notez que cette réponse ne vous donne que les informations sur l'existence de la colonne. Si vous voulez conditionnellement exécuter des instructions DDL, vous devrez envelopper le tout dans une procédure qui permet des déclarations comme IF, etc. Voir stackoverflow.com/questions/7384711/... un exemple de la façon d'envelopper une procédure autour de le test de la colonne et l'instruction DML conditionnelle.
Christopher Schultz
@Iain: stackoverflow.com/questions/32962149/... J'ai référencé votre réponse
Amar Singh
C'est plus agréable que l'approche "SHOW COLUMNS" ci-dessous car elle peut être utilisée dans les requêtes paramétrées réduisant le risque d'injection SQL. Je voulais juste ajouter que vous pouvez utiliser la fonction DATABASE () pour remplir la colonne TABLE_SCHEMA.
Andrew
16

Juste pour aider tous ceux qui recherchent un exemple concret de ce que @Mchl décrivait, essayez quelque chose comme

SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'my_schema' AND TABLE_NAME = 'my_table' AND COLUMN_NAME = 'my_column'

Si elle renvoie false (aucun résultat), vous savez que la colonne n'existe pas.

julio
la source
2
Je crois que cela devrait être TABLE_NAME = 'my_table', TABLE_SCHEMA est le schéma dans lequel se trouve la table. IeSELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'my_schema' AND TABLE_NAME = 'my_table' AND COLUMN_NAME = 'my_column'
AaronHolland
10

Voici une autre façon de le faire en utilisant PHP simple sans la base de données information_schema:

$chkcol = mysql_query("SELECT * FROM `my_table_name` LIMIT 1");
$mycol = mysql_fetch_array($chkcol);
if(!isset($mycol['my_new_column']))
  mysql_query("ALTER TABLE `my_table_name` ADD `my_new_column` BOOL NOT NULL DEFAULT '0'");
wvasconcelos
la source
Pourquoi voudriez-vous éviter d'utiliser information_schema? Il existe uniquement dans ce but. (De plus, ce fil est assez ancien et a déjà reçu une réponse.)
Leigh
1
Dans mes tests, c'est environ 10 à 50 fois plus rapide que d'utiliser information_schema. Cela nécessite que la table ait au moins une ligne, ce que vous ne pouvez pas toujours garantir.
Martijn
isset()trow 'false' si la clé de tableau existe mais la valeur est NULL . Il vaut mieux utiliser array_key_exists()commeif( !array_key_exists( 'my_new_column', $mycol ) )
Paul Geisler
1
Cette réponse m'a fait facepalm parce que j'ai négligé un simple isset()chèque. Cela ne résout pas la question, mais il est préférable que vous ayez déjà besoin d'exécuter la requête de sélection et d'avoir le résultat en mémoire.
Siphon
10

J'ai jeté cette procédure stockée avec un début des commentaires de @ lain ci-dessus, plutôt sympa si vous devez l'appeler plusieurs fois (et ne pas avoir besoin de php):

delimiter //
-- ------------------------------------------------------------
-- Use the inforamtion_schema to tell if a field exists.
-- Optional param dbName, defaults to current database
-- ------------------------------------------------------------
CREATE PROCEDURE fieldExists (
OUT _exists BOOLEAN,      -- return value
IN tableName CHAR(255),   -- name of table to look for
IN columnName CHAR(255),  -- name of column to look for
IN dbName CHAR(255)       -- optional specific db
) BEGIN
-- try to lookup db if none provided
SET @_dbName := IF(dbName IS NULL, database(), dbName);

IF CHAR_LENGTH(@_dbName) = 0
THEN -- no specific or current db to check against
  SELECT FALSE INTO _exists;
ELSE -- we have a db to work with
  SELECT IF(count(*) > 0, TRUE, FALSE) INTO _exists
  FROM information_schema.COLUMNS c
  WHERE 
  c.TABLE_SCHEMA    = @_dbName
  AND c.TABLE_NAME  = tableName
  AND c.COLUMN_NAME = columnName;
END IF;
END //
delimiter ;

Travailler avec fieldExists

mysql> call fieldExists(@_exists, 'jos_vm_product', 'child_option', NULL) //
Query OK, 0 rows affected (0.01 sec)

mysql> select @_exists //
+----------+
| @_exists |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> call fieldExists(@_exists, 'jos_vm_product', 'child_options', 'etrophies') //
Query OK, 0 rows affected (0.01 sec)

mysql> select @_exists //
+----------+
| @_exists |
+----------+
|        1 |
+----------+
quickshiftin
la source
MIster @quickshiftin, merci beaucoup. Cela m'aide à vérifier si une table est expirable , en vérifiant si elle contient un ExpirationDatechamp. 😊
Jeancarlo Fontalvo
@JeancarloFontalvo Mon plaisir! Consultez également mon projet mysql-inspectors que j'ai commencé pour inspecter le schéma avec des méthodes de niveau supérieur.
quickshift du
1
OMG C'est comme une bibliothèque 😱. Merci de l'avoir partagé Monsieur!
Jeancarlo Fontalvo
8

Sélectionnez simplement nom_colonne dans le schéma d'information et mettez le résultat de cette requête dans une variable. Testez ensuite la variable pour décider si la table doit être modifiée ou non.

PS Ne vous inquiétez pas de spécifier TABLE_SCHEMA pour la table COLUMNS également.

Mchl
la source
1
Je suis plutôt nouveau sur MySQL, pouvez-vous peut-être poster un petit exemple ici?
clops
2

J'utilise ce script simple:

mysql_query("select $column from $table") or mysql_query("alter table $table add $column varchar (20)");

Cela fonctionne si vous êtes déjà connecté à la base de données.

vio
la source
Cela ne fonctionnerait que si la table contenait également des lignes de données. Mais si la table était vide, cela ne fonctionnerait pas.
orrd
1
pas parfait, utilise d'anciens pilotes, ne fonctionne pas si la table est vide, les entrées ne sont pas échappées, mais j'aime comment vous l'avez fait en une ligne. +1
J'ai combattu un ours une fois.
1

Un grand merci à Mfoo qui a mis le très beau script pour ajouter des colonnes dynamiquement s'il n'existe pas dans la table. J'ai amélioré sa réponse avec PHP . Le script vous aide également à trouver le nombre de tables réellement nécessaires à la commande mysql «Ajouter une colonne» . Goûtez simplement la recette. Fonctionne comme du charme.

<?php
ini_set('max_execution_time', 0);

$host = 'localhost';
$username = 'root';
$password = '';
$database = 'books';

$con = mysqli_connect($host, $username, $password);
if(!$con) { echo "Cannot connect to the database ";die();}
mysqli_select_db($con, $database);
$result=mysqli_query($con, 'show tables');
$tableArray = array();
while($tables = mysqli_fetch_row($result)) 
{
     $tableArray[] = $tables[0];    
}

$already = 0;
$new = 0;
for($rs = 0; $rs < count($tableArray); $rs++)
{
    $exists = FALSE;

    $result = mysqli_query($con, "SHOW COLUMNS FROM ".$tableArray[$rs]." LIKE 'tags'");
    $exists = (mysqli_num_rows($result))?TRUE:FALSE;

    if($exists == FALSE)
    {
        mysqli_query($con, "ALTER TABLE ".$tableArray[$rs]." ADD COLUMN tags VARCHAR(500) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL");
        ++$new;
        echo '#'.$new.' Table DONE!<br/>';
    }
    else
    {
        ++$already;
        echo '#'.$already.' Field defined alrady!<br/>';    
    }
    echo '<br/>';
}
?>
webblover
la source
1

Ce travail pour moi avec un échantillon AOP:

public function GetTableColumn() {      
$query  = $this->db->prepare("SHOW COLUMNS FROM `what_table` LIKE 'what_column'");  
try{            
    $query->execute();                                          
    if($query->fetchColumn()) { return 1; }else{ return 0; }
    }catch(PDOException $e){die($e->getMessage());}     
}
user3706926
la source
0

NE PAS mettre ALTER TABLE / MODIFY COLS ou toute autre opération de modification de table dans une TRANSACTION. Les transactions sont pour pouvoir annuler un échec de QUERY et non pour des ALTERations ... cela entraînera une erreur à chaque fois dans une transaction.

Exécutez simplement une requête SELECT * sur la table et vérifiez si la colonne est là ...

gmize
la source
ALTER(et DDL comme ça) dans MySQL valide en effet (implicitement) la transaction. Mais l'utilisation de SELECT * générerait une surcharge importante dans le réseau pour simplement vérifier l'existence de la colonne. Vous pouvez plutôt essayer SELECT my_col_to_check FROM t LIMIT 0: si mysql génère une erreur, alors la colonne n'existe probablement pas (vérifiez quand même l'erreur, car il peut s'agir d'un problème de réseau ou autre!); s'il ne génère aucune erreur, la colonne existe. Je ne suis pas sûr que ce soit le meilleur moyen de vérifier l'existence d'une colonne.
Xenos