MySQL: Crear índice si no existe

62

¿Hay alguna manera de crear un índice en MySQL si no existe?

MySQL no admite el formato obvio:

CREATE INDEX IF NOT EXISTS index_name ON table(column)
ERROR 1064 (42000): You have an error in your SQL syntax;...

La versión de MySQL ( mysql -V) es 5.1.48, pero creo que MySQL carece de la CREATE INDEX IF NOT EXISTcapacidad en todas sus versiones.

¿Cuál es la forma correcta de crear un índice solo si aún no existe en MySQL?

Adam Matan
fuente

Respuestas:

36

Esa funcionalidad no existe. Hay dos cosas a tener en cuenta:

Crear el índice de todos modos

Puede generar el índice de tal manera que el índice se cree sin verificar si el índice existe antes de tiempo. Por ejemplo, puede ejecutar lo siguiente:

ALTER TABLE table_name ADD INDEX (column_to_index);
ALTER TABLE table_name ADD INDEX (column_to_index);

Esto definitivamente creará dos índices sin verificar. A cada índice se le asignará un nombre (quizás column_to_index, column_to_index_1). Por supuesto, estás tratando de evitar eso.

Verifique INFORMACIÓN_SQUEMA primero

Aquí está el diseño de INFORMATION_SCHEMA.STATISTICS:

mysql> show create table statistics\G
*************************** 1. row ***************************
       Table: STATISTICS
Create Table: CREATE TEMPORARY TABLE `STATISTICS` (
  `TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
  `TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '',
  `TABLE_NAME` varchar(64) NOT NULL DEFAULT '',
  `NON_UNIQUE` bigint(1) NOT NULL DEFAULT '0',
  `INDEX_SCHEMA` varchar(64) NOT NULL DEFAULT '',
  `INDEX_NAME` varchar(64) NOT NULL DEFAULT '',
  `SEQ_IN_INDEX` bigint(2) NOT NULL DEFAULT '0',
  `COLUMN_NAME` varchar(64) NOT NULL DEFAULT '',
  `COLLATION` varchar(1) DEFAULT NULL,
  `CARDINALITY` bigint(21) DEFAULT NULL,
  `SUB_PART` bigint(3) DEFAULT NULL,
  `PACKED` varchar(10) DEFAULT NULL,
  `NULLABLE` varchar(3) NOT NULL DEFAULT '',
  `INDEX_TYPE` varchar(16) NOT NULL DEFAULT '',
  `COMMENT` varchar(16) DEFAULT NULL,
  `INDEX_COMMENT` varchar(1024) NOT NULL DEFAULT ''
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql>

Simplemente puede consultar la existencia del índice por nombre. Por ejemplo, antes de correr

CREATE INDEX index_name ON mytable(column);

Necesitas correr

SELECT COUNT(1) IndexIsThere FROM INFORMATION_SCHEMA.STATISTICS
WHERE table_schema=DATABASE() AND table_name='mytable' AND index_name='index_name';

Si IndexIsThere es 0, puede crear en el índice. Quizás pueda escribir un procedimiento almacenado para crear un índice en la tabla que elija.

DELIMITER $$

DROP PROCEDURE IF EXISTS `adam_matan`.`CreateIndex` $$
CREATE PROCEDURE `adam_matan`.`CreateIndex`
(
    given_database VARCHAR(64),
    given_table    VARCHAR(64),
    given_index    VARCHAR(64),
    given_columns  VARCHAR(64)
)
BEGIN

    DECLARE IndexIsThere INTEGER;

    SELECT COUNT(1) INTO IndexIsThere
    FROM INFORMATION_SCHEMA.STATISTICS
    WHERE table_schema = given_database
    AND   table_name   = given_table
    AND   index_name   = given_index;

    IF IndexIsThere = 0 THEN
        SET @sqlstmt = CONCAT('CREATE INDEX ',given_index,' ON ',
        given_database,'.',given_table,' (',given_columns,')');
        PREPARE st FROM @sqlstmt;
        EXECUTE st;
        DEALLOCATE PREPARE st;
    ELSE
        SELECT CONCAT('Index ',given_index,' already exists on Table ',
        given_database,'.',given_table) CreateindexErrorMessage;   
    END IF;

END $$

DELIMITER ;

Aquí hay una muestra de ejecución (¿Recuerda esta tabla? Es de la pregunta que hizo el 27 de junio de 2012 ):

mysql> show create table pixels\G
*************************** 1. row ***************************
       Table: pixels
Create Table: CREATE TABLE `pixels` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(30) DEFAULT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `pixel_data` blob,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> call createindex('adam_matan','pixels','type_timestamp_id_ndx','type,timestamp,id');
Query OK, 0 rows affected (0.20 sec)

mysql> show create table pixels\G
*************************** 1. row ***************************
       Table: pixels
Create Table: CREATE TABLE `pixels` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(30) DEFAULT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `pixel_data` blob,
  PRIMARY KEY (`id`),
  KEY `type_timestamp_id_ndx` (`type`,`timestamp`,`id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> call createindex('adam_matan','pixels','type_timestamp_id_ndx','type,timestamp,id');
+-----------------------------------------------------------------------+
| CreateindexErrorMessage                                               |
+-----------------------------------------------------------------------+
| Index type_timestamp_id_ndx Already Exists on Table adam_matan.pixels |
+-----------------------------------------------------------------------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.01 sec)

mysql>

Darle una oportunidad !!!

RolandoMySQLDBA
fuente
37

Tengo algo similar con el uso de la SELECT IF()declaración en MySQL si está tratando de no tener procedimientos:

select if (
    exists(
        select distinct index_name from information_schema.statistics 
        where table_schema = 'schema_db_name' 
        and table_name = 'tab_name' and index_name like 'index_1'
    )
    ,'select ''index index_1 exists'' _______;'
    ,'create index index_1 on tab_name(column_name_names)') into @a;
PREPARE stmt1 FROM @a;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;

Aquí el select iftiene este formato if (condition, true_case, false_case). El select 'index index_1 exists'es un caso ficticio. y _____juega el papel de nombre de alias. Si no se hace el alias, se muestran el nombre de columna y la fila index index_1 exists, lo que confunde aún más. para ser más descriptivo puedes usar 'select ''index index_1 exists'' as _______;'.

Mithun B
fuente
3

Si nombra el índice, la consulta fallará si el índice ya existe (probado en MySQL 8.0):

ALTER TABLE `my_table` ADD INDEX `col_idx` (`col` DESC);

Código de error: 1061. Nombre de clave duplicado 'col_idx';

Así que puedes atrapar la excepción e ignorarla, por ejemplo en PHP:

try {
    $db->query('ALTER TABLE `my_table` ADD INDEX `col_idx` (`col` DESC) VISIBLE;');
} catch (PDOException $ex) {
    if($exception->errorInfo[2] == 1061) {
        // Index already exists
    } else {
        // Another error occurred
    }
}
Las nueces
fuente
2
SELECT COUNT(*)
FROM information_schema.statistics
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME = 'table_name' 
  AND INDEX_NAME = 'index_name'; 

Mi consulta le daría el recuento de índices presentes en una tabla con un nombre_índice particular. Según ese recuento, puede decidir si emitir un CREATE INDEXcomando o no.

Probado en MySQL versión 5.5 .

MariaDB es compatible con la IF NOT EXISTSsintaxis . Puedes usar CREATE INDEX IF NOT EXISTSallí.

Bennet Joseph
fuente