I have a table with columns id, user_id, post_id, like, views. When a user click the Like button, a row will be added to the table with the corresponding user_id and post_id, and like will be set to 1. If the row already exist and the like column already is 1, nothing will happen.
When the user visits a particular page, a row will be added to the table with the corresponding user_id and post_id, and view will be set to 1. However if the row already exist, the value in view will be incremented.
Problem: I tried using INSERT INTO mytable ... ON DUPLICATE KEY UPDATE like = 1, but the primary key of the table is id and not user_id or post_id. If I set the primary key to either user_id or post_id, there will be a problem because the row will only be duplicated if there exist a row with the same user_id and post_id.
In this situation, how should the query be built?
Or will a better situation be to split the table into 2 tables, one for likes and 1 for views. If this is the case, I still need to make the row unique based on both user_id and post_id columns.
Or do multiple SQL queries?
Please advise, thanks!
I would suggest that you have a table for likes, and then in whatever table that you keep posts in, you add a row for views. So whenever this post is viewed, you simply update the view value for that post to view = view + 1.
Now to address the problem that you outlined about the primary keys, you should note that you can use a unique key in that scenario, which can apply to multiple columns and would fix the issue that you ran into. So for example if in your scenario you had a unique key that encompassed both the userid and postid, your database would not allow for two different rows in that table with the same userid and postid.
So for future reference, use a unique key if you need to :)
discard the id as primary key
use two tables
make primary key on (user_id, post_id)
when handle insert like, do a insert ignore
when handle insert view, do a insert .. duplicate key update view = view+1
Related
I have a php script that logs inputs from a form into a mysql database table. I'm looking for a way to insert this data untill 3 rows are created, after which it has to update the existing rows so that the first one updates to the new input, the second one to the former first input and the third one to the former second input.
Table:
CREATE TABLE IF NOT EXISTS inputlog (
id int(11) NOT NULL auto_increment,
userid int(11) NOT NULL default '0',
name text,
value text,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;`
For the first three rows i use:
insert into inputlog (userid,name,value) values('$userid','$name','$value')
After that is has to become:
update inputlog set value = '$value' where userid = '$userid' and name = '$name'
where it has to update all the successive rows.
How can i accomplish this?
Too long for comments, so...
Looks like you want to have only 3 rows in your table because you want the data to be sorted by the id. So id=1 will be the latest value, then id=2 and finally id=3.
In short, do not do that, the id field can be any value. Do not code for that. The danger is if you use the id in another table as a foreign key, you will loose referential integrity. What I propose is:
Add an timestamp column for each row.
Every time you insert a new value, set the timestamp column to NOW()
When selecting, sort on the timestamp and limit to 3 results
If you MUST have only 3 rows, you can then delete the row except for the 3 most recent timestamps.
But... if you must do that...
perform a SELECT with the first 2 lines
truncate the table (delete all rows)
insert the new line, then the 2 stored lines
You will then ahve your 3 rows in the order you want. But without seeing the entire reasoning for your application, my "spider sense" tells me you will hit a wall later on...
And check the comments for other things to worry about.
I have a SQL query as follows-
"INSERT INTO users(id, rank) SELECT v.user, v.vote FROM votes v WHERE
v.assertion = '$ID' ON DUPLICATE KEY UPDATE
rank = ( CASE WHEN v.vote = '1' THEN rank+50 WHEN v.vote = '-1'
THEN rank-200 WHEN v.vote = '3' THEN rank+100 ELSE rank END)"
applied on a database with a table users with and id and rank field, and a votes table with a user and vote field. I have to update the rank of the users in the users table based on their vote.
I really like this kind of query, but I've noticed a problem: every time I execute this from my PHP script the query adds a row to the users table completely empty (with only an ID, which is A_I, and a rank of 1, when usually there would be other field as well). I can't really wrap my head around why this happens.
Any help/idea?
Your table does not have a primary key first provide a primary key to id
run this sql query
alter table user add primary key (id)
and than try it will work
There are two possible reasons :
The id column is not the primary key, and probably you table doesn't have a primary key at all.
Create a primary key like this :
alter table user add primary key (id)
If you insert an value of 0 in an auto increment column, a new id is generated. An auto incremented column must not contain the value 0.
There is also a more general problem with your approach : in fact you only insert the user id and the rank, other compulsory fields in the table (username) are missing. The insert part does not seem to be valid for this reason. If you use an insert on duplicate key update, you must make sure that the result is correct which ever of insert and update is executed.
I have two tables users, and posts. The posts table has a column with the foreign key user_id which corresponds to the id in the users table. If another row is inserted to the posts table and it has the same value in the user_id column as another row, I get an error:
"Duplicate entry '4' for key in 'user_id'"
How can I allow for duplicate entries in this user_id column?
I went into the relational view, edit keys, and changed it from unique, to index. Thanks to Cory.
I have a page views table where every time a page is viewed, the user's ip and page id are put in a table. In this table, I only want one row for each ip/page id combination. If I set primary key to ip, then each ip can only be associated with one page. If I set primary key to page id, then only one ip will be associated with each page.
I need both of these to be keys because it's the combination of ip and page id that can not be repeated in the table. How can I do this?
$mysqli->query("INSERT INTO page_views (ip, page_id, view_key) VALUES ('$ip', '$page_id', '$view_key')
ON DUPLICATE KEY UPDATE page_key='$view_key'");
This is how I need the setup to work - so every time someone views the page, they are assigned a view_key, and it is put in the table. If there is already a record of that ip viewing that page, then the view_key field should be overwritten.
If I understand correctly you need compound primary key:
CREATE TABLE(...,
PRIMARY KEY(page_id, ip));
Another option for you would be:
INSERT INTO your table (user_ip, page_id)
SELECT * FROM (SELECT inserted_user_ip, inserted_page_id) AS tmp
WHERE NOT EXISTS (
SELECT user_ip, page_id FROM your table WHERE user_id = inserted_user_id AND page_id = inserted_page_id
) LIMIT 1;
add a unique key to the table
ALTER TABLE table_name add unique key(ip_address,page_id);
In MySQL, is it possible to have a column in two different tables that auto-increment? Example: table1 has a column of 'secondaryid' and table2 also has a column of 'secondaryid'. Is it possible to have table1.secondaryid and table2.secondaryid hold the same information? Like table1.secondaryid could hold values 1, 2, 4, 6, 7, 8, etc and table2.secondaryid could hold values 3, 5, 9, 10? The reason for this is twofold: 1) the two tables will be referenced in a separate table of 'likes' (similar to users liking a page on facebook) and 2) the data in table2 is a subset of table1 using a primary key. So the information housed in table2 is dependent on table1 as they are the topics of different categories. (categories being table1 and topics being table2). Is it possible to do something described above or is there some other structural work around that im not aware of?
It seems you want to differentiate categories and topics in two separate tables, but have the ids of both of them be referenced in another table likes to facilitate users liking either a category or a topic.
What you can do is create a super-entity table with subtypes categories and topics. The auto-incremented key would be generated in the super-entity table and inserted into only one of the two subtype tables (based on whether it's a category or a topic).
The subtype tables reference this super-entity via the auto-incremented field in a 1:1 relationship.
This way, you can simply link the super-entity table to the likes table just based on one column (which can represent either a category or a topic), and no id in the subtype tables will be present in both.
Here is a simplified example of how you can model this out:
This model would allow you to maintain the relationship between categories and topics, but having both entities generalized in the superentity table.
Another advantage to this model is you can abstract out common fields in the subtype tables into the superentity table. Say for example that categories and topics both contained the fields title and url: you could put these fields in the superentity table because they are common attributes of its subtypes. Only put fields which are specific to the subtype tables IN the subtype tables.
If you just want the ID's in the two tables to be different you can initially set table2's AUTO_INCREMENT to some big number.
ALTER TABLE `table2` AUTO_INCREMENT=1000000000;
You can't have an auto_increment value shared between tables, but you can make it appear that it is:
set ##auto_increment_increment=2; // change autoinrement to increase by 2
create table evens (
id int auto_increment primary key
);
alter table evens auto_increment = 0;
create table odds (
id int auto_increment primary key
);
alter table odds auto_increment = 1;
The downside to this is that you're changing a global setting, so ALL auto_inc fields will now be growing by 2 instead of 1.
It sounds like you want a MySQL equivalent of sequences, which can be found in DBMS's like PosgreSQL. There are a few known recipes for this, most of which involve creating table(s) that track the name of the sequence and an integer field that keeps the current value. This approach allows you to query the table that contains the sequence and use that on one or more tables, if necessary.
There's a post here that has an interesting approach on this problem. I have also seen this approach used in the DB PEAR module that's now obsolete.
You need to set the other table's increment value manually either by the client or inside mysql via an sql function:
ALTER TABLE users AUTO_INCREMENT = 3
So after inserting into table1 you get back the last auto increment then modify the other table's auto increment field by that.
I'm confused by your question. If table 2 is a subset of table 3, why would you have it share the primary key values. Do you mean that the categories are split between table 2 and table 3?
If so, I would question the design choice of putting them into separate tables. It sounds like you have one of two different situations. The first is that you have a "category" entity that comes in two flavors. In this case, you should have a single category table, perhaps with a type column that specifies the type of category.
The second is that your users can "like" things that are different. In this case, the "user likes" table should have a separate foreign key for each object. You could pull off a trick using a composite foreign key, where you have the type of object and a regular numeric id afterwards. So, the like table would have "type" and "id". The person table would have a column filled with "PERSON" and another with the numeric id. And the join would say "on a.type = b.type and a.id = b.id". (Or the part on the "type" could be implicit, in the choice of the table).
You could do it with triggers:
-- see http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id
CREATE TABLE sequence (id INT NOT NULL);
INSERT INTO sequence VALUES (0);
CREATE TABLE table1 (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
secondardid INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
CREATE TABLE table2 (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
secondardid INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
DROP TRIGGER IF EXISTS table1_before_insert;
DROP TRIGGER IF EXISTS table2_before_insert;
DELIMITER //
CREATE
TRIGGER table1_before_insert
BEFORE INSERT ON
table1
FOR EACH ROW
BEGIN
UPDATE sequence SET id=LAST_INSERT_ID(id+1);
NEW.secondardid = LAST_INSERT_ID();
END;
//
CREATE
TRIGGER table2_before_insert
BEFORE INSERT ON
table2
FOR EACH ROW
BEGIN
UPDATE sequence SET id=LAST_INSERT_ID(id+1);
NEW.secondardid = LAST_INSERT_ID();
END;
//