【问题标题】:How can I employ "if exists" for creating or dropping an index in MySQL?如何使用“如果存在”在 MySQL 中创建或删除索引?
【发布时间】:2011-01-29 14:59:23
【问题描述】:

我想知道是否有办法在 MySQL 上创建或销毁索引之前检查它是否存在。几年前似乎有一个功能请求,但我找不到任何解决方案的文档。这需要在使用 MDB2 的 PHP 应用程序中完成。

【问题讨论】:

  • 使用最新的 MariaDB,它支持 IF EXISTS

标签: php mysql indexing


【解决方案1】:

这是我的 4 班轮:

set @exist := (select count(*) from information_schema.statistics where table_name = 'table' and index_name = 'index' and table_schema = database());
set @sqlstmt := if( @exist > 0, 'select ''INFO: Index already exists.''', 'create index i_index on tablename ( columnname )');
PREPARE stmt FROM @sqlstmt;
EXECUTE stmt;

【讨论】:

  • 作为使用相同架构加载多个数据库的人,我会在第一行的 WHERE 中添加:“AND TABLE_SCHEMA = DATABASE()”
  • @VenerableAgents,我刚刚编辑了答案以包含它。在这里救了我的命:)
  • 如果您不想处理任何存储过程,这个答案非常好。
  • 我认为我应该阅读 :table_schema = 'database' 而不是 table_schema = database()
【解决方案2】:

IF EXISTS 修饰符尚未为 DROP INDEXCREATE INDEX 构建。但是您可以在创建/删除索引之前手动检查是否存在。

用这句话检查索引是否已经存在。

SHOW INDEX FROM table_name WHERE KEY_NAME = 'index_name'
  • 如果查询返回零 (0) 则索引不存在,那么您可以创建它。
  • 如果查询返回一个正数,那么索引存在,那么你可以删除它。

【讨论】:

  • 这个查询对我不起作用。如果我删除 WHERE 子句它可以工作,但它不是很有帮助。但是,当我删除 WHERE 子句时,会显示 KEY_name 列。
  • 我刚刚再次检查以确保为我工作。可能是版本或配置问题。我使用的是 5.1.45 版本。
  • @PabloVenturino 可以在 SQL 语句中“以编程方式”检查吗?
【解决方案3】:

这是一个 DROP INDEX IF EXISTS 过程:

DELIMITER $$

DROP PROCEDURE IF EXISTS drop_index_if_exists $$
CREATE PROCEDURE drop_index_if_exists(in theTable varchar(128), in theIndexName varchar(128) )
BEGIN
 IF((SELECT COUNT(*) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name =
theTable AND index_name = theIndexName) > 0) THEN
   SET @s = CONCAT('DROP INDEX ' , theIndexName , ' ON ' , theTable);
   PREPARE stmt FROM @s;
   EXECUTE stmt;
 END IF;
END $$

DELIMITER ;

此代码是根据此处的过程创建的:Determining if MySQL table index exists before creating

【讨论】:

    【解决方案4】:

    我调整了在此处找到的答案,并在其他地方提出了以下用于删除和创建索引的存储过程。请注意,如果需要,AddTableIndex sproc 可以删除索引。他们还接受对我的使用至关重要的架构名称。

    DELIMITER //
    
    DROP PROCEDURE IF EXISTS migrate.DropTableIndex //
    
    CREATE PROCEDURE migrate.DropTableIndex
        (
            in schemaName varchar(128) -- If null use name of current schema;
            , in tableName varchar(128) -- If null an exception will be thrown.
            , in indexName varchar(128) -- If null an exception will be thrown.
        )
    BEGIN
        SET schemaName = coalesce(schemaName, schema());
        IF((SELECT COUNT(*) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = schemaName and table_name = tableName AND index_name = indexName) > 0) THEN
            SET @s = CONCAT('DROP INDEX `' , indexName , '` ON `' , schemaName, '`.`', tableName, '`');
            PREPARE stmt FROM @s;
            EXECUTE stmt;
        END IF;
    END //
    
    DROP PROCEDURE IF EXISTS migrate.AddTableIndex//
    
    CREATE PROCEDURE migrate.AddTableIndex
        ( 
            IN schemaName varchar(128) -- If null use name of current schema;
            , IN tableName varchar(128) -- If null an exception will be thrown.
            , IN indexName varchar(128) -- If null an exception will be thrown.
            , IN indexDefinition varchar(1024) -- E.g. '(expireTS_ ASC)'
            , IN ifPresent ENUM('leaveUnchanged', 'dropAndReplace') -- null=leaveUnchanged.
            , OUT outcome tinyint(1) -- 0=unchanged, 1=replaced, 4=added.
        )
        BEGIN
    
        DECLARE doDrop tinyint(1) DEFAULT NULL;
        DECLARE doAdd tinyint(1) DEFAULT NULL;
        DECLARE tmpSql varchar(4096) DEFAULT '';
    
        SET schemaName = coalesce(schemaName, schema());
        SET ifPresent = coalesce(ifPresent, 'leaveUnchanged');
        IF EXISTS (SELECT * FROM   INFORMATION_SCHEMA.STATISTICS WHERE  table_schema = schemaName AND table_name = tableName AND index_name = indexName) THEN
            IF (ifPresent = 'leaveUnchanged') THEN
                SET doDrop = 0;
                SET doAdd = 0;
                SET outcome = 0;
                ELSEIF (ifPresent = 'dropAndReplace')
                THEN
                SET doDrop = 1;
                SET doAdd = 1;
                SET outcome = 1;
            END IF;
        ELSE
            SET doDrop = 0;
            SET doAdd = 1;
            SET outcome = 4;
        END IF;
    
        IF (doDrop = 1) THEN
            SET tmpSql = concat( 'alter table `', schemaName, '`.`', tableName, '` drop index `', indexName, '` ');
            SET @sql = tmpSql;
            PREPARE tmp_stmt FROM @sql;
            EXECUTE tmp_stmt;
            DEALLOCATE PREPARE tmp_stmt;
        END IF;
    
        IF (doAdd = 1) THEN
            SET tmpSql = concat( 'alter table `', schemaName, '`.`', tableName, '` add index `', indexName, '` (', indexDefinition, ')');
            SET @sql = tmpSql;
            PREPARE tmp_stmt FROM @sql;
            EXECUTE tmp_stmt;
            DEALLOCATE PREPARE tmp_stmt;
        END IF;
    
        END;
    //
    
    DELIMITER ;
    

    【讨论】:

    • 做得很好,刚刚点击了这个迁移脚本! :) 您可能希望在过程名称中使用一些 _ 字符,但 ;)
    【解决方案5】:

    我在 MySQL 中使用 SELECT IF() 语句有类似的情况。

    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;
    

    使用 if() 语句的好处是,它不需要存储过程。

    【讨论】:

      【解决方案6】:

      我认为这将有助于您删除现有索引。

              DELIMITER //
              CREATE PROCEDURE dropIndexing
              ()
              BEGIN
      
              IF EXISTS(
                          SELECT * FROM information_schema.statistics 
                          WHERE TABLE_SCHEMA = DATABASE() 
                          AND `table_name`='mytable' 
                          AND `index_name` = 'myindex'
                      )
              THEN
              ALTER TABLE `mytable` DROP INDEX `myindex`;
              END IF;
      
              END //
              DELIMITER ;
      
              CALL dropIndexing();
              DROP PROCEDURE dropIndexing;
      

      【讨论】:

        【解决方案7】:

        MySQL Workbench 6.3 版(MySql fork MariaDb)

        DROP INDEX IF EXISTS FK_customer__client_school__school_id ON dbname.tablename;
        

        【讨论】:

        • 这在 MariaDB 上运行良好。 Workbench 突出显示“如果”,就好像它是一个错误一样。
        【解决方案8】:

        我对此处介绍的一些解决方案有疑问。这是我想出的:

        DELIMITER $$
        
        DROP PROCEDURE IF EXISTS myschema.create_index_if_not_exists $$
        CREATE PROCEDURE myschema.create_index_if_not_exists(in p_tableName VARCHAR(128), in p_indexName VARCHAR(128), in p_columnName VARCHAR(128) )
        BEGIN
        
        PREPARE stmt FROM 'SELECT @indexCount := COUNT(1) from information_schema.statistics WHERE `table_name` = ? AND `index_name` = ?';
        SET @table_name = p_tableName;
        SET @index_name = p_indexName;
        EXECUTE stmt USING @table_name, @index_name;
        DEALLOCATE PREPARE stmt;
        
        -- select @indexCount;
        
        IF( @indexCount = 0 ) THEN
          SELECT 'Creating index';
          SET @createIndexStmt = CONCAT('CREATE INDEX ', p_indexName, ' ON ', p_tableName, ' ( ', p_columnName ,')');
          PREPARE stmt FROM @createIndexStmt;
          EXECUTE stmt;
          DEALLOCATE PREPARE stmt;
        END IF;
        
        END $$
        
        DELIMITER ;
        

        如下使用:

        call myschema.create_index_if_not_exists('MyTable','end_time_index','end_time');
        

        这是在带有 MySQL 5.5.24 的 MAC OS X 10.8.2 和带有 MySQL 5.5.21 的 Windows 7 上测试的

        【讨论】:

        • 从 information_schema 中选择时,还应指定 db.... SELECT @indexCount := COUNT(1) from information_schema.statistics WHERE table_schema='DB' .......否则,您可以从具有相同表和索引名称的不同数据库中挑选索引 - 让我失望了。
        【解决方案9】:

        这是DROP INDEX IF EXISTS 的解决方法,v10.1.4 之前的 MySQL 和 MariaDB 版本中缺少该解决方法。您也可以将它用于您想要的所有其他语句,这应该取决于 INDEX 的存在(例如,SELECT "info: index exists.",如下例所示)。

        -- DROP INDEX IF EXISTS
        SELECT
            COUNT(*)
        INTO
            @INDEX_my_index_ON_TABLE_my_table_EXISTS
        FROM
            `information_schema`.`statistics`
        WHERE
            `table_schema` = 'my_database'
            AND `index_name` = 'my_index'
            AND `table_name` = 'my_table'
        ;
        SET @statement := IF(
            @INDEX_my_index_ON_TABLE_my_table_EXISTS > 0,
            -- 'SELECT "info: index exists."',
            'DROP INDEX `my_index` ON `my_table`',
            'SELECT "info: index does not exist."'
        );
        PREPARE statement FROM @statement;
        EXECUTE statement;
        

        【讨论】:

          猜你喜欢
          • 2013-11-16
          • 2020-01-23
          • 2017-09-25
          • 2020-12-28
          • 2011-02-12
          • 2010-12-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多