PHP PDO - MySQL Query Create or Update (unique index) - php

hope you are well.
I have a table in a MySQL (MariaDB) database with the below schema:
CREATE TABLE `scheduled_immobilise` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`account` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
`device` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
`allow_from` time DEFAULT NULL,
`allow_to` time DEFAULT NULL,
`active` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL,
`last_updated` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`cron_id_from` int(11) DEFAULT NULL,
`cron_id_to` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique` (`account`,`device`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
What I am trying to do is write a query that will either create a row (if the unique index doesn't exist), or if it already exists update it. I know you can do this by doing a select query first, but ultimately i was hoping to avoid this. Below is the SQL query I am using to create. Please note this is using PDO named placeholders..
INSERT INTO
scheduled_immobilise (
account,
device,
allow_from,
allow_to,
active
)
VALUES (
:account,
:device,
:allow_from,
:allow_to,
:active
)
Any help is much appreciated, thanks in advance!
Paul.

There's exactly a mysql option for this
INSERT INTO
scheduled_immobilise (
account,
device,
allow_from,
allow_to,
active
)
VALUES (
:account,
:device,
:allow_from,
:allow_to,
:active
)
ON DUPLICATE KEY UPDATE account = :account, device = device...etc

a slightly more correct answer
INSERT INTO
scheduled_immobilise (
account,
device,
allow_from,
allow_to,
active
)
VALUES (
:account,
:device,
:allow_from,
:allow_to,
:active
)
ON DUPLICATE KEY UPDATE
account = values(account),
device = values(device)
...etc
it will let you to use placeholders of any type, in any mode and with any driver beside PDO, while other answer will work only if emulation mode for PDO is turned on.

Related

PHP SQL query slow?

Here is my function which i am using to un-follow users.It first DELETE the relationship between users and all the notifications that are related to this relationship.Then it INSERT a new notification for user which we are going to un-follow and then UPDATE his followers count (as one follower has left).I am using multi_query and this query seems to be bit slower on large database and i want to know whether it's a good practice or not or is there is any more complex form of query to get the job done.
PHP Function
// 'By' is the array that hold logged user and 'followed' is the user id which we are going to unfollow
function unFollowUser($followed,$by) {
$following = $this->getUserByID($followed);// Return fetch_assoc of user row
if(!empty($following['idu'])) { // if user exists
// return user followers as number of rows
$followers = $this->db->real_escape_string($this->numberFollowers($following['idu'])) - 1;
$followed_esc = $this->db->real_escape_string($following['idu']);
$by_user_esc = $this->db->real_escape_string($by['idu']);
// delete relationship
$query = "DELETE FROM `relationships` WHERE `relationships`.`user2` = '$followed_esc' AND `relationships`.`user1` = '$by_user_esc' ;" ;
// delete notification (user started following you )
$query.= "DELETE FROM `notifications` WHERE `notifications`.`not_from` = '$by_user_esc' AND `notifications`.`not_to` = '$followed_esc' ;" ;
// Insert a new notification( user has unfollowed you)
$query.= "INSERT INTO `notifications`(`id`, `not_from`, `not_to`, `not_content_id`,`not_content`,`not_type`,`not_read`, `not_time`) VALUES (NULL, '$by_user_esc', '$followed_esc', '0','0','5','0', CURRENT_TIMESTAMP) ;" ;
// update user followers (-1)
$query .= "UPDATE `users` SET `followers` = '$followers' WHERE `users`.`idu` = '$followed_esc' ;" ;
if($this->db->multi_query($query) === TRUE) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
Table structures
--
-- Table structure for table `notifications`
--
CREATE TABLE IF NOT EXISTS `notifications` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`not_from` int(11) NOT NULL,
`not_to` int(11) NOT NULL,
`not_content_id` int(11) NOT NULL,
`not_content` int(11) NOT NULL,
`not_type` int(11) NOT NULL,
`not_read` int(11) NOT NULL,
`not_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table structure for table `relationships`
--
CREATE TABLE IF NOT EXISTS `relationships` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user1` int(11) NOT NULL,
`user2` int(11) NOT NULL,
`status` int(11) NOT NULL,
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Table structure for table `users`
--
CREATE TABLE IF NOT EXISTS `users` (
`idu` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`password` varchar(256) NOT NULL,
`email` varchar(256) NOT NULL,
`first_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`last_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`verified` int(11) NOT NULL,
`posts` text CHARACTER SET utf32 NOT NULL,
`photos` text CHARACTER SET utf32 NOT NULL,
`followers` text CHARACTER SET utf32 NOT NULL,
UNIQUE KEY `id` (`idu`),
UNIQUE KEY `idu` (`idu`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
In my testing, multi_query has been the fastest way to execute multiple different queries. Why do you feel it's running slow? Compared to what?
Anyway, improvements could come from adding indexes to some of the columns you search frequently:
relationships.users2
relationships.users1
notifications.not_from
notifications.not_to
users.idu
Adding indexes makes searching faster, but it has at least two downsides:
Makes the DB a lot more resource hungry, which could affect your server performance
Makes writing operations take longer
I don't see any problem with your current queries. Really consider whether the slow performance you're seeing comes from the DB queries themselves, or from the rest of your PHP process. Try measuring the script time with the queries, then skipping the queries and taking another measurement (you could hardcode query results). It will give you an idea of whether the slowness is attributable to something else.
Either way, benchmark.
Try creating index on user where deletes are running , this may speed up query

SQL/PHP - On key update

I have messed up my database design a bit. This was the original schema:
CREATE TABLE IF NOT EXISTS `xeon_stats_clicks` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`typ` enum('1','2','3','4','5','6','7','8','9') COLLATE utf8_bin NOT NULL,
`user` varchar(20) COLLATE utf8_bin NOT NULL,
`data` varchar(10) COLLATE utf8_bin NOT NULL,
`value` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `typ` (`typ`,`user`,`data`),
KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ;
As you can see, I have KEY on the following:
UNIQUE KEY `typ` (`typ`,`user`,`data`),
KEY `data` (`data`)
I have the following code execute:
"INSERT INTO `xeon_stats_clicks` (typ, user, data, value) VALUES ('1', :username, :date, 1) ON DUPLICATE KEY UPDATE value = value + 1"
However, above code doesn't work now, as my table schema now look like this:
CREATE TABLE `xeon_stats_clicks` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`typ` enum('1','2','3','4','5','6','7','8','9') COLLATE utf8_bin NOT NULL,
`user` varchar(20) COLLATE utf8_bin NOT NULL,
`data` varchar(10) COLLATE utf8_bin NOT NULL,
`value` varchar(20) COLLATE utf8_bin NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_value` (`value`),
KEY `idx_typ` (`typ`),
KEY `idx_data` (`data`),
KEY `idx_user` (`user`),
KEY `data` (`data`),
KEY `data_2` (`data`)
) ENGINE=MyISAM AUTO_INCREMENT=991799 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
How can I revert the changes made, and return to the first schema without messing up the data in the table?
I have no idea why your first schema + code doesn't work.
It works for value that is integer:
http://sqlfiddle.com/#!9/c3260/1
INSERT INTO `xeon_stats_clicks` (typ, user, data, value) VALUES
('2', 'user2', 'data3', 1) ON DUPLICATE KEY UPDATE `value` = `value` + 1
But if you will try on fiddle to apply that query to other lines it doesn't work. Because mysql can't convert VARCHAR to INT.
My guess you have wrong data in value column. For the combination of (typ, user, data, value) that you test.
UPDATE Here is the fiddle with your second schema in use:
http://sqlfiddle.com/#!9/5ea62b/1
As you can see your query works fine as well if you add
UNIQUE KEY `typ` (`typ`,`user`,`data`),
to that second schema.
and here is ALTER TABLE variant that works as well:
http://sqlfiddle.com/#!9/57b409/1
UPDATE 2 Another guess: You have broken uniqueness in your table now.
If I got you correctly you had
UNIQUE KEY `typ` (`typ`,`user`,`data`),
when start the project. After a while you did remove that UNIQUE KEY from schema. That change allowed mysql to insert duplicate records into that table. And apparently you inserted several (or a lot) of duplicates. And now you want to apply ALTER TABLE to get back unique key but mysql refuse that because of that.
Like here: http://sqlfiddle.com/#!9/4cbb5 <-- uncomment ALTER line to see error message
So you need to fix uniqueness first.
UPDATE 3 Delete duplicates:
http://sqlfiddle.com/#!9/85f228/1
DELETE FROM xeon_stats_clicks USING xeon_stats_clicks
INNER JOIN xeon_stats_clicks dup
ON xeon_stats_clicks.id < dup.id
AND xeon_stats_clicks.typ = dup.typ
AND xeon_stats_clicks.user = dup.user
AND xeon_stats_clicks.data = dup.data;

I need mysql query to generate following result

CREATE TABLE IF NOT EXISTS `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`form_id` tinyint(4) NOT NULL COMMENT '1=shortfor;2=longfom',
`remote_addr` varchar(19) NOT NULL,
`type` tinyint(4) NOT NULL COMMENT '1=impression;2=click;3=conversion',
`website_url` varchar(50) NOT NULL,
`c_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `form_id` (`form_id` , `remote_addr` , `type` , `website_url`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=64;
i want to total count impression,click,conversion.
i want group by website url group by impression,group by click ,group by conversion.
how to wright query? please help me i need to emergency !!!!!
![enter image description here][1]
I need following result
website url----------Impression----click--converison-----
192.1.1.1--------------1-----------2------1--------------
192.1.1.2--------------3-----------2------2--------------
192.1.1.3--------------4-----------3------1--------------
192.1.1.4--------------2-----------6------1--------------
please try below query
$sql = "SELECT website_url,SUM(type=1) as impression,SUM(type=2) as click,SUM(type=3) as conversion FROM `test` GROUP BY website_url";
Let me know this is the way you need.

mysql retrieve autoinc value for join insert

We have the following two tables:
CREATE TABLE IF NOT EXISTS `gp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`amount` decimal(15,2) NOT NULL,
`user` varchar(100) NOT NULL DEFAULT '',
`status` tinyint(2) NOT NULL DEFAULT '1',
`ip` varchar(20) NOT NULL DEFAULT 'N/A',
`token` varchar(100) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `gp_logs` (
`id` int(11) NOT NULL,
`log` varchar(1000) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
We JOIN them, for statistics, but we do this rarely, since the data from the 2nd table is not used too often except when we need to verify things.
Considering that we have many queries per second, how can our query be optimized to use 1 INSERT query instead of two and to insert the correct id in the 2nd table (gp_logs) that was generated by the INSERT into table gp?
Right now, we do a combination of MYSQL with PHP:
mysqli_query($con,"INSERT INTO `gp` (amount,user) VALUES ('1234','1')");
$id = mysqli_insert_id($con);
mysqli_query($con,"INSERT INTO gp_logs(id,log) VALUES ('$id','some_data')");
We want to eliminate the requirement of PHP for getting the last inserted ID and to insert both entries by running a single INSERT query (with a JOIN).

Cant "give" null value in a field of my sql table phpmyadmin

Im using phpmyadmin and I have a table categories with 4 fields:
-> id
-> id_father_category (I put this with predefined: NULL) and I also put a checkbox Null
-> name
-> content
I want to give null values to my id_father_category field.
But Im having this error:
Warning: #1366 Incorrect integer value: '' for column 'id_father_category field' at row 1
And my field id_father_category stays automatically with value 0, and I dont want that.
Somebody there knows how I can solve this problem?
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_father_category` int(11) DEFAULT NULL,
`name` varchar(64) NOT NULL,
`content` varchar(256) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Hope this will solve your problem...
This is an addition to #Nirjhor's answer.
ALTER TABLE categories MODIFY id_father_category INT(11) DEFAULT NULL;
or you can recreate the whole thing:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_father_category` int(11) DEFAULT NULL,
`name` varchar(64) NOT NULL,
`content` varchar(256) NOT NULL,
PRIMARY KEY (`id`) )
ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Setting the default value to NULL will handle your issue as well as set the field to NULL. One thing to note here is to make sure you don't have some weird index on that field cause that can also create an issue.

Categories