I trying update my plugin. So I must upgrade mysql_table. But when trying new column, program get exception.
This is my current table :
$sql = "CREATE TABLE {$table_name} (
say_id int(11) not null AUTO_INCREMENT,
customer_mail text not null,
customer_name text not null,
customer_messagge text not null,
messagge_date_time datetime not null,
PRIMARY KEY (say_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1";
require_once(ABSPATH . "wp-admin/includes/upgrade.php");
dbDelta($sql);
Now I am add colum more one table. I try Alter table, this working one time and add a column but one more refresh I get this error.
This is mycode
$wpdb->query("ALTER TABLE wp_customer_say ADD say_state INT(1) NOT NULL DEFAULT 1");
And this is my error
WordPress database error: [Duplicate column name 'say_state']
ALTER TABLE wp_customer_say ADD say_state INT(1) NOT NULL DEFAULT 1
I see this error and ı try this;
$query = $wpdb->query("select * from wp_customer_say");
$respond = mysql_num_fields( $query );
$column_array = array();
for($i = 0; $i < $respond ; $i++):
$column_array[] = mysql_field_name($query,$i);
endfor;
if( !in_array("say_state",$column_array) ):
$wpdb->query("ALTER TABLE wp_customer_say ADD say_state INT(1) NOT NULL DEFAULT 1");
endif;
and I get this error.
Warning: mysql_num_fields() expects parameter 1 to be resource, integer given in
Help please. Thank you.
Sorry bad english.
Use this query. I use only mysql-standred for get field name by fast query and this will solve your problem:
$row = $wpdb->get_results( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'wp_customer_say' AND column_name = 'say_state'" );
if(empty($row)){
$wpdb->query("ALTER TABLE wp_customer_say ADD say_state INT(1) NOT NULL DEFAULT 1");
}
You can check column name exists in WordPress using below way,
$myCustomer = $wpdb->get_row("SELECT * FROM wp_customer_say");
//Add column if not present.
if(!isset($myCustomer->say_state)){
$wpdb->query("ALTER TABLE wp_customer_say ADD say_state INT(1) NOT NULL DEFAULT 1");
}
Best and correct way to add new column into the table if column not exists.
/*# Add status column if not exist */
global $wpdb;
$dbname = $wpdb->dbname;
$marks_table_name = $wpdb->prefix . "wpsp_mark";
$is_status_col = $wpdb->get_results( "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `table_name` = '{$marks_table_name}' AND `TABLE_SCHEMA` = '{$dbname}' AND `COLUMN_NAME` = 'status'" );
if( empty($is_status_col) ):
$add_status_column = "ALTER TABLE `{$marks_table_name}` ADD `status` VARCHAR(50) NULL DEFAULT NULL AFTER `attendance`; ";
$wpdb->query( $add_status_column );
endif;
Just wanted to add that there's a slightly better way to do this than #Amandeep Wadhawan's answer.
register_activation_hook(__FILE__, 'install_tables');
function install_tables()
{
update_options('my_plugins_current_db_version', 0); // Replace 0 with whatever your final database version is
// Code here to create the final version of your database tables
}
add_action('plugins_loaded', 'update_databases');
public function update_databases()
{
global $wpdb;
$prefix = $wpdb->prefix;
$a_table_to_update = $prefix . $a_table_to_update;
$current_version = get_option('my_plugins_current_db_version', 0);
switch($current_version)
{
// First update
case 0:
// first update code goes here (alter, new table, whatever)
$current_version++;
case 1:
// next update code goes here
$current_version++;
}
update_option('my_plugins_current_db_version', $current_version);
}
You just have to make sure that your install_tables() function will always create the tables that reflect the final version number and sets the my_plugins_current_db_version option to the final version number.
Then in the update_databases() function you just have to make sure to increment $current_version before each subsequent case.
With this set-up you can blindly update without having to unnecessarily query to find out if columns or tables exist first - and less queries are always a good thing....especially if your update code is firing on the plugins_loaded hook.
It is also much cleaner, shows a clear upgrade path, and only executes necessary updates - so it is more efficient.
I really like Rikesh's option (even upvoted!), but I think to prevent hardcoding information which could change, such as $table_prefix from the wp-config.php file, this option is a safer bet.
// Replace `table_name` with the actual table name
$table = $table_prefix.'table_name';
// And use sprintf to pass the table name
// Alternatively, you can use $table instead of sprintf,
// if you use double quotes such as shown here
$myCustomer = $wpdb->get_row( sprintf("SELECT * FROM %s LIMIT 1", $table) );
// If you prefer not using sprintf, make sure you use double quotes
// $myCustomer = $wpdb->get_row( "SELECT * FROM $table LIMIT 1" );
// Replace "missing_field" by the column you wish to add
if(!isset($myCustomer->missing_field)) {
$wpdb->query( sprintf( "ALTER TABLE %s ADD phone2 VARCHAR(255) NOT NULL", $table) );
// Alternate example using variable
// $wpdb->query( "ALTER TABLE $table ADD phone2 VARCHAR(255) NOT NULL") );
}
Wrote a good solution, it works in all cases
$check_column = (array) $wpdb->get_results( "SELECT count(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME = '{$wpdb->prefix}my_table' AND COLUMN_NAME = 'some_name'" )[0];
$table_name = $wpdb->prefix . 'my_table';
$check_column = (int) array_shift($check_column);
if($check_column == 0) {
$wpdb->query(
"ALTER TABLE $table_name
ADD COLUMN `some_name` VARCHAR(55) NOT NULL
");
}
$myCustomer = $wpdb->get_row("SELECT * FROM wp_customer_say");
//Add column if not present.
if(!isset($myCustomer->say_state)){
$wpdb->query("ALTER TABLE wp_customer_say ADD say_state INT(1) NOT NULL DEFAULT '' ");
This code it will fail if you have no records in the database. Only works if you have at least one record saved in your database.
Related
I am trying to create a wordpress plugin. I had created a table and trying to retrieve data from the table but it give me an error message
Warning: Missing argument 2 for wpdb::prepare(), called in
/home2/l2on708/public_html/mysite.com/wp-content/plugins/myplugin/myplugin.php
on line 79 and defined in
/home2/l2on708/public_html/mysite.com/wp-includes/wp-db.php on line
1210
<?php
$table_name = $wpdb->prefix . "gallery_rating";
function options_page(){
/*
* displaying back-end plugin page
*/
global $table_name;
global $wpdb;
$stmt = $wpdb->prepare("SELECT * FROM $table_name ORDER BY id DESC LIMIT 1 ");
$stmt->execute();
$rows = $stm->fetchALL(PDO::FETCH_ASSOC);
foreach ($rows as $rows) {
$options_link = $rows['link'];
}
}
function table_install () {
global $wpdb;
global $table_name;
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
link varchar(100) NOT NULL,
src varchar(250) NOT NULL,
click int(11) NOT NULL,
UNIQUE KEY id (id)
) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
}
// run the install scripts upon plugin activation
register_activation_hook(__FILE__,'table_install');
?>
As of WordPress 3.5, wpdb::prepare() enforces a minimum of 2 arguments (as the error tells you):
The query
A value to substitute into the placeholder.
More info can be found in the Codex.
That said, prepare() may not even be the correct method for you to be using in this case; you may be better off using something like get_results().
The query:
UPDATE caption_queue SET status = 'Conversion Completed' WHERE tpi_id = '3130'
As stated in the title, when I run this in PHP, the value is set to an empty string. However, when the exact same query is run directly in MySQL, it works correctly.
On top of that, I'm only getting this behavior on a single enum value: 'Conversion Completed'. When updating with other values (most of which also contain spaces), there is no problem.
Actual PHP code for those interested:
$sql = "UPDATE caption_queue SET status = 'Conversion Completed' WHERE tpi_id = '$tpi_id'";
$val = mysqli_query($link, $sql);
//$link comes from somewhere else, but we use it extensively throughout our website
Table definition:
CREATE TABLE IF NOT EXISTS `caption_queue` (
`tpi_id` int(11) NOT NULL,
`pid` int(6) DEFAULT NULL,
`conversion_began` datetime DEFAULT NULL,
`yt_caption_id` varchar(50) DEFAULT NULL,
`yt_video_id` varchar(50) DEFAULT NULL,
`status` enum('Pending Conversion','Converting','Conversion Completed','Pending Upload','Video Processing','Video Processed','Uploading Transcription','Caption Syncing','Caption Synced','Caption Downloading','Caption Ready') DEFAULT 'Pending Conversion'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
if you are using MySQLI and your database has enums you need to find the place of your value in order to update your database because it does not accept new strings!
here is an example of the column of my database configuration!
status enum('active', 'inactive', 'banned')
if you want to update these values convert the values into numbers for example active = 1, inactive = 2, banned = 3
from PHP we are able to do the following
$query = 'UPDATE '.$this->table.' SET status = :status'
$stmt = $this->conn->prepare($query);
if($this->status == 'active')
{
$finalStatus = 1;
}
if($this->status == 'inactive')
{
$finalStatus = 2;
}
if($this->status == 'banned')
{
$finalStatus = 3;
}
$stmt->bindParam(':status', $finalStatus);
$stmt->execute();
and this will save your day!
this code was used as an example to provide a full solution to this issue!
Thanks.
I think you'll find it should work if you put the column called status in back ticks.
$query="UPDATE caption_queue SET `status` = 'Conversion Completed' WHERE tpi_id = '3130'";
I found a workaround. By using strict mode:
SET SESSION sql_mode = 'STRICT_ALL_TABLES'
I'm able to update the field with no issues. Seems like some kind of issue with mysqli.
I am running this SQL Query in PHP:
$sql="alter table callplandata change '".$_POST["col_name$y"]."' '".$_POST["col_newname$y"]."' ";
to alter column names but before it updates i want to check if the column name already exists and if it does to add a number on the end otherwise to just carry on updating
how can i do this using PHP?
Please, please, please, don't do this. This is about as unsafe a thing to do. However, I will say this: The ALTER TABLE syntax is worth a look:
ALTER TABLE <table name>
CHANGE [COLUMN] old_col_name new_col_name column_definition
Note that the column_definition bit is not optional.
Also, if you want to see if the fieldname given already exists:
SHOW COLUMNS FROM <table_name> /* or SHOW COLUMNS IN tbl */
Then, in PHP, depending on the extension you used, you do something like this:
$existingFields = array();
foreach ($resultSet as $row)
{
$existingFields[] = $row['Field'];
}
SHOW COLUMNS will also give you information concerning the type of each field, if it's a key, or even if it's an auto_increment value details, as ever, on the mysql website
So putting it all together:
$db = new PDO();//connect
$stmt = $db->prepare('SHOW COLUMNS IN callplandata WHERE Field = :field');
$bind = array(
':field' => $_POST['colname_new']
);
$stmt->execute($bind);
if ($row = $stmt->fetch())
throw new InvalidArgumentException($_POST['colname_new'].' already exists!');
$bind[':field'] = $_POST['colname_old'];
$stmt->closeCursor();//reset cursor, so we can re-use the statement
$stmt->execute($bind);//re-use statement
if (!($row = $stmt->fetch(PDO::FETCH_OBJ))
throw new InvalidArgumentException($_POST['colname_old'].' does not exist, cannot rename it!');
//very basic column definition construction here, needs more work, though!
$current = '';
$current = $row->Type. ' '.($row->Null == 'NO' ? 'NOT NULL ' : '').
($row->Default !== '' ? 'DEFAULT '.$row->Default.' ' : '').$row->Extra;
/**
* ADD CODE HERE TO SANITIZE THE NEW COLUMN NAME
* if you want to procede with this madness... I would urge you not to, though!
*/
$pdo->exec(
'ALTER TABLE callplandata
CHANGE '.$_POST['colname_old'].' '.$_POST['colname_new'].' './/rename
$current//add column definition
);
Disclaimer:
The code I posted here is meant to be purely academic. It should not be used, it's unsafe and incomplete. Please rethink what you are trying to do. Avoid, at all cost, using user data to alter how the server stores/structures the data!
Try this
SELECT *
FROM information_schema.COLUMNS
WHERE
TABLE_SCHEMA = 'db_name'
AND TABLE_NAME = 'table_name'
AND COLUMN_NAME = 'column_name'
In PHP
$result = mysqli_query("SHOW COLUMNS FROM `table` LIKE 'fieldname'");
$exists = (mysqli_num_rows($result))?TRUE:FALSE;
I think you need to specify datatype and default value also.
example
ALTER TABLE `ca_4_4_14` CHANGE `active` `is_active` ENUM('Y','N') CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT 'Y' NOT NULL;
I have my database with table test1.
It has a primary id "Id" which is auto-increment.
Now the id is in the format 1,2,3.. . .Is it possible to store the primary Id as
PNR1,PNR2,PNR3 .. . . and so on(with auto-increment).
No. Either add the prefix in the query, or use a view instead.
Not really, but you can use another column (but a view)
this is already covered here:
MySQL Auto Increment Custom Values
Yes you can do it if you have INT prefix. You have id as INT in table
// START PREFIX
$query = mysql_query("SELECT id FROM `table_name` ORDER BY id DESC LIMIT 1");
// GET THE LAST ID MAKE SURE IN TABLE YOU 9991
while ($row = mysql_fetch_object($query)) {
$lastId = $row->id;
}
list($prefix,$Id) = explode('999',$lastId );
$Id = ($Id+1);
$new_id = '999'.$Id;
// END PREFIX
$insertQuery = mysql_query("INSERT INTO `table_name` SET id = '".$new_id."',...");
Hi, I made it work in this way :
Products Table (products):
id_prod(varchar(11), NOT NULL, PK), name(varchar(40))
Products Sequence Table (productidseq):
id(AI, PK, NOT NULL)
Before Insert Trigger in Products Table:
CREATE DEFINER=`root`#`localhost` TRIGGER `dbname`.`products_BEFORE_INSERT` BEFORE INSERT ON `products` FOR EACH ROW
BEGIN
insert into productidseq (id) values(NULL);
set new.id_prod = concat('PROD or any prefix here',last_insert_id());
set #productId = new.id_prod; -- To use outside of trigger this variable is useful.
END
When you run below query :
insert into products (name) values('Bat');
data inside tables will we be like this :
products:
id | name
---|-----
1 | Bat
productidseq:
id
---
1
If any better way than this or any cons with this, please comment below. Thanks.
This function gives me an infinite loop
function getCats($parent,$level){
// retrieve all children of $parent
$result = "";
$query = "SELECT title,parent_id from t_cats where parent_id = '$parent'";
if($rs = C_DB::fetchRecordset($query)){
while($row = C_DB::fetchRow($rs)){
$result .= str_repeat($parent,$level).$row['title']."\n";
getCats($row['parent_id'],$level+1);
}
}
return $result;
}
here is my db table
CREATE TABLE `db`.`t_cats` (
`ID` int(10) unsigned NOT NULL auto_increment,
`datasource_id` int(10) unsigned default '0',
`version` char(10) character set latin1 default 'edit',
`status` char(10) character set latin1 default 'new',
`modified_date` datetime default NULL,
`modified_by` int(10) unsigned default '0',
`title` char(255) character set latin1 default NULL,
`parent_id` int(11) default NULL,
PRIMARY KEY (`ID`),
KEY `idx_datasource_id` (`datasource_id`)
) ENGINE=MyISAM AUTO_INCREMENT=50 DEFAULT CHARSET=utf8;
I just want to be able to get my list of categories recursive.
But what am i doing wrong?
EDIT:
function getCats($parent,$level){
// retrieve all children of $parent
$result ="";
$query = "SELECT title,parent_id from t_cats where parent_id = '$parent'";
if($rs = C_DB::fetchRecordset($query)){
while($row = C_DB::fetchRow($rs)){
$result.= str_repeat($parent,$level).$row['title']."\n";
getCats($row['id'],$level + 1 );
}
}
return $result;
}
This line looks wrong:
getCats($row['parent_id'],$level+1);
You should be calling it with the current child ID - at the moment you're calling it with the same ID over and over. Something like this (you need to select the id from your table):
getCats($row['id'], $level + 1);
Edit: you need to update your query to select id:
$query = "SELECT id, title, parent_id from t_cats where parent_id = '$parent' AND id != parent_id";
I've also added a bit to stop it getting into a loop if an item is its own parent.
I found this SitePoint article on "Storing Hierarchical Data in a Database" very helpful. It's all PHP examples, and it will improve the performance of what you're trying to do dramatically.
Maybe one of the items in the db has itself as parent?
I don't know C_DB, but I'd bet that the $rs returned by fetchrecordset is a reference, which means that every invocation of getCats is using the same $rs. Exactly what it will do then is unpredictable, depending on how fetchRow is implemented.
If you want to do this (and recursive closures are a pain in SQL, I know), you should open a new connection inside getCats. and be using a separate connection for each access.
correct answer provided by greg ...
2 side notes:
in the case of infinite loops, track recursion depth (you can conveniently use $level here) or overall invocation count (if you are lazy, since this is a oneliner accessing a global counter), and terminate recursion with an exception, when it reaches a maximum value (in general, 10 is already enough to see the problem, but of course that may vary) ... and then get some debug output ... for example $query or something like "calling getCats($parent,$level)" ... would've shown you the problem in no time in this case ... :)
you should minimize the amount of queries ... traversing a tree like that is quite inefficient ... especially, if the database is on another machine ...
greetz
back2dos
Erm shouldnt it be:
$query = "SELECT title,parent_id from t_cats where id = '$parent'";
And not:
$query = "SELECT title,parent_id from t_cats where parent_id = '$parent'";