Mysql - On Duplicate Key Update not working for 2 keys - php

Hello I have a table with two unique keys profile_id and date. I don't know where is the problem but my query is not working.
Table:
CREATE TABLE `profile_views`
(\n `id` int(11) NOT NULL AUTO_INCREMENT,
\n `profile_id` varchar(45) DEFAULT NULL,
\n `counter` varchar(45) DEFAULT NULL,
\n `date` date DEFAULT NULL,
\n PRIMARY KEY (`id`),
\n UNIQUE KEY `date_UNIQUE` (`date`),
\n UNIQUE KEY `profile_id_UNIQUE` (`profile_id`)\n
) ENGINE=InnoDB AUTO_INCREMENT=150 DEFAULT CHARSET=latin1'
Data Right Now:
# id , profile_id, counter, date
113, 2 , 36 , 2015-08-27
I issue this command:
INSERT INTO profile_views (profile_id, counter, date)
VALUES (2, 1, '2015-08-28')
ON DUPLICATE KEY UPDATE counter = counter+1;
And
INSERT INTO profile_views (profile_id, counter, date)
VALUES (2, 1, '2015-08-27')
ON DUPLICATE KEY UPDATE counter = counter+1;
In this query i just changed the date so it should insert new row.
My Desired Results:
If i change date still it changing the same profile id counter.
I want to store everyday profile views for each profile id. So if the date and profile id is same its increment the counter otherwise insert new row.
Any help? Thanks.

Schema:
CREATE TABLE `profile_views`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`profile_id` varchar(45) DEFAULT NULL,
`counter` varchar(45) DEFAULT NULL,
`date` date DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `date_UNIQUE` (`date`),
UNIQUE KEY `profile_id_UNIQUE` (`profile_id`)
) ENGINE=InnoDB auto_increment=150;
insert profile_views (id,profile_id,counter,date) values (113,2,36,'2015-08-27');
...
...
select * from profile_views;
+-----+------------+---------+------------+
| id | profile_id | counter | date |
+-----+------------+---------+------------+
| 113 | 2 | 36 | 2015-08-27 |
+-----+------------+---------+------------+
INSERT INTO profile_views (profile_id, counter, date)
VALUES (2, 1, '2015-08-28')
ON DUPLICATE KEY UPDATE counter = counter+1;
-- 2 row(s) affected
select * from profile_views;
+-----+------------+---------+------------+
| id | profile_id | counter | date |
+-----+------------+---------+------------+
| 113 | 2 | 37 | 2015-08-27 |
+-----+------------+---------+------------+
INSERT INTO profile_views (profile_id, counter, date)
VALUES (2, 1, '2015-08-27')
ON DUPLICATE KEY UPDATE counter = counter+1;
-- 2 row(s) affected
select * from profile_views;
+-----+------------+---------+------------+
| id | profile_id | counter | date |
+-----+------------+---------+------------+
| 113 | 2 | 38 | 2015-08-27 |
+-----+------------+---------+------------+
It looks good to me. Each insert on duplicate update has a unique key clash, allowing the update to happen. What clashes? Well the unique key on profile_id does.
What am I missing?
If you lay things out step by step, people can visualize it better :>
Edit: (OP changed his mind)
CREATE TABLE `profile_views`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`profile_id` varchar(45) DEFAULT NULL,
`counter` varchar(45) DEFAULT NULL,
`date` date DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `combo_thingie1` (profile_id,`date`) -- unique composite
) ENGINE=InnoDB auto_increment=150;
insert profile_views (id,profile_id,counter,date) values (113,2,36,'2015-08-27');
... ...
select * from profile_views;
+-----+------------+---------+------------+
| id | profile_id | counter | date |
+-----+------------+---------+------------+
| 113 | 2 | 36 | 2015-08-27 |
+-----+------------+---------+------------+
INSERT INTO profile_views (profile_id, counter, date)
VALUES (2, 1, '2015-08-28')
ON DUPLICATE KEY UPDATE counter = counter+1;
select * from profile_views;
+-----+------------+---------+------------+
| id | profile_id | counter | date |
+-----+------------+---------+------------+
| 113 | 2 | 36 | 2015-08-27 |
| 150 | 2 | 1 | 2015-08-28 |
+-----+------------+---------+------------+
INSERT INTO profile_views (profile_id, counter, date)
VALUES (2, 1, '2015-08-27')
ON DUPLICATE KEY UPDATE counter = counter+1;
select * from profile_views;
+-----+------------+---------+------------+
| id | profile_id | counter | date |
+-----+------------+---------+------------+
| 113 | 2 | 37 | 2015-08-27 |
| 150 | 2 | 1 | 2015-08-28 |
+-----+------------+---------+------------+

I came up with this mad structure - it inserts new records for new dates and then updates on successive insert statements - thus incrementing the counter.
CREATE TABLE `profile_views` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`profile_id` VARCHAR(45) NOT NULL,
`counter` VARCHAR(45) NOT NULL,
`date` DATE NOT NULL,
PRIMARY KEY (`id`, `profile_id`, `date`),
UNIQUE INDEX `profile_id_date` (`profile_id`, `date`),
UNIQUE INDEX `id_profile_id_date` (`id`, `profile_id`, `date`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=267;

Related

Update/Insert Large table records based on another Table ReferenceID.

I have an Internal Inventory system with the below 3 tables as
a. Stocks - Daily updated from a CSV file.
---------------------------------
| id | MODELNO | Discount | MRP |
---------------------------------
| 1 | MODEL_1 | 40% | 900 |
| 2 | MODEL_A | 20% | 600 |
---------------------------------
Everyday this table is truncated and new stocks data are imported from a CSV file of a merchant.(around 6 Million records)
b. Cloths Master - The master clothes database
----------------------------------------
| ref_id | MODELNO | Name | MRP |
----------------------------------------
| 80 | MODEL_1 |Some Dress | 900 |
| 81 | MODEL_A |Another Dress| 600 |
----------------------------------------
The MODELNO is unique and ref_id the primary key. This table is part of the internal Inventory application (Has around 4.5 Million records)
c. Inventory table - It's part of the internal Applications
-------------------------------------------------
| id | ref_id | Name | MRP | status |
-------------------------------------------------
| 1 | 80 |Some Dress | 900 | ACTIVE |
| 2 | 81 |Another Dress| 600 | INACTIVE |
--------------------------------------------------
This table stores the available inventory for the product, based on the stocks and if the discount if above 40% the product is ACTIVE else by default INACTIVE.
The required functionality is that every day I need to run a script that would loop throught stock table records, and for the MODELNO update the stock on the Inventory table and If the record in Inventory table does not exist then it needs to be added.
What I have tried till now is a PHP script that would.
a. Firstly, set status in Inventory table for all records to INACTIVE.
b. And for each of the records in the stocks table, check if the MODELNO exists in Cloths Master table.
b. If the records exists then get the ref_id, and check if the ref_id exists in the Inventory Table and Update/Insert record accordingly.
The problem is that the script takes more than 8+ Hrs to complete.
Can you a suggest an efficient way, that can be used to implement the above functionality.
Note :
All the inserts and updated to the Inventory table are done using CodeIgniter's batch insert/update function.
I set all the status to INACTIVE, as there may be few products that are not present in the Stock DB.
The question in this case which comes in mind is - why not using a trigger ?
Create a table so_stocks
CREATE TABLE IF NOT EXISTS `so_stocks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`MODELNO` varchar(50) COLLATE uft8_general_ci NOT NULL DEFAULT '0',
`Discount` int(10) DEFAULT '0',
`MRP` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=uft8_general_ci;
INSERT INTO `so_stocks` (`id`, `MODELNO`, `Discount`, `MRP`) VALUES
(1, 'MODEL_1', 40, 900),
(2, 'MODEL_A', 20, 600);
Create a table so_inventory
CREATE TABLE `so_inventory` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`ref_id` INT(11) NOT NULL DEFAULT '0',
`Name` VARCHAR(255) NOT NULL DEFAULT '0' COLLATE 'uft8_general_ci',
`MRP` INT(11) NOT NULL DEFAULT '0',
`status` TINYINT(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
COLLATE='uft8_general_ci'
ENGINE=MyISAM
AUTO_INCREMENT=1
;
And finally a table so_cloths
CREATE TABLE `so_cloths` (
`ref_id` INT(11) NOT NULL AUTO_INCREMENT,
`MODELNO` VARCHAR(50) NOT NULL DEFAULT '0' COLLATE 'uft8_general_ci',
`Name` VARCHAR(255) NOT NULL DEFAULT '0' COLLATE 'uft8_general_ci',
`MRP` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`ref_id`)
)
COLLATE='uft8_general_ci'
ENGINE=MyISAM
AUTO_INCREMENT=1
;
And now the trigger
CREATE DEFINER=`root`#`::1` TRIGGER `so_cloths_after_insert` AFTER INSERT ON `so_cloths` FOR EACH ROW BEGIN
INSERT INTO so_inventory (ref_id,Name,MRP,status)
select sc.ref_id,sc.Name, sc.MRP, if (ss.Discount >= 40, 1,0) AS active from so_cloths AS sc
LEFT JOIN so_stocks AS ss ON (sc.MODELNO = ss.MODELNO)
WHERE sc.ref_id = new.ref_id;
END
Everytime you insert something into so_cloths an insert would be made into so_inventory.
Obviously it depends whether you want to insert data after inserting it into so_stocks or into so_cloths - you've to decide it - but the example should give you some insight.
The definer in the trigger statement has to be changed to your settings

SQL Upvote Downvote system

I am currently working on a forum where posts need to have an upvote/downvote system.
My current sql(phpmyadmin) structure is like this:
Table 1 (posts)
| post_id | post_title | post_score |
Table 2 (pvotes)
| pvote_id | fk_post_id | fk_user_id | pvote_score |
I want to somehow make post_score (in table 1), find all pvote_score (table 2) columns and add/subtract them together, where fk_post_id (table 2) is = to post_id (table 1)
This way I hope to make a voting system that only allows every user to vote once, and automatically calculate a posts post_score from the pvote_score values.
EXAMPLE:
user_1 upvotes post_1 inserting following into table 2:
| (pvote_id) 1 | (fk_post_id) 1 | (fk_user_id) 1 | (pvote_score) 1 |
I now want post_score (table 1) to find all entries in table 2 where:
fk_post_id is same as post_id, and thereafter add or subtract the values from pvote_score and make the sum the NEW value of post_score.
I am trying to make it work just like stackoverflows own upvote/downvote system.
EDIT 1:
Question:
I want to know how can I make the post_score column automatically add/subtract the values from pvotes_score and show the sum as its value?
I've created your database structure like this:
CREATE TABLE `posts` (
`post_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`post_title` varchar(50) DEFAULT NULL,
`post_score` int(11) DEFAULT NULL,
PRIMARY KEY (`post_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
INSERT INTO `posts` VALUES (NULL, 'test', 0), (NULL, 'test2', 0);
CREATE TABLE `pvotes` (
`pvote_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`fk_post_id` int(11) DEFAULT NULL,
`fk_user_id` int(11) DEFAULT NULL,
`pvote_score` int(11) DEFAULT NULL,
PRIMARY KEY (`pvote_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
INSERT INTO `pvotes` VALUES (NULL, 1, 0, 2), (NULL, 1, 0, 3), (NULL, 1, 0, -1), (NULL, 2, 0, 2);
This is the query that should do the trick:
UPDATE posts SET post_score = (SELECT SUM(pvote_score) FROM pvotes WHERE fk_post_id = post_id);
The result I've got is this:
post_id | post_title | post_score
1 | test | 4
2 | test2 | 2

How do I make a SQL Command with INSERT AND COUNT

I don't know how to COUNT a column named id. I tried
mysql_query("INSERT INTO `servers` (`user_id`, `ip`, `port`, `banner`, `disabled`, `vip`,`premium`, `name`, `status`, `votifier_key`, `votifier_port`, `country`)
VALUES ('$session_user_id', '$ip', '$port', 's=.'id'.back', '$disabled', 0,'false', '$name', '1', '$votifier', '$votPort', '$country')");
But it's not working, because I couldn't get id. Can someone help?
You need to use INSERT ... SELECT request.
Suppose, we have an empty table test:
test
|----+-------|
| id | value |
|----+-------|
CREATE TABLE IF NOT EXISTS `test` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`value` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
When we run this SQL request:
INSERT INTO test (value) SELECT COUNT(*) FROM `test`
we shall sequentially get test filling up with data:
| id | value |
|----+-------|
| 1 | 0 |
| 2 | 1 |
| 3 | 2 |
Use this approach for your table, and you'll get what you need.

How do I make my sql Primary Key automaticlly count up?

I defined the column id as my primary key, but how do I make it automatically one larger than the last one?
You are lookin for AUTO_INCREMENT, you can check documentation here
You will need to set id column as AUTO_INCREMENT
Example from documentation
CREATE TABLE animals (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM;
you must set autoincrement.
CREATE TABLE animals (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM;
INSERT INTO animals (name) VALUES
('dog'),('cat'),('penguin'),
('lax'),('whale'),('ostrich');
SELECT * FROM animals;
Which returns:
+----+---------+
| id | name |
+----+---------+
| 1 | dog |
| 2 | cat |
| 3 | penguin |
| 4 | lax |
| 5 | whale |
| 6 | ostrich |
+----+---------+
http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html mysql reference
Ok didn't notice the tags.
Hit the A_I checkbox in phpMyAdmin for the id column.
---old---
The Oracle way, triggered sequence:
CREATE sequence aic increment BY 1 start WITH 1;
CREATE TABLE blarg (
id NUMBER(15,0) PRIMARY KEY,
foobar VARCHAR2(255)
);
CREATE TRIGGER trigger ait BEFORE INSERT ON blarg
REFERENCING NEW AS NEW OLD AS OLD FOR EACH ROW
Begin
SELECT aic.NEXTVAL INTO :NEW.id FROM DUAL;
End;

Using MySQL, What is the best way to not to select users that exist in a different table?

My problem is the following:
I have two tables; persons and teams, I want to select all the persons with role_id = 2, that exist in persons but not in teams.
Table teams stores the hashes for the team leader who can only lead one team at a time. When creating teams, I just want to show administrators the people who is not currently leading a team, basically exclude all the ones who are already leaders of any given team.
My structure is as follows:
mysql> desc persons;
+-------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| firstname | varchar(9) | YES | | NULL | |
| lastname | varchar(10) | YES | | NULL | |
| role_id | int(2) | YES | | NULL | |
| hash | varchar(32) | NO | UNI | NULL | |
+-------------+-------------+------+-----+---------+-------+
mysql> desc teams;
+--------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| leader | varchar(32) | NO | | NULL | |
+--------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
My current SQL is as follows:
SELECT CONCAT( `persons`.`firstname` ," ", `persons`.`lastname` ) AS `manager`,
`hash` FROM `persons`
WHERE `persons`.`role_id` =2 AND `persons`.`hash` !=
(SELECT `leader` FROM `teams` );
The latter SQL Query works when the table teams only has 1 record, but as soon as I add another one, MySQL complaints about the subquery producing two records.
In the WHERE Clause, instead of subqueries I've also tried the following:
WHERE `persons`.`role_id` = 2 AND `persons`.`hash` != `teams`.`leader`
but then it complaints about column leader not existing in table teams
I was also thinking about using some kind of inverse LEFT JOIN, but I haven't been able to come up with an optimal solution.
Any help is greatly appreciated!
Thanks
P.S.: Here is the SQL statements should you want to have a scenario similar to mine:
DROP TABLE IF EXISTS `teams`;
CREATE TABLE IF NOT EXISTS `teams` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`leader` varchar(32) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
INSERT INTO `teams` (`id`, `name`, `leader`) VALUES
(1, 'Team 1', '406a3f5892e0fcb22bfc81ae023ce252'),
(2, 'Team 2', 'd0ca479152996c8cabd89151fe844e63');
DROP TABLE IF EXISTS `persons`;
CREATE TABLE IF NOT EXISTS `persons` (
`firstname` varchar(9) DEFAULT NULL,
`lastname` varchar(10) DEFAULT NULL,
`role_id` int(2) DEFAULT NULL,
`hash` varchar(32) NOT NULL,
PRIMARY KEY `hash` (`hash`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `persons` (`firstname`, `lastname`, `role_id`,`hash`) VALUES
('John', 'Doe', 2, '406a3f5892e0fcb22bfc81ae023ce252'),
('Jane', 'Doe', 2, 'd0ca479152996c8cabd89151fe844e63'),
('List', 'Me', 2, 'fbde2c4eeee7f455b655fe4805cfe66a'),
('List', 'Me Too', 2, '6dee2c4efae7f452b655abb805cfe66a');
You don't need a subquery to do that. A LEFT JOIN is enough:
SELECT
CONCAT (p.firstname, " ", p.lastname) AS manager
, p.hash
FROM persons p
LEFT JOIN teams t ON p.hash = t.leader
WHERE
p.role_id = 2
AND t.id IS NULL -- the trick
I think you want an IN clause.
SELECT CONCAT( `persons`.`firstname` ," ", `persons`.`lastname` ) AS `manager`,
`hash` FROM `persons`
WHERE `persons`.`role_id` =2 AND `persons`.`hash` NOT IN
(SELECT `leader` FROM `teams` );
As pointed out, this is not optimal. You may want to do a join instead.

Categories