Title: dbDelta
Published: April 25, 2014
Last modified: February 24, 2026

---

# dbDelta( string[]|string $queries, bool $execute = true ): string[]

## In this article

 * [Description](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#description)
 * [Parameters](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#parameters)
 * [Return](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#return)
 * [Source](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#source)
 * [Hooks](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#hooks)
 * [Related](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#related)
 * [Changelog](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#changelog)
 * [User Contributed Notes](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#user-contributed-notes)

[ Back to top](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#wp--skip-link--target)

Modifies the database based on specified SQL statements.

## 󠀁[Description](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#description)󠁿

Useful for creating new tables and updating existing tables to a new structure.

## 󠀁[Parameters](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#parameters)󠁿

 `$queries`string[]|stringoptional

The query to run. Can be multiple queries in an array, or a string of queries separated
by semicolons. Default empty string.

`$execute`booloptional

Whether or not to execute the query right away.

Default:`true`

## 󠀁[Return](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#return)󠁿

 string[] Strings containing the results of the various update queries.

## 󠀁[Source](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#source)󠁿

    ```php
    function dbDelta( $queries = '', $execute = true ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
    	global $wpdb;

    	if ( in_array( $queries, array( '', 'all', 'blog', 'global', 'ms_global' ), true ) ) {
    		$queries = wp_get_db_schema( $queries );
    	}

    	// Separate individual queries into an array.
    	if ( ! is_array( $queries ) ) {
    		$queries = explode( ';', $queries );
    		$queries = array_filter( $queries );
    	}

    	/**
    	 * Filters the dbDelta SQL queries.
    	 *
    	 * @since 3.3.0
    	 *
    	 * @param string[] $queries An array of dbDelta SQL queries.
    	 */
    	$queries = apply_filters( 'dbdelta_queries', $queries );

    	$cqueries   = array(); // Creation queries.
    	$iqueries   = array(); // Insertion queries.
    	$for_update = array();

    	// Create a tablename index for an array ($cqueries) of recognized query types.
    	foreach ( $queries as $qry ) {
    		if ( preg_match( '|CREATE TABLE ([^ ]*)|', $qry, $matches ) ) {
    			$table_name = trim( $matches[1], '`' );

    			$cqueries[ $table_name ]   = $qry;
    			$for_update[ $table_name ] = 'Created table ' . $matches[1];
    			continue;
    		}

    		if ( preg_match( '|CREATE DATABASE ([^ ]*)|', $qry, $matches ) ) {
    			array_unshift( $cqueries, $qry );
    			continue;
    		}

    		if ( preg_match( '|INSERT INTO ([^ ]*)|', $qry, $matches ) ) {
    			$iqueries[] = $qry;
    			continue;
    		}

    		if ( preg_match( '|UPDATE ([^ ]*)|', $qry, $matches ) ) {
    			$iqueries[] = $qry;
    			continue;
    		}
    	}

    	/**
    	 * Filters the dbDelta SQL queries for creating tables and/or databases.
    	 *
    	 * Queries filterable via this hook contain "CREATE TABLE" or "CREATE DATABASE".
    	 *
    	 * @since 3.3.0
    	 *
    	 * @param string[] $cqueries An array of dbDelta create SQL queries.
    	 */
    	$cqueries = apply_filters( 'dbdelta_create_queries', $cqueries );

    	/**
    	 * Filters the dbDelta SQL queries for inserting or updating.
    	 *
    	 * Queries filterable via this hook contain "INSERT INTO" or "UPDATE".
    	 *
    	 * @since 3.3.0
    	 *
    	 * @param string[] $iqueries An array of dbDelta insert or update SQL queries.
    	 */
    	$iqueries = apply_filters( 'dbdelta_insert_queries', $iqueries );

    	$text_fields = array( 'tinytext', 'text', 'mediumtext', 'longtext' );
    	$blob_fields = array( 'tinyblob', 'blob', 'mediumblob', 'longblob' );
    	$int_fields  = array( 'tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint' );

    	$global_tables  = $wpdb->tables( 'global' );
    	$db_version     = $wpdb->db_version();
    	$db_server_info = $wpdb->db_server_info();

    	foreach ( $cqueries as $table => $qry ) {
    		// Upgrade global tables only for the main site. Don't upgrade at all if conditions are not optimal.
    		if ( in_array( $table, $global_tables, true ) && ! wp_should_upgrade_global_tables() ) {
    			unset( $cqueries[ $table ], $for_update[ $table ] );
    			continue;
    		}

    		// Fetch the table column structure from the database.
    		$suppress    = $wpdb->suppress_errors();
    		$tablefields = $wpdb->get_results( "DESCRIBE {$table};" );
    		$wpdb->suppress_errors( $suppress );

    		if ( ! $tablefields ) {
    			continue;
    		}

    		// Clear the field and index arrays.
    		$cfields                  = array();
    		$indices                  = array();
    		$indices_without_subparts = array();

    		// Get all of the field names in the query from between the parentheses.
    		preg_match( '|\((.*)\)|ms', $qry, $match2 );
    		$qryline = trim( $match2[1] );

    		// Separate field lines into an array.
    		$flds = explode( "\n", $qryline );

    		// For every field line specified in the query.
    		foreach ( $flds as $fld ) {
    			$fld = trim( $fld, " \t\n\r\0\x0B," ); // Default trim characters, plus ','.

    			// Extract the field name.
    			preg_match( '|^([^ ]*)|', $fld, $fvals );
    			$fieldname            = trim( $fvals[1], '`' );
    			$fieldname_lowercased = strtolower( $fieldname );

    			// Verify the found field name.
    			$validfield = true;
    			switch ( $fieldname_lowercased ) {
    				case '':
    				case 'primary':
    				case 'index':
    				case 'fulltext':
    				case 'unique':
    				case 'key':
    				case 'spatial':
    					$validfield = false;

    					/*
    					 * Normalize the index definition.
    					 *
    					 * This is done so the definition can be compared against the result of a
    					 * `SHOW INDEX FROM $table_name` query which returns the current table
    					 * index information.
    					 */

    					// Extract type, name and columns from the definition.
    					preg_match(
    						'/^
    							(?P<index_type>             # 1) Type of the index.
    								PRIMARY\s+KEY|(?:UNIQUE|FULLTEXT|SPATIAL)\s+(?:KEY|INDEX)|KEY|INDEX
    							)
    							\s+                         # Followed by at least one white space character.
    							(?:                         # Name of the index. Optional if type is PRIMARY KEY.
    								`?                      # Name can be escaped with a backtick.
    									(?P<index_name>     # 2) Name of the index.
    										(?:[0-9a-zA-Z$_-]|[\xC2-\xDF][\x80-\xBF])+
    									)
    								`?                      # Name can be escaped with a backtick.
    								\s+                     # Followed by at least one white space character.
    							)*
    							\(                          # Opening bracket for the columns.
    								(?P<index_columns>
    									.+?                 # 3) Column names, index prefixes, and orders.
    								)
    							\)                          # Closing bracket for the columns.
    						$/imx',
    						$fld,
    						$index_matches
    					);

    					// Uppercase the index type and normalize space characters.
    					$index_type = strtoupper( preg_replace( '/\s+/', ' ', trim( $index_matches['index_type'] ) ) );

    					// 'INDEX' is a synonym for 'KEY', standardize on 'KEY'.
    					$index_type = str_replace( 'INDEX', 'KEY', $index_type );

    					// Escape the index name with backticks. An index for a primary key has no name.
    					$index_name = ( 'PRIMARY KEY' === $index_type ) ? '' : '`' . strtolower( $index_matches['index_name'] ) . '`';

    					// Parse the columns. Multiple columns are separated by a comma.
    					$index_columns                  = array_map( 'trim', explode( ',', $index_matches['index_columns'] ) );
    					$index_columns_without_subparts = $index_columns;

    					// Normalize columns.
    					foreach ( $index_columns as $id => &$index_column ) {
    						// Extract column name and number of indexed characters (sub_part).
    						preg_match(
    							'/
    								`?                      # Name can be escaped with a backtick.
    									(?P<column_name>    # 1) Name of the column.
    										(?:[0-9a-zA-Z$_-]|[\xC2-\xDF][\x80-\xBF])+
    									)
    								`?                      # Name can be escaped with a backtick.
    								(?:                     # Optional sub part.
    									\s*                 # Optional white space character between name and opening bracket.
    									\(                  # Opening bracket for the sub part.
    										\s*             # Optional white space character after opening bracket.
    										(?P<sub_part>
    											\d+         # 2) Number of indexed characters.
    										)
    										\s*             # Optional white space character before closing bracket.
    									\)                  # Closing bracket for the sub part.
    								)?
    							/x',
    							$index_column,
    							$index_column_matches
    						);

    						// Escape the column name with backticks.
    						$index_column = '`' . $index_column_matches['column_name'] . '`';

    						// We don't need to add the subpart to $index_columns_without_subparts
    						$index_columns_without_subparts[ $id ] = $index_column;

    						// Append the optional sup part with the number of indexed characters.
    						if ( isset( $index_column_matches['sub_part'] ) ) {
    							$index_column .= '(' . $index_column_matches['sub_part'] . ')';
    						}
    					}

    					// Build the normalized index definition and add it to the list of indices.
    					$indices[]                  = "{$index_type} {$index_name} (" . implode( ',', $index_columns ) . ')';
    					$indices_without_subparts[] = "{$index_type} {$index_name} (" . implode( ',', $index_columns_without_subparts ) . ')';

    					// Destroy no longer needed variables.
    					unset( $index_column, $index_column_matches, $index_matches, $index_type, $index_name, $index_columns, $index_columns_without_subparts );

    					break;
    			}

    			// If it's a valid field, add it to the field array.
    			if ( $validfield ) {
    				$cfields[ $fieldname_lowercased ] = $fld;
    			}
    		}

    		// For every field in the table.
    		foreach ( $tablefields as $tablefield ) {
    			$tablefield_field_lowercased = strtolower( $tablefield->Field );
    			$tablefield_type_lowercased  = strtolower( $tablefield->Type );

    			$tablefield_type_without_parentheses = preg_replace(
    				'/'
    				. '(.+)'       // Field type, e.g. `int`.
    				. '\(\d*\)'    // Display width.
    				. '(.*)'       // Optional attributes, e.g. `unsigned`.
    				. '/',
    				'$1$2',
    				$tablefield_type_lowercased
    			);

    			// Get the type without attributes, e.g. `int`.
    			$tablefield_type_base = strtok( $tablefield_type_without_parentheses, ' ' );

    			// If the table field exists in the field array...
    			if ( array_key_exists( $tablefield_field_lowercased, $cfields ) ) {

    				// Get the field type from the query.
    				preg_match( '|`?' . $tablefield->Field . '`? ([^ ]*( unsigned)?)|i', $cfields[ $tablefield_field_lowercased ], $matches );
    				$fieldtype            = $matches[1];
    				$fieldtype_lowercased = strtolower( $fieldtype );

    				$fieldtype_without_parentheses = preg_replace(
    					'/'
    					. '(.+)'       // Field type, e.g. `int`.
    					. '\(\d*\)'    // Display width.
    					. '(.*)'       // Optional attributes, e.g. `unsigned`.
    					. '/',
    					'$1$2',
    					$fieldtype_lowercased
    				);

    				// Get the type without attributes, e.g. `int`.
    				$fieldtype_base = strtok( $fieldtype_without_parentheses, ' ' );

    				// Is actual field type different from the field type in query?
    				if ( $tablefield->Type !== $fieldtype_lowercased ) {
    					$do_change = true;
    					if ( in_array( $fieldtype_lowercased, $text_fields, true ) && in_array( $tablefield_type_lowercased, $text_fields, true ) ) {
    						if ( array_search( $fieldtype_lowercased, $text_fields, true ) < array_search( $tablefield_type_lowercased, $text_fields, true ) ) {
    							$do_change = false;
    						}
    					}

    					if ( in_array( $fieldtype_lowercased, $blob_fields, true ) && in_array( $tablefield_type_lowercased, $blob_fields, true ) ) {
    						if ( array_search( $fieldtype_lowercased, $blob_fields, true ) < array_search( $tablefield_type_lowercased, $blob_fields, true ) ) {
    							$do_change = false;
    						}
    					}

    					if ( in_array( $fieldtype_base, $int_fields, true ) && in_array( $tablefield_type_base, $int_fields, true )
    						&& $fieldtype_without_parentheses === $tablefield_type_without_parentheses
    					) {
    						/*
    						 * MySQL 8.0.17 or later does not support display width for integer data types,
    						 * so if display width is the only difference, it can be safely ignored.
    						 * Note: This is specific to MySQL and does not affect MariaDB.
    						 */
    						if ( version_compare( $db_version, '8.0.17', '>=' )
    							&& ! str_contains( $db_server_info, 'MariaDB' )
    						) {
    							$do_change = false;
    						}
    					}

    					if ( $do_change ) {
    						// Add a query to change the column type.
    						$cqueries[] = "ALTER TABLE {$table} CHANGE COLUMN `{$tablefield->Field}` " . $cfields[ $tablefield_field_lowercased ];

    						$for_update[ $table . '.' . $tablefield->Field ] = "Changed type of {$table}.{$tablefield->Field} from {$tablefield->Type} to {$fieldtype}";
    					}
    				}

    				// Get the default value from the array.
    				if ( preg_match( "| DEFAULT '(.*?)'|i", $cfields[ $tablefield_field_lowercased ], $matches ) ) {
    					$default_value = $matches[1];
    					if ( $tablefield->Default !== $default_value ) {
    						// Add a query to change the column's default value
    						$cqueries[] = "ALTER TABLE {$table} ALTER COLUMN `{$tablefield->Field}` SET DEFAULT '{$default_value}'";

    						$for_update[ $table . '.' . $tablefield->Field ] = "Changed default value of {$table}.{$tablefield->Field} from {$tablefield->Default} to {$default_value}";
    					}
    				}

    				// Remove the field from the array (so it's not added).
    				unset( $cfields[ $tablefield_field_lowercased ] );
    			} else {
    				// This field exists in the table, but not in the creation queries?
    			}
    		}

    		// For every remaining field specified for the table.
    		foreach ( $cfields as $fieldname => $fielddef ) {
    			// Push a query line into $cqueries that adds the field to that table.
    			$cqueries[] = "ALTER TABLE {$table} ADD COLUMN $fielddef";

    			$for_update[ $table . '.' . $fieldname ] = 'Added column ' . $table . '.' . $fieldname;
    		}

    		// Index stuff goes here. Fetch the table index structure from the database.
    		$tableindices = $wpdb->get_results( "SHOW INDEX FROM {$table};" );

    		if ( $tableindices ) {
    			// Clear the index array.
    			$index_ary = array();

    			// For every index in the table.
    			foreach ( $tableindices as $tableindex ) {
    				$keyname = strtolower( $tableindex->Key_name );

    				// Add the index to the index data array.
    				$index_ary[ $keyname ]['columns'][]  = array(
    					'fieldname' => $tableindex->Column_name,
    					'subpart'   => $tableindex->Sub_part,
    				);
    				$index_ary[ $keyname ]['unique']     = ( '0' === (string) $tableindex->Non_unique ) ? true : false;
    				$index_ary[ $keyname ]['index_type'] = $tableindex->Index_type;
    			}

    			// For each actual index in the index array.
    			foreach ( $index_ary as $index_name => $index_data ) {

    				// Build a create string to compare to the query.
    				$index_string = '';
    				if ( 'primary' === $index_name ) {
    					$index_string .= 'PRIMARY ';
    				} elseif ( $index_data['unique'] ) {
    					$index_string .= 'UNIQUE ';
    				}

    				if ( 'FULLTEXT' === strtoupper( $index_data['index_type'] ) ) {
    					$index_string .= 'FULLTEXT ';
    				}

    				if ( 'SPATIAL' === strtoupper( $index_data['index_type'] ) ) {
    					$index_string .= 'SPATIAL ';
    				}

    				$index_string .= 'KEY ';
    				if ( 'primary' !== $index_name ) {
    					$index_string .= '`' . $index_name . '`';
    				}

    				$index_columns = '';

    				// For each column in the index.
    				foreach ( $index_data['columns'] as $column_data ) {
    					if ( '' !== $index_columns ) {
    						$index_columns .= ',';
    					}

    					// Add the field to the column list string.
    					$index_columns .= '`' . $column_data['fieldname'] . '`';
    				}

    				// Add the column list to the index create string.
    				$index_string .= " ($index_columns)";

    				// Check if the index definition exists, ignoring subparts.
    				$aindex = array_search( $index_string, $indices_without_subparts, true );
    				if ( false !== $aindex ) {
    					// If the index already exists (even with different subparts), we don't need to create it.
    					unset( $indices_without_subparts[ $aindex ] );
    					unset( $indices[ $aindex ] );
    				}
    			}
    		}

    		// For every remaining index specified for the table.
    		foreach ( (array) $indices as $index ) {
    			// Push a query line into $cqueries that adds the index to that table.
    			$cqueries[] = "ALTER TABLE {$table} ADD $index";

    			$for_update[] = 'Added index ' . $table . ' ' . $index;
    		}

    		// Remove the original table creation query from processing.
    		unset( $cqueries[ $table ], $for_update[ $table ] );
    	}

    	$allqueries = array_merge( $cqueries, $iqueries );
    	if ( $execute ) {
    		foreach ( $allqueries as $query ) {
    			$wpdb->query( $query );
    		}
    	}

    	return $for_update;
    }
    ```

[View all references](https://developer.wordpress.org/reference/files/wp-admin/includes/upgrade.php/)
[View on Trac](https://core.trac.wordpress.org/browser/tags/6.9.4/src/wp-admin/includes/upgrade.php#L2907)
[View on GitHub](https://github.com/WordPress/wordpress-develop/blob/6.9.4/src/wp-admin/includes/upgrade.php#L2907-L3329)

## 󠀁[Hooks](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#hooks)󠁿

 [apply_filters( ‘dbdelta_create_queries’, string[] $cqueries )](https://developer.wordpress.org/reference/hooks/dbdelta_create_queries/)

Filters the dbDelta SQL queries for creating tables and/or databases.

 [apply_filters( ‘dbdelta_insert_queries’, string[] $iqueries )](https://developer.wordpress.org/reference/hooks/dbdelta_insert_queries/)

Filters the dbDelta SQL queries for inserting or updating.

 [apply_filters( ‘dbdelta_queries’, string[] $queries )](https://developer.wordpress.org/reference/hooks/dbdelta_queries/)

Filters the dbDelta SQL queries.

## 󠀁[Related](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#related)󠁿

| Uses | Description | 
| [wpdb::db_server_info()](https://developer.wordpress.org/reference/classes/wpdb/db_server_info/)`wp-includes/class-wpdb.php` |

Returns the raw version string of the database server.

  | 
| [wp_should_upgrade_global_tables()](https://developer.wordpress.org/reference/functions/wp_should_upgrade_global_tables/)`wp-admin/includes/upgrade.php` |

Determine if global tables should be upgraded.

  | 
| [wp_get_db_schema()](https://developer.wordpress.org/reference/functions/wp_get_db_schema/)`wp-admin/includes/schema.php` |

Retrieve the SQL for creating database tables.

  | 
| [wpdb::db_version()](https://developer.wordpress.org/reference/classes/wpdb/db_version/)`wp-includes/class-wpdb.php` |

Retrieves the database server version number.

  | 
| [wpdb::query()](https://developer.wordpress.org/reference/classes/wpdb/query/)`wp-includes/class-wpdb.php` |

Performs a database query, using current database connection.

  | 
| [wpdb::tables()](https://developer.wordpress.org/reference/classes/wpdb/tables/)`wp-includes/class-wpdb.php` |

Returns an array of WordPress tables.

  | 
| [wpdb::suppress_errors()](https://developer.wordpress.org/reference/classes/wpdb/suppress_errors/)`wp-includes/class-wpdb.php` |

Enables or disables suppressing of database errors.

  | 
| [apply_filters()](https://developer.wordpress.org/reference/functions/apply_filters/)`wp-includes/plugin.php` |

Calls the callback functions that have been added to a filter hook.

  | 
| [wpdb::get_results()](https://developer.wordpress.org/reference/classes/wpdb/get_results/)`wp-includes/class-wpdb.php` |

Retrieves an entire SQL result set from the database (i.e., many rows).

  |

[Show 4 more](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#)
[Show less](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#)

| Used by | Description | 
| [install_network()](https://developer.wordpress.org/reference/functions/install_network/)`wp-admin/includes/schema.php` |

Install Network.

  | 
| [make_db_current()](https://developer.wordpress.org/reference/functions/make_db_current/)`wp-admin/includes/upgrade.php` |

Updates the database tables to a new schema.

  | 
| [make_db_current_silent()](https://developer.wordpress.org/reference/functions/make_db_current_silent/)`wp-admin/includes/upgrade.php` |

Updates the database tables to a new schema, but without displaying results.

  |

## 󠀁[Changelog](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#changelog)󠁿

| Version | Description | 
| [6.1.0](https://developer.wordpress.org/reference/since/6.1.0/) | Ignores display width for integer data types on MySQL 8.0.17 or later, to match MySQL behavior. Note: This does not affect MariaDB. | 
| [1.5.0](https://developer.wordpress.org/reference/since/1.5.0/) | Introduced. |

## 󠀁[User Contributed Notes](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#user-contributed-notes)󠁿

 1.   [Skip to note 13 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-3322)
 2.    [Mahd Ali](https://profiles.wordpress.org/mahd/)  [  7 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-3322)
 3.  [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-3322)
     Vote results for this note: 13[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-3322)
 4.  I think it should be a common knowledge but in order to use dbDelta function you
     must require or include upgrade.php file. So if your dbDelta code is not working
     check if you have upgrade.php inside your code or not.
      It is required as following.
 5.      ```php
         require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
         ```
     
 6.   [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D3322%23feedback-editor-3322)
 7.   [Skip to note 14 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-1329)
 8.    [Lance Cleveland](https://profiles.wordpress.org/charlestonsw/)  [  10 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-1329)
 9.  [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-1329)
     Vote results for this note: 8[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-1329)
 10. You must be very careful in your SQL command structure when creating tables with
     indexes.
 11. Here is a simple example of the proper create table syntax for a table with a 
     primary key on a field named “id” and a secondary key on a field named “first”.
 12. PRIMARY KEY must be followed by TWO SPACES then the open parenthesis then the 
     field name and a closing parenthesis.
 13. KEY must be followed by a SINGLE SPACE then the key name then a space then open
     parenthesis with the field name then a closed parenthesis.
 14.     ```php
            private function index_test_001() {
                 global $wpdb;
                 $table_name = $wpdb->prefix . 'dbdelta_test_001';
                 $wpdb_collate = $wpdb->collate;
                 $sql =
                     "CREATE TABLE {$table_name} (
                     id mediumint(8) unsigned NOT NULL auto_increment ,
                     first varchar(255) NULL,
                     PRIMARY KEY  (id),
                     KEY first (first)
                     )
                     COLLATE {$wpdb_collate}";
     
                 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
                 dbDelta( $sql );
             }
         ```
     
 15.  * On my plugin I am developing I suddenly started getting errors: // The plugin
        generated 12250 characters of unexpected output during activation. // debug
        bar showed me stuff like: Notice: Undefined index: index_type in /home/cabox/
        workspace/wp-admin/includes/upgrade.php on line 2721 I used debug bar to see
        the errors but could not identify what had started the issue. This document
        helped me, specifically the earlier suggestion to make sure that the “…PRIMARY
        KEY must be followed by TWO SPACES”. I added the extra space on all 3 of my
        sql for my custom tables. It did not fix it, but that lead me to “clean up”
        all white spaces or any blank lines that were in my sql code. I would click
        way to the right of the , on each line, and if there were blanks I would just
        backspace till I got rid of them. That fixed it. Funny, but all 3 of the custom
        tables were throwing the same type of errors when the 3 bdDelta’s ran: ` require_once(
        ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($creation_query);` This
        was in PHP 7.2.11-2+ubuntu16.04.1+deb.sury.org+1, MySQL 5.7.23, WP 5.4.1 Not
        sure if a WP update made it more sensitive. I did no changes to the code involved
        in this, before the errors started. Without the help I found on this page, 
        I would not have located whatever it was. Just wanted to give back with this
        trick.
      * [Jamie Robe](https://profiles.wordpress.org/onmountain/) [6 years ago](https://developer.wordpress.org/reference/functions/dbdelta/#comment-3924)
      * I think this is incorrect. I know of no reason why MySQL syntax would require
        two spaces after `PRIMARY KEY`, and in testing just now, I ran this function
        with only a single space after `PRIMARY KEY` and it worked fine.
      * [Room 34 Creative Services, LLC](https://profiles.wordpress.org/room34/) [2 years ago](https://developer.wordpress.org/reference/functions/dbdelta/#comment-6926)
 16.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D1329%23feedback-editor-1329)
 17.  [Skip to note 15 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-5413)
 18.   [fpuenteonline](https://profiles.wordpress.org/fpuenteonline/)  [  4 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-5413)
 19. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-5413)
     Vote results for this note: 7[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-5413)
 20. There is a funny bug when creating a DATABASE or a TABLE using an IF NOT EXIST
     clause.
 21. Example: “CREATE TABLE IF NOT EXISTS {$table_name}…”
 22. In this case, the dbDelta function tries to find a database/table called “IF”,
     that does not exist and will not modify the current database/table, and execute
     the query entered so if the database/table exists, nothing happens.
 23. So please do not use the IF NOT EXISTS clause to delegate the dbDelta function
     to manage database/table structure.
 24.  * What I found was that if IF NOT EXISTS is in the statement, it will create 
        the table if necessary, however, if the table already exists, it will not alter
        it (which is the whole point of “delta”). @fpuenteonline is correct that the
        result says “Created table IF”, which is a confusing message. For reference
        I’m using WP 5.5.6.
      * [tylercollier](https://profiles.wordpress.org/tylercollier/) [4 years ago](https://developer.wordpress.org/reference/functions/dbdelta/#comment-6021)
 25.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D5413%23feedback-editor-5413)
 26.  [Skip to note 16 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-3062)
 27.   [Bence Szalai](https://profiles.wordpress.org/grapestain/)  [  7 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-3062)
 28. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-3062)
     Vote results for this note: 5[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-3062)
 29. Note that the result array may say “Created table {yourtablename}” even if the
     table was not created, but should have been. So the result of the call is in some
     cases more like what should have been done, not what was actually done.
 30. Always check if all requested changes were done properly after using this function,
     or prepare for surprises…
 31. To check if the table was really created, you can use something like this:
 32.     ```php
         if ( $wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name ) {
             // Table was not created !!
         }
         ```
     
 33.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D3062%23feedback-editor-3062)
 34.  [Skip to note 17 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-4027)
 35.   [Denis Žoljom](https://profiles.wordpress.org/dingo_d/)  [  6 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-4027)
 36. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-4027)
     Vote results for this note: 4[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-4027)
 37. Note that you cannot use `FOREIGN KEY` constraint with `dbDelta`: [https://core.trac.wordpress.org/ticket/19207](https://core.trac.wordpress.org/ticket/19207)
 38.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D4027%23feedback-editor-4027)
 39.  [Skip to note 18 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-2195)
 40.   [Jacob N. Breetvelt](https://profiles.wordpress.org/opajaap/)  [  9 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-2195)
 41. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-2195)
     Vote results for this note: 2[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-2195)
 42. Note: If you change the name of a field, an empty column with the new name will
     be created, but the old column is not removed!
 43.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D2195%23feedback-editor-2195)
 44.  [Skip to note 19 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-4925)
 45.   [TCBarrett](https://profiles.wordpress.org/tcbarrett/)  [  5 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-4925)
 46. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-4925)
     Vote results for this note: 2[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-4925)
 47. Always use the CREATE statement.
 48. I think it is worth noting that this function has been specifically design to 
     carry out the heavy lifting for you. Instead of having to manage updated table
     specs yourself, simply *always* pass in the CREATE statement and the dbDelta function
     will compare it to the existing schema. It will perform the CREATE or UPDATE as
     needed.
 49.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D4925%23feedback-editor-4925)
 50.  [Skip to note 20 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-7028)
 51.   [thomasprice61](https://profiles.wordpress.org/thomasprice61/)  [  2 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-7028)
 52. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-7028)
     Vote results for this note: 1[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-7028)
 53. The dbDelta function uses lowercase for data types (int, varchar, datetime, text,
     etc) and is case-sensitive.
 54.     ```php
         CREATE TABLE {$table_name} (
             id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
             roles TEXT DEFAULT NULL, // should be lowercase
             start_date datetime DEFAULT NULL,
             ...
         ```
     
 55. If Deadlock errors similar to below occur, it will likely be from UPPERCASE being
     used:
 56.     ```php
         WordPress database error Deadlock found when trying to get lock; try restarting transaction for query ALTER TABLE abc_table_name CHANGE COLUMN `roles` roles TEXT DEFAULT NULL made by require_once...dbDelta
         ```
     
 57.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D7028%23feedback-editor-7028)
 58.  [Skip to note 21 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-7225)
 59.   [Koorosh](https://profiles.wordpress.org/koorosh14/)  [  1 year ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-7225)
 60. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-7225)
     Vote results for this note: 0[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-7225)
 61. I would like to add another point to the do’s and don’ts of using `dbDelta`:
 62. You should not have an empty line in your query. Otherwise you’ll get these 4 
     warnings:
      – Undefined array key “index_type” in upgrade.php on line 3019. – Undefined
     array key “index_name” in upgrade.php on line 3025. – Undefined array key “index_columns”
     in upgrade.php on line 3028. – Undefined array key “column_name” in upgrade.php
     on line 3057.
 63.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D7225%23feedback-editor-7225)
 64.  [Skip to note 22 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-2070)
 65.   [Earnest Boyd](https://profiles.wordpress.org/eboyd53/)  [  9 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-2070)
 66. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-2070)
     Vote results for this note: -1[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-2070)
 67. Be careful not to put a COMMENT on field or key; the preg_match code doesn’t handle
     it. The following code is wrong (thanks to Store Locator Plus’ code).
 68.     ```php
         private function index_test_001() {
              global $wpdb;
              $table_name = $wpdb->prefix . 'dbdelta_test_001';
              $wpdb_collate = $wpdb->collate;
              $sql =
                  "CREATE TABLE {$table_name} (
                  id mediumint(8) unsigned NOT NULL auto_increment ,
                  first varchar(255) NULL,
                  PRIMARY KEY  (id),
                  KEY first (first) COMMENT 'First name'
                  )
                  COLLATE {$wpdb_collate}";
     
              require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
              dbDelta( $sql );
          }
         ```
     
 69.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D2070%23feedback-editor-2070)
 70.  [Skip to note 23 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-2495)
 71.   [neoacevedo](https://profiles.wordpress.org/neoacevedo/)  [  8 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-2495)
 72. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-2495)
     Vote results for this note: -1[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-2495)
 73. If you need to change the structure of a table, is better to use `$wpdb` rather
     than this method because is not guaranteed it works for updating any table structure.
 74.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D2495%23feedback-editor-2495)
 75.  [Skip to note 24 content](https://developer.wordpress.org/reference/functions/dbdelta/?output_format=md#comment-content-2215)
 76.   [octag](https://profiles.wordpress.org/octag/)  [  9 years ago  ](https://developer.wordpress.org/reference/functions/dbdelta/#comment-2215)
 77. [You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-2215)
     Vote results for this note: -2[You must log in to vote on the helpfulness of this note](https://login.wordpress.org?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%23comment-2215)
 78. (I post here a **corrected version** of my previous note. Several typographical
     mistakes have slipped into the original version.)
      As a side-note, the **dbDelta**
     function cannot be used to **drop a table** from the **wp_** database . A function
     such as the one below can be used instead (don’t forget to replace `my_theme` 
     with your own theme name):
 79.     ```php
         function my_theme_drop_table ( $table_name = 'the_name_without_any_prefix' ){
         	global $wpdb;
     
         	$table_name_prepared = $wpdb->prefix . $table_name;
         	$the_removal_query = "DROP TABLE IF EXISTS {$table_name_prepared}";
     
         	$wpdb->query( $the_removal_query );
         }
         ```
     
 80. See also [https://developer.wordpress.org/plugins/the-basics/uninstall-methods/](https://developer.wordpress.org/plugins/the-basics/uninstall-methods/).
 81.  [Log in to add feedback](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F%3Freplytocom%3D2215%23feedback-editor-2215)

You must [log in](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fdbdelta%2F)
before being able to contribute a note or feedback.