Insert into table or update if exists multiple unique index (MySQL) - mysql

I'm having a issue where I want to INSERT values to a table if the combination (poll_id, user_id, question_id) does not exists, else I want to update the column answer.
The table is as follows:
poll_results
poll_id - int(11) | user_id - int(11) | question_id - int(11) | answer - varchar(100)
I've set the unique to (poll_id, user_id, question_id) using:
ALTER TABLE poll_results ADD UNIQUE INDEX unique_index(poll_id, user_id, question_id);
When I run the following query it will first insert the first values set and then it updates the answer field with the value from the second value set.
INSERT INTO poll_results (poll_id, user_id, question_id, answer) VALUES(1, 1, 1, 'User 1'),(1, 2, 1, 'User 2') ON DUPLICATE KEY UPDATE answer=VALUES(answer)
What I see is:
poll_id - user_id - question_id - answer
1 | 1 | 1 | User 1
What I want to see is:
poll_id | user_id | question_id | answer
1 | 1 | 1 | User 1
1 | 2 | 1 | User 2
How can I achieve the end result?

The way the ON DUPLICATE KEY feature works is on the trigger of any UNIQUE type indexes, not just the one you intend it to trigger on.
Remove any indexes that might be interfering with this and you should be fine.

Related

PDO Deleting entries from table that does not exist in post request data

I have a table which consist of columns person_id, level_id, is_admin
person_id | level_id | is_admin
--------------------------------
1 | 1 | 1
1 | 2 | 0
3 | 2 | 1
In the server side, I have a function that accepts a request data which is an array of objects:
[
{person_id: 5, level_id: 1, is_admin: 1},
{person_id: 1, level_id: 2, is_admin: 0}
]
What I want to achieve is that, delete rows from the table whose values of columns person_id, level_id, is_admin does not exists in the post request data.
For example, the expected output of the delete query:
person_id | level_id | is_admin
--------------------------------
1 | 1 | 1
3 | 2 | 1
Notice that the second row is deleted.
EDIT: You might wonder delete entries that do not exists in post data, yes that's right. because the function meant to insert things in the table and delete existing rows that does not exist in the post data.
My current delete query is:
$delete = "
DELETE FROM pivotTable
WHERE NOT EXISTS (
SELECT * FROM (
SELECT
{$personId} AS person_id,
{$levelId} AS level_id,
{$isAdmin} AS is_admin
) as delTemp
);
";
$this->pdo->exec($delete);
no error, but it seems that it's not deleting the row in the database.
Easiest way to debug this would be to run the query as a SELECT:
SELECT FROM pivotTable
WHERE NOT EXISTS (
SELECT * FROM (
SELECT
{$personId} AS person_id,
{$levelId} AS level_id,
{$isAdmin} AS is_admin
) as delTemp
);
After this you can check if the rows you want to delete are correct.
I would also recommend looking into using WHERE NOT IN
as in:
DELETE FROM pivotTable
WHERE (person_id, level_id, is_admin) NOT IN ((5,1,1), (1,2,0));
Also it seems that you aren't using prepared statements which will lead you to be vulnerable to SQL Injection, I would recommend reading on prepared statements here:
https://phpdelusions.net/pdo

Insert into mysql table only if those values are not in table

I have search already an answer but i can't find one that is good for my situation.
I have a table called Names like this
ID NAME Age
1 Paula 20
2 Mark 17
And i want to run this sql
Insert into table names(name,age) values ("Chriss",15)//should be inserted
Insert into table names(name,age) values ("Mark",17)// should be ignored
Insert into table names(name,age) values ("Andrea",20) //should be inserted
So how can I ignore second insert query
Create a constraint that demands NAME and Age to be unique in the table.
ALTER TABLE `tablename` ADD UNIQUE `unique_index`(`NAME`, `Age`);
You would either need to Add UNIQUE constraint or check the data at the run time (if you don't have a permission to change table schema):
ALTER TABLE `Table_name`
ADD UNIQUE INDEX (`NAME`, `AGE`);
You can use:
INSERT INTO names(name,age)
SELECT * FROM (SELECT 'Chriss', 15) AS tmp
WHERE NOT EXISTS (
SELECT name FROM names WHERE name = 'Chriss' AND age = 15
) LIMIT 1;
An other way is just make the columns name and age UNIQUE so the query fails.
Change your query to this:
Insert into table names(name,age)
SELECT "Chriss",15 WHERE NOT EXISTS (SELECT 1 FROM names WHERE `name` = "Chriss");
Insert into table names(name,age)
SELECT "Mark",17 WHERE NOT EXISTS (SELECT 1 FROM names WHERE `name` = "Mark");
Insert into table names(name,age)
SELECT "Andrea",20 WHERE NOT EXISTS (SELECT 1 FROM names WHERE `name` = "Andrea");
First create a unique constraint for the columns NAME and Age:
ALTER TABLE names ADD UNIQUE un_name_age (`NAME`, `Age`);
and then use INSERT IGNORE to insert the rows:
Insert ignore into names(name,age) values
("Chriss",15),
("Mark",17),
("Andrea",20);
So if you try to insert a duplicate name the error will just be ignored and the statement will continue with the next row to insert.
See the demo.
Result:
| ID | NAME | Age |
| --- | ------ | --- |
| 1 | Paula | 20 |
| 2 | Mark | 17 |
| 3 | Chriss | 15 |
| 4 | Andrea | 20 |

Is it possible to do a select and update it with new value in the same query?

I have tgot the following table structure
mysql> desc test
-> ;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | varchar(19) | NO | PRI | | |
| name | varchar(19) | YES | | NULL | |
| age | varchar(19) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.05 sec)
Initialy i have done an insert as shown
insert into test (id, name, age) values("1", "A", 19);
my requirement is that , i need to extract the age of id "1" and add some integer to the existing age
I have seen this below example , can tis be useful in my case ??
insert into test (id, name, age) values("1", "A", 30) on duplicate key update age=values(age)
I am using JAVA , i have symbols of 300 , for which i need to update contonouslly
Is it possible to do a select and update the existing column with new value in the same query ??
For example
how can i get the existing age 19 and add it with 30 in the same query for the id 1 ??
(This question has already been answered by Marc B in the comments section.)
Yes, the statement OP has posted will work just fine, because there is a primary key constraint on the id column, and INSERT ... ON DUPLICATE KEY will cause an UPDATE of the existing row.
To "add" the value being inserted to the value that already exists in the column, we'd assign an expression that does that operation to the column:
e.g.
insert into test (id, name, age) values("1", "A", 30)
on duplicate key update age = age + values(age)
^^^^^
Note that the only change required in OP statement is the reference to the existing column value and an addition operation.
N.B. If either the existing value in the column, or the new value being supplied in the INSERT statement is NULL, the result of the expression will be NULL. A different expression would be needed if this is undesired behavior.
This should work:
UPDATE test SET age=age+1 WHERE id=1;

MySQL schema design issues - Normalizing

I'm creating tables for my site using the following design(s)
Design 1
Design 2
Since not every user who register will try the challenge, Design 1 is suited. On insert into third table, table 2 score is updated accordingly. But the user_id field becomes redundant.
Either 0 or NULL values are set for every user in design 2 which still isn't normalized.
What would be the optimal design and how important is normalization or key in an organization?
Edit:
For future people - I had some problems understanding what OP was asking for so read through the comments if you get a little lost. Ultimately, they were looking to store aggregate data and didn't know where to put it or how to make it happen. The solution is basically to use an insert trigger, which is explained near the end of this post.
I chose to just add another column on to the user table to store the accumulated sum of user_problem.score. However, making a new table (with the columns user_id and total_sum) isn't a bad option at all even though it seems to be an excessive use of normalization. Sometimes it is good to keep data that is constantly updated separate from data that is rarely changed. That way if something goes wrong, you know your static data will be safe.
Something else I never touched on are the data concurrency and integrity issues associated with storing aggregate data in general... so beware of that.
I would suggest something like this:
User Table
User_ID - Email - Name - Password - FB_ID
-- holds all the user information
Problem Table
Problem_ID - Problem_Title - Problem_Descr
-- holds all the info on the individual challenges/problems/whatever
User_Problem Table
User_Problem_ID - User_ID - Problem_ID - Score - Completion_Date
-- Joins the User and Problem tables and has information specific
-- to a user+challenge pair
And this assumes that a user can take many challenges/problems. And one problem/challenge can be taken by several users.
To see all the problems by a certain user, you would do something like:
select user.user_id,
user.name,
problem_title,
problem_descr,
user_problem.score,
user_problem.completed_date
from user
join user_problem on user.user_id = user_problem.user_id
join problem on user_problem.problem_id = problem.problem_id
where user.user_id = 123 or user.email = 'stuff#gmail.com'
The lengths for the varchar fields are fairly generic...
create table User(
User_ID int unsigned auto_increment primary key,
Email varchar(100),
Name varchar(100),
Password varchar(100),
FB_ID int
);
create table Problem (
Problem_ID int unsigned auto_increment primary key,
Problem_Title varchar(100),
Problem_Descr varchar(500)
);
create table User_Problem (
User_Problem_ID int unsigned auto_increment primary key,
User_ID int unsigned,
Problem_ID int unsigned,
Score int,
Completion_Date datetime,
foreign key (User_ID) references User (User_ID),
foreign key (Problem_ID) references Problem (Problem_ID)
);
After our conversation from down below in the comments... you would add a column to user:
User Table
User_ID - Email - Name - Password - FB_ID - Total_Score
I gave the column a default value of 0 because you seemed to want/need that if the person didn't have any associated problem/challenges. Depending on other things, it may benefit you to make this an unsigned int if you have a rule which states there will never be a negative score.
alter table user add column Total_Score int default 0;
then... you would use an insert trigger on the user_problem table that affects the user table.
CREATE TRIGGER tgr_update_total_score
AFTER INSERT ON User_Problem
FOR EACH ROW
UPDATE User
SET Total_score = Total_score + New.Score
WHERE User_ID = NEW.User_ID;
So... after a row is added to User_Problem, you would add the new score to user.total_score...
mysql> select * from user;
+---------+-------+------+----------+-------+-------------+
| User_ID | Email | Name | Password | FB_ID | Total_Score |
+---------+-------+------+----------+-------+-------------+
| 1 | NULL | kim | NULL | NULL | 0 |
| 2 | NULL | kyle | NULL | NULL | 0 |
+---------+-------+------+----------+-------+-------------+
2 rows in set (0.00 sec)
mysql> insert into user_problem values (null,1,1,10,now());
Query OK, 1 row affected (0.16 sec)
mysql> select * from user;
+---------+-------+------+----------+-------+-------------+
| User_ID | Email | Name | Password | FB_ID | Total_Score |
+---------+-------+------+----------+-------+-------------+
| 1 | NULL | kim | NULL | NULL | 10 |
| 2 | NULL | kyle | NULL | NULL | 0 |
+---------+-------+------+----------+-------+-------------+
2 rows in set (0.00 sec)
mysql> select * from user_problem;
+-----------------+---------+------------+-------+---------------------+
| User_Problem_ID | User_ID | Problem_ID | Score | Completion_Date |
+-----------------+---------+------------+-------+---------------------+
| 1 | 1 | 1 | 10 | 2013-11-03 11:31:53 |
+-----------------+---------+------------+-------+---------------------+
1 row in set (0.00 sec)

Mysql (php) update or replace shopping bag content in one query, with multiple values

I've got the flowing tables in mysql db for a shopping bag:
BAGS
-----
| bagID | date_added |
| primary Key | |
------------------------------
| 1 | 2012-01-04 |
BAGS_CONTENT
-----
| ID | productID | qyt |
| foreign key->bagID | | |
-----------------------------------------
| 1 | 103 | 4 |
// $sql Could contain this:
$sql = "(1,103,5),
(1,101,3)";
INSERT INTO BAGS_CONTENT
( ID, product_id, qty)
VALUES
".$sql."
I like the BAGS_CONTENT to update an existing record (if exists ID and product_id) and add a new row if not exists (the ID and product_id).
I've tried using REPLACE INTO and ON DUPLICATE KEY UPDATE but I can't get it to work.
May be its has something to do with the keys?
How should you query the db in a situation like this?
ON DUPLICATE KEY UPDATE triggers the UPDATE statement when the unique value is already existing in the table. Make sure that you've set the right fields to be unique. I think you have to put an UNIQUE on ID and productID (both in one combined unique):
ALTER TABLE BAGS_CONTENT ADD UNIQUE (ID, product_id)
Your query should look like this:
INSERT INTO BAGS_CONTENT (ID, product_id, qty) ".$sql." ON DUPLICATE KEY UPDATE qty = VALUES(qty);
Here's more information about 'on duplicate key':
http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
To use ON DUPLICATE KEY UPDATE you first need to create a unique key on BAGS_CONTENT(ID,productID)
Then use
INSERT INTO BAGS_CONTENT (ID,productID,qyt) VALUES(1,101,3) ON DUPLICATE KEY UPDATE qyt=VALUES(qyt);
You need to create a key error in order to trigger the special behavior of REPLACE. To get the proposed behavior add an index:
mysql> create table bag_content (id INT,productID INT,qty SMALLINT);
mysql> create unique index baggy on bag_content (id,productID);
mysql> replace bag_content values(1,111,5);
mysql> replace bag_content values(1,112,5);
mysql> replace bag_content values(1,111,500);
mysql> select * from bag_content;
+------+-----------+------+
| id | productID | qty |
+------+-----------+------+
| 1 | 111 | 500 |
| 1 | 112 | 5 |
+------+-----------+------+
Also watch out: you're using SQL supported in mysql only. The pejorative is 'their sql'... because of all the innovations of mysql that don't go through a standards process. The replace functionality is great, but it will increase the barrier to moving your code to other databases.