I want to find the optimal way to run this query, here the details, lets say the following are my tables with their data:
-- User Table
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
-- Entries
INSERT INTO `users` VALUES (1,'user_a');
INSERT INTO `users` VALUES (2,'user_b');
INSERT INTO `users` VALUES (3,'user_c');
-- User Log Table
DROP TABLE IF EXISTS `user_log`;
CREATE TABLE `user_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`idrel` int(11) NOT NULL,
`event` varchar(20) NOT NULL,
`log` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idrel` (`idrel`,`log`)
) ENGINE=MyISAM;
-- Entries
INSERT INTO `user_log` VALUES (1,1,'log1','2014-01-01 13:00:00');
INSERT INTO `user_log` VALUES (2,1,'log2','2014-01-02 13:00:00');
INSERT INTO `user_log` VALUES (3,2,'log3','2014-01-03 13:00:00');
INSERT INTO `user_log` VALUES (4,2,'log4','2014-01-04 13:00:00');
INSERT INTO `user_log` VALUES (5,3,'log5','2014-01-05 13:00:00');
INSERT INTO `user_log` VALUES (6,3,'log6','2014-01-06 13:00:00');
And I want to query the users joining the latest event logged, I have found two ways to do this but I don't know which one is the best (talking about speed when both tables grow) or if there is another way, here are my queries:
-- Query 1
SELECT
u.id,
u.name,
l2.event as last_record
FROM
users AS u
INNER JOIN
(
SELECT
idrel,
MAX(id) as last_id
FROM user_log
GROUP BY
idrel
) AS l1
ON (l1.idrel = u.id)
INNER JOIN
user_log AS l2
ON (l2.id = l1.last_id);
Which gives me this result:
+----+--------+-------------+
| id | name | last_record |
+----+--------+-------------+
| 1 | user_a | log2 |
| 2 | user_b | log4 |
| 3 | user_c | log6 |
+----+--------+-------------+
3 rows in set (0.00 sec)
-- Query 2
SELECT
u.id,
u.name,
(
SELECT event FROM user_log WHERE idrel = u.id ORDER BY log DESC LIMIT 1
) AS last_record
FROM
users AS u;
And the result:
+----+--------+-------------+
| id | name | last_record |
+----+--------+-------------+
| 1 | user_a | log2 |
| 2 | user_b | log4 |
| 3 | user_c | log6 |
+----+--------+-------------+
3 rows in set (0.00 sec)
Which one could be the best way? Is there any other better way?
Don’t do it that way, here is a better way, first, add a field to store on the users table the last ID of any new log entry, then create a trigger that updates that field on each new field inserted, then (optional) create a view to have a complete “table” to query from.
-- Add new field to save the modified id
ALTER TABLE `users` ADD `last_mod_id` INT(11) NOT NULL, ADD INDEX (`last_mod_id`);
-- Drop the trigger just in case it exists
DROP TRIGGER IF EXISTS `update_last_id`;
-- Create a trigger to save the last modified id each time a row is created
DELIMITER ;;
CREATE TRIGGER update_last_id
AFTER INSERT ON user_log
FOR EACH ROW
BEGIN
UPDATE `users`
SET `last_mod_id` = NEW.id
WHERE `id` = NEW.idrel;
END;
;;
-- Emtpy your table
TRUNCATE `user_log`;
-- Create all new registries
INSERT INTO `user_log` VALUES (1,1,'log1','2014-01-01 13:00:00');
INSERT INTO `user_log` VALUES (2,1,'log2','2014-01-02 13:00:00');
INSERT INTO `user_log` VALUES (3,2,'log3','2014-01-03 13:00:00');
INSERT INTO `user_log` VALUES (4,2,'log4','2014-01-04 13:00:00');
INSERT INTO `user_log` VALUES (5,3,'log5','2014-01-05 13:00:00');
INSERT INTO `user_log` VALUES (6,3,'log6','2014-01-06 13:00:00');
-- Query
SELECT
u.id,
u.name,
u.last_mod_id,
l.event,
l.log
FROM
`users` as u
INNER JOIN
`user_log` as l
ON (l.id = u.last_mod_id)
-- Drop view if exists
DROP VIEW IF EXISTS `users_log_view`;
-- View
CREATE VIEW `users_log_view` AS
SELECT
u.id,
u.name,
u.last_mod_id,
l.event,
l.log
FROM
`users` as u
INNER JOIN
`user_log` as l
ON (l.id = u.last_mod_id);
-- Query
SELECT * FROM `users_log_view` WHERE `id` = 1;
Related
This question already has answers here:
How can I do 'insert if not exists' in MySQL?
(11 answers)
Closed 2 years ago.
I am trying to execute the following query:
INSERT INTO table_listnames (name, address, tele)
VALUES ('Rupert', 'Somewhere', '022')
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name='value'
);
But this returns an error. Basically I don't want to insert a record if the 'name' field of the record already exists in another record - how to check if the new name is unique?
I'm not actually suggesting that you do this, as the UNIQUE index as suggested by Piskvor and others is a far better way to do it, but you can actually do what you were attempting:
CREATE TABLE `table_listnames` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`address` varchar(255) NOT NULL,
`tele` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Insert a record:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
+----+--------+-----------+------+
Try to insert the same record again:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
+----+--------+-----------+------+
Insert a different record:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'John'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
| 2 | John | Doe | 022 |
+----+--------+-----------+------+
And so on...
Update:
To prevent #1060 - Duplicate column name error in case two values may equal, you must name the columns of the inner SELECT:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Unknown' AS name, 'Unknown' AS address, '022' AS tele) AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+---------+-----------+------+
| id | name | address | tele |
+----+---------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
| 2 | John | Doe | 022 |
| 3 | Unknown | Unknown | 022 |
+----+---------+-----------+------+
INSERT doesn't allow WHERE in the syntax.
What you can do: create a UNIQUE INDEX on the field which should be unique (name), then use either:
normal INSERT (and handle the error if the name already exists)
INSERT IGNORE (which will fail silently cause a warning (instead of error) if name already exists)
INSERT ... ON DUPLICATE KEY UPDATE (which will execute the UPDATE at the end if name already exists, see documentation)
Worked :
INSERT INTO users (full_name, login, password)
SELECT 'Mahbub Tito','tito',SHA1('12345') FROM DUAL
WHERE NOT EXISTS
(SELECT login FROM users WHERE login='tito');
MySQL provides a very cute solution :
REPLACE INTO `table` VALUES (5, 'John', 'Doe', SHA1('password'));
Very easy to use since you have declared a unique primary key (here with value 5).
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;
Here the mysql query, that insert records if not exist and will ignore existing similar records.
----Untested----
You can easily use the following way :
INSERT INTO ... ON DUPLICATE KEY UPDATE ...
In this way, you can insert any new raw and if you have duplicate data, replace a specific column ( The best columns are timestamps ).
For example :
CREATE TABLE IF NOT EXISTS Devices (
id INT(6) NOT NULL AUTO_INCREMENT,
unique_id VARCHAR(100) NOT NULL PRIMARY KEY,
created_at VARCHAR(100) NOT NULL,
UNIQUE KEY unique_id (unique_id),
UNIQUE KEY id (id)
)
CHARACTER SET utf8
COLLATE utf8_unicode_ci;
INSERT INTO Devices(unique_id, time)
VALUES('$device_id', '$current_time')
ON DUPLICATE KEY UPDATE time = '$current_time';
To overcome a similar problem, I have modified the table to have a unique column. Using your example, on creation I would have something like:
name VARCHAR(20),
UNIQUE (name)
and then use the following query when inserting into it:
INSERT IGNORE INTO train
set table_listnames='Rupert'
If you really can't get a unique index on the table, you could try...
INSERT INTO table_listnames (name, address, tele)
SELECT 'Rupert', 'Somewhere', '022'
FROM some_other_table
WHERE NOT EXISTS (SELECT name
FROM table_listnames
WHERE name='Rupert')
LIMIT 1;
This query works well:
INSERT INTO `user` ( `username` , `password` )
SELECT * FROM (SELECT 'ersks', md5( 'Nepal' )) AS tmp
WHERE NOT EXISTS (SELECT `username` FROM `user` WHERE `username` = 'ersks'
AND `password` = md5( 'Nepal' )) LIMIT 1
And you can create the table using following query:
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` varchar(32) NOT NULL,
`status` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Note: Create table using second query before trying to use first query.
Brian Hooper :
You almost hit the point but you have an error in your synatx. Your insert will never work. I tested on my database and here is the right answer:
INSERT INTO podatki (datum,ura,meritev1,meritev1_sunki,impulzi1,meritev2,meritev2_sunki,impulzi2)
SELECT '$datum', '$ura', '$meritve1','$sunki1','$impulzi1','$meritve2','$sunki2','$impulzi2'
FROM dual
WHERE NOT EXISTS (SELECT datum,ura
FROM podatki
WHERE datum='$datum' and ura='$ura'
I'm giving you my example of y table. Insert is almost the same like Bian Hooper wrote, except that I put the select FROM DUAL ont from other table.
Cind regards, Ivan
This is not an answer, it's just a note. The query like the one in the accepted answer does not work if the inserted values are duplicates, like here:
INSERT INTO `addr` (`email`, `name`)
SELECT * FROM (SELECT 'user#domain.tld', 'user#domain.tld') AS tmp
WHERE NOT EXISTS (
SELECT `email` FROM `addr` WHERE `email` LIKE 'user#domain.tld'
);
Error
SQL query: Copy Documentation
MySQL said: Documentation
#1060 - Duplicate column name 'user#domain.tld'
In the contrary, the query like the one from Mahbub Tito's answer works fine:
INSERT INTO `addr` (`email`, `name`)
SELECT 'user#domain.tld', 'user#domain.tld'
WHERE NOT EXISTS (
SELECT `email` FROM `addr` WHERE `email` LIKE 'user#domain.tld'
);
1 row inserted.
Tested in MariaDB
insert into customer_keyskill(customerID, keySkillID)
select 2,1 from dual
where not exists (
select customerID from customer_keyskill
where customerID = 2
and keySkillID = 1 )
You are inserting not Updating the result.
You can define the name column in primary column or set it is unique.
I had a problem, and the method Mike advised worked partly, I had an error Dublicate Column name = '0', and changed the syntax of your query as this`
$tQ = "INSERT INTO names (name_id, surname_id, sum, sum2, sum3,sum4,sum5)
SELECT '$name', '$surname', '$sum', '$sum2', '$sum3','$sum4','$sum5'
FROM DUAL
WHERE NOT EXISTS (
SELECT sum FROM names WHERE name_id = '$name'
AND surname_id = '$surname') LIMIT 1;";
The problem was with column names. sum3 was equal to sum4 and mysql throwed dublicate column names, and I wrote the code in this syntax and it worked perfectly,
I had a similar problem and I needed to insert multiple if not existing. So from the examples above I came to this combination... it's here just in case somebody would need it.
Notice:
I had to define name everywhere as MSSQL required it... MySQL works with * too.
INSERT INTO names (name)
SELECT name
FROM
(
SELECT name
FROM
(
SELECT 'Test 4' as name
) AS tmp_single
WHERE NOT EXISTS
(
SELECT name FROM names WHERE name = 'Test 4'
)
UNION ALL
SELECT name
FROM
(
SELECT 'Test 5' as name
) AS tmp_single
WHERE NOT EXISTS
(
SELECT name FROM names WHERE name = 'Test 5'
)
) tmp_all;
MySQL:
CREATE TABLE names (
OID int(11) NOT NULL AUTO_INCREMENT,
name varchar(32) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (OID),
UNIQUE KEY name_UNIQUE (name)
) ENGINE=InnoDB AUTO_INCREMENT=1;
or
MSSQL:
CREATE TABLE [names] (
[OID] INT IDENTITY (1, 1) NOT NULL,
[name] NVARCHAR (32) NOT NULL,
PRIMARY KEY CLUSTERED ([OID] ASC)
);
CREATE UNIQUE NONCLUSTERED INDEX [Index_Names_Name] ON [names]([name] ASC);
This query can be used in PHP code.
I have an ID column in this table, so I need check for duplication for all columns except this ID column:
#need to change values
SET #goodsType = 1, #sybType=5, #deviceId = asdf12345SDFasdf2345;
INSERT INTO `devices` (`goodsTypeId`, `goodsId`, `deviceId`) #need to change tablename and columnsnames
SELECT * FROM (SELECT #goodsType, #sybType, #deviceId) AS tmp
WHERE NOT EXISTS (
SELECT 'goodsTypeId' FROM `devices` #need to change tablename and columns names
WHERE `goodsTypeId` = #goodsType
AND `goodsId` = #sybType
AND `deviceId` = #deviceId
) LIMIT 1;
and now new item will be added only in case of there is not exist row with values configured in SET string
I am returning a array of user created statuses in a feed. I have realized that looping through the mysql results in php post mysql to add on additional key/values ridiculously slows down our api response times so im trying to eliminate any foreach looping in php on this array of statuses.
here is my table setup:
USERS TABLE
UID / FIRST_NAME/ LAST_NAME/ECT
UPDATES TABLE
msg_id/message/ UID_FK
LIKED TABLE
LIKED_ID/ MSG_ID_FK/ LIKED_BY_UID
Is there a way that in mysql i can include a "isLiked" bool if the user making the api request has liked the update on each row returned in my query?
Here is my actual query im working with, it has a bit more data than i described in it but the goal is the same....find a way to add a 'isLiked' column to each row:
SELECT b.type,b.owner,b.update_img,b.ALBUM_ID,b.last_comment,a.uid, a.first_name, a.last_name, a.gender, a.thumb_img, b.msg_id, b.message, b.created,b.POST_PRIVACY
FROM users AS a, updates AS b, LIKED as c WHERE b.uid_fk = a.uid AND b.type<>'FRIEND_RELATIONSHIP'AND b.created<$time AND b.type<>'FAMILIAR_RELATIONSHIP' AND a.college='$college' AND b.POST_PRIVACY<>'4' AND b.POST_PRIVACY<>'5' AND b.created>=$tstamp ORDER BY b.created DESC
LIMIT 100
EDIT:
Here is a version of the query where I attempt to do what im aiming for ,but the problem is this ONLY returns the rows that I have liked:
SELECT b.type, b.owner, b.update_img, b.ALBUM_ID, b.last_comment, a.uid, a.first_name, a.last_name, a.gender, a.thumb_img, b.msg_id, b.message, b.created, b.POST_PRIVACY,c.LIKED
FROM users AS a, updates AS b, LIKES AS c
WHERE b.uid_fk = a.uid
AND b.POST_PRIVACY <> '4'
AND b.POST_PRIVACY <> '5'
AND c.LIKED_UID = '1'
AND b.msg_id = c.MSG_ID_FK
ORDER BY b.created DESC
LIMIT 100
If you are getting only the line with liked but want all the lines, try this:
SELECT b.type, b.owner, b.update_img, b.ALBUM_ID, b.last_comment, a.uid, a.first_name, a.last_name, a.gender, a.thumb_img, b.msg_id, b.message, b.created, b.POST_PRIVACY,c.LIKED
FROM users AS a, updates AS b, BUMPS AS c
WHERE b.uid_fk = a.uid
AND b.POST_PRIVACY <> '4'
AND b.POST_PRIVACY <> '5'
AND ((c.LIKED_UID = '1'
AND b.msg_id = c.MSG_ID_FK)
OR c.MSG_ID_FK IS NULL
)
ORDER BY b.created DESC
LIMIT 100
Added null test so that it is equivalent to a LEFT JOIN.
The column c.LIKED will be 1 when there is a liked on that message id and null when it doesn't.
-- EDIT --
As the above didn't work (may be wrong, I didn't test it), try this form of writing it (I like this way):
SELECT b.type, b.owner, b.update_img, b.ALBUM_ID, b.last_comment, a.uid, a.first_name, a.last_name, a.gender, a.thumb_img, b.msg_id, b.message, b.created, b.POST_PRIVACY,c.LIKED
FROM
users AS a
INNER JOIN updates AS b ON b.uid_fk = a.uid
LEFT JOIN BUMPS AS c ON b.msg_id = c.MSG_ID_FK
WHERE b.POST_PRIVACY <> '4'
AND b.POST_PRIVACY <> '5'
ORDER BY b.created DESC
LIMIT 100
Note that it will only select lines that are related on users and updates, and values from table BUMPS will be selected as NULL if no related line exists. This is what I recommend.
(deleted informations that could cause error and lead to confusion)
-- EDIT 2 added a test-case with simplified data and query --
I didn't see the entire problem before, then I did a test-case.
I created test tables and data:
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `test_multi_sets`()
DETERMINISTIC
begin
select user() as first_col;
select user() as first_col, now() as second_col;
select user() as first_col, now() as second_col, now() as third_col;
end$$
DELIMITER ;
CREATE TABLE IF NOT EXISTS `BUMPS` (
`id` int(11) NOT NULL,
`MSG_ID_FK` int(11) NOT NULL,
`LIKED_UID` int(11) NOT NULL,
`LIKED` tinyint(4) NOT NULL DEFAULT '0'
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
INSERT INTO `BUMPS` (`id`, `MSG_ID_FK`, `LIKED_UID`, `LIKED`) VALUES
(1, 1, 1, 1),
(2, 3, 2, 0),
(3, 1, 2, 0);
CREATE TABLE IF NOT EXISTS `updates` (
`msg_id` int(11) NOT NULL,
`uid_fk` int(11) NOT NULL,
`text` varchar(20) NOT NULL,
`POST_PRIVACY` tinyint(4) NOT NULL DEFAULT '0'
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
INSERT INTO `updates` (`msg_id`, `uid_fk`, `text`, `POST_PRIVACY`) VALUES
(1, 1, 't11', 3),
(2, 1, 't12', 3),
(3, 2, 't21', 3),
(4, 2, 't22', 3);
CREATE TABLE IF NOT EXISTS `users` (
`uid` int(11) NOT NULL,
`name` varchar(20) NOT NULL,
`pass` varchar(20) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
INSERT INTO `users` (`uid`, `name`, `pass`) VALUES
(1, 'u1', 'p1'),
(2, 'u2', 'p2');
ALTER TABLE `BUMPS`
ADD PRIMARY KEY (`id`), ADD KEY `MSG_ID_FK` (`MSG_ID_FK`), ADD KEY `LIKED_UID` (`LIKED_UID`);
ALTER TABLE `updates`
ADD PRIMARY KEY (`msg_id`), ADD KEY `uid_fk` (`uid_fk`);
ALTER TABLE `users`
ADD PRIMARY KEY (`uid`);
ALTER TABLE `BUMPS`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=4;
ALTER TABLE `updates`
MODIFY `msg_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=5;
ALTER TABLE `users`
MODIFY `uid` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3;
ALTER TABLE `BUMPS`
ADD CONSTRAINT `bumps_ibfk_1` FOREIGN KEY (`LIKED_UID`) REFERENCES `users` (`uid`),
ADD CONSTRAINT `bumps_ibfk_2` FOREIGN KEY (`MSG_ID_FK`) REFERENCES `updates` (`msg_id`);
ALTER TABLE `updates`
ADD CONSTRAINT `updates_ibfk_1` FOREIGN KEY (`uid_fk`) REFERENCES `users` (`uid`);
and run this select:
SELECT a.uid, a.name, b.msg_id, b.text, b.POST_PRIVACY, c.LIKED
FROM users AS a
INNER JOIN updates AS b ON b.uid_fk = a.uid
LEFT JOIN BUMPS AS c ON (c.LIKED_UID = 1 AND b.msg_id = c.MSG_ID_FK)
WHERE NOT (b.POST_PRIVACY IN(4, 5)) LIMIT 100
with these results:
| uid | name | msg_id | text | POST_PRIVACY | LIKED
| 1 | u1 | 1 | t11 | 3 | 1
| 1 | u1 | 2 | t12 | 3 | NULL
| 2 | u2 | 3 | t21 | 3 | NULL
| 2 | u2 | 4 | t22 | 3 | NULL
Even that user 2 liked updates, it won't show on user1 query.
I hope this helps you to solve your problem. I believe that I can't go any further than this, but anyway, send a message if needed.
Caution if using this on your production server, as first scrip on the EDIT 2 will create tables.
I have two tables as shown below.
table1
site | Link type
-----+----------
A | pdf
B | html
C | NULL
D | NULL
Table2
site | link type
-----+----------
C | htm
D | doc
This is the result I want:
site | link type
-----+----------
A | pdf
B | html
C | htm
D | doc
I want an insert query to insert the values of link type from table 2 to table 1
where link type is null joined with the condition with of table1.site = table2.site.
I tried:
INSERT INTO table1(linktype)
SELECT linktype FROM table1 t1
JOIN table2 t2
ON t1.site=t2.site
I want a insert query. as update query is working and want to know how a insert can be done?
Edit: Completely edited after clarification:
Creation of table1:
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `table1`;
CREATE TABLE `table1` (
`site` varchar(250) DEFAULT NULL,
`linktype` varchar(250) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `table1` VALUES ('A', 'pdf');
INSERT INTO `table1` VALUES ('B', 'html');
INSERT INTO `table1` VALUES ('C', null);
INSERT INTO `table1` VALUES ('D', null);
Creation of table2:
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `table2`;
CREATE TABLE `table2` (
`site` varchar(250) DEFAULT NULL,
`linktype` varchar(250) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `table2` VALUES ('C', 'htm');
INSERT INTO `table2` VALUES ('D', 'doc');
Insert query:
INSERT INTO
table1
(site, linktype)
(
SELECT
table2.site,
table2.linktype
FROM
table2
JOIN
table1
ON
table1.site = table2.site
)
;
Table1 after insert query:
I'm trying to select the most recent entries per group in a table.
Say I have a table "blog_posts" which has a column for "id" (all unique, auto incremented), "post_cat" which can be values 'category1' or 'category2' or 'category3', and a "publish_status" column which can be values 'online' or 'offline'.
How can I select the most recent entries for each category?
I have the following right now, but it almost feels like it's selecting randomly:
select * FROM `blog_posts` WHERE (publish_status = 'online') GROUP BY post_cat ORDER BY id DESC LIMIT 10
I'd keep it real simple and use a trigger to maintain a last_post_id in the category table so you can easily join back on the posts table - something like this:
Simple Query
select
pc.cat_id,
pc.name,
u.username,
bp.*
from
post_category pc
inner join blog_post bp on pc.last_post_id = bp.post_id
inner join users u on bp.user_id = u.user_id
order by
pc.cat_id;
+--------+------+----------+---------+---------+---------------------+
| cat_id | name | username | post_id | user_id | post_date |
+--------+------+----------+---------+---------+---------------------+
| 1 | cat1 | bar | 3 | 2 | 2011-02-09 12:45:33 |
| 2 | cat2 | BAR | 5 | 3 | 2011-02-09 12:45:33 |
| 3 | cat3 | f00 | 4 | 1 | 2011-02-09 12:45:33 |
+--------+------+----------+---------+---------+---------------------+
Tables
drop table if exists post_category;
create table post_category
(
cat_id smallint unsigned not null auto_increment primary key,
name varchar(255) unique not null,
last_post_id int unsigned null,
key (last_post_id)
)
engine=innodb;
drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
username varbinary(32) unique not null
)
engine=innodb;
drop table if exists blog_post;
create table blog_post
(
post_id int unsigned not null auto_increment primary key,
user_id int unsigned not null,
post_date datetime not null,
key (post_date, user_id)
)
engine=innodb;
drop table if exists blog_post_category;
create table blog_post_category
(
cat_id smallint unsigned not null,
post_id int unsigned not null,
primary key (cat_id, post_id)
)
engine=innodb;
Triggers
delimiter #
create trigger blog_post_before_ins_trig before insert on blog_post
for each row
begin
set new.post_date = now();
end#
create trigger blog_post_category_before_ins_trig before insert on blog_post_category
for each row
begin
update post_category set last_post_id = new.post_id where cat_id = new.cat_id;
end#
delimiter ;
Test Data
insert into post_category (name) values ('cat1'),('cat2'),('cat3'),('cat4');
insert into users (username) values ('f00'),('bar'),('BAR'),('alpha'),('beta');
insert into blog_post (user_id) values (1),(1),(2),(1),(3);
insert into blog_post_category (cat_id, post_id) values
(1,1),(1,3),
(2,1),(2,5),
(3,1),(3,3),(3,4);
Hope this helps :)
There's no really straightforward way to go about this...
http://www.artfulsoftware.com/infotree/queries.php#104
http://planet.mysql.com/entry/?id=26926
If you only have three categories, you can just make separate queries and take the union:
(SELECT * FROM `blog_posts` WHERE `publish_status` = 'online' AND `post_cat`='category1' ORDER BY `id` DESC LIMIT 10) UNION
(SELECT * FROM `blog_posts` WHERE `publish_status` = 'online' AND `post_cat`='category2' ORDER BY `id` DESC LIMIT 10) UNION
(SELECT * FROM `blog_posts` WHERE `publish_status` = 'online' AND `post_cat`='category3' ORDER BY `id` DESC LIMIT 10)
You could even re-sort the whole thing at the end. It's only 30 rows!
SELECT * FROM (
(SELECT * FROM `blog_posts` WHERE `publish_status` = 'online' AND `post_cat`='category1' ORDER BY `id` DESC LIMIT 10) UNION
(SELECT * FROM `blog_posts` WHERE `publish_status` = 'online' AND `post_cat`='category2' ORDER BY `id` DESC LIMIT 10) UNION
(SELECT * FROM `blog_posts` WHERE `publish_status` = 'online' AND `post_cat`='category3' ORDER BY `id` DESC LIMIT 10)
) `monster` ORDER BY `id` DESC
This question already has answers here:
How can I do 'insert if not exists' in MySQL?
(11 answers)
Closed 2 years ago.
I am trying to execute the following query:
INSERT INTO table_listnames (name, address, tele)
VALUES ('Rupert', 'Somewhere', '022')
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name='value'
);
But this returns an error. Basically I don't want to insert a record if the 'name' field of the record already exists in another record - how to check if the new name is unique?
I'm not actually suggesting that you do this, as the UNIQUE index as suggested by Piskvor and others is a far better way to do it, but you can actually do what you were attempting:
CREATE TABLE `table_listnames` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`address` varchar(255) NOT NULL,
`tele` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Insert a record:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
+----+--------+-----------+------+
Try to insert the same record again:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
+----+--------+-----------+------+
Insert a different record:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'John'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
| 2 | John | Doe | 022 |
+----+--------+-----------+------+
And so on...
Update:
To prevent #1060 - Duplicate column name error in case two values may equal, you must name the columns of the inner SELECT:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Unknown' AS name, 'Unknown' AS address, '022' AS tele) AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+---------+-----------+------+
| id | name | address | tele |
+----+---------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
| 2 | John | Doe | 022 |
| 3 | Unknown | Unknown | 022 |
+----+---------+-----------+------+
INSERT doesn't allow WHERE in the syntax.
What you can do: create a UNIQUE INDEX on the field which should be unique (name), then use either:
normal INSERT (and handle the error if the name already exists)
INSERT IGNORE (which will fail silently cause a warning (instead of error) if name already exists)
INSERT ... ON DUPLICATE KEY UPDATE (which will execute the UPDATE at the end if name already exists, see documentation)
Worked :
INSERT INTO users (full_name, login, password)
SELECT 'Mahbub Tito','tito',SHA1('12345') FROM DUAL
WHERE NOT EXISTS
(SELECT login FROM users WHERE login='tito');
MySQL provides a very cute solution :
REPLACE INTO `table` VALUES (5, 'John', 'Doe', SHA1('password'));
Very easy to use since you have declared a unique primary key (here with value 5).
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;
Here the mysql query, that insert records if not exist and will ignore existing similar records.
----Untested----
You can easily use the following way :
INSERT INTO ... ON DUPLICATE KEY UPDATE ...
In this way, you can insert any new raw and if you have duplicate data, replace a specific column ( The best columns are timestamps ).
For example :
CREATE TABLE IF NOT EXISTS Devices (
id INT(6) NOT NULL AUTO_INCREMENT,
unique_id VARCHAR(100) NOT NULL PRIMARY KEY,
created_at VARCHAR(100) NOT NULL,
UNIQUE KEY unique_id (unique_id),
UNIQUE KEY id (id)
)
CHARACTER SET utf8
COLLATE utf8_unicode_ci;
INSERT INTO Devices(unique_id, time)
VALUES('$device_id', '$current_time')
ON DUPLICATE KEY UPDATE time = '$current_time';
To overcome a similar problem, I have modified the table to have a unique column. Using your example, on creation I would have something like:
name VARCHAR(20),
UNIQUE (name)
and then use the following query when inserting into it:
INSERT IGNORE INTO train
set table_listnames='Rupert'
If you really can't get a unique index on the table, you could try...
INSERT INTO table_listnames (name, address, tele)
SELECT 'Rupert', 'Somewhere', '022'
FROM some_other_table
WHERE NOT EXISTS (SELECT name
FROM table_listnames
WHERE name='Rupert')
LIMIT 1;
This query works well:
INSERT INTO `user` ( `username` , `password` )
SELECT * FROM (SELECT 'ersks', md5( 'Nepal' )) AS tmp
WHERE NOT EXISTS (SELECT `username` FROM `user` WHERE `username` = 'ersks'
AND `password` = md5( 'Nepal' )) LIMIT 1
And you can create the table using following query:
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` varchar(32) NOT NULL,
`status` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Note: Create table using second query before trying to use first query.
Brian Hooper :
You almost hit the point but you have an error in your synatx. Your insert will never work. I tested on my database and here is the right answer:
INSERT INTO podatki (datum,ura,meritev1,meritev1_sunki,impulzi1,meritev2,meritev2_sunki,impulzi2)
SELECT '$datum', '$ura', '$meritve1','$sunki1','$impulzi1','$meritve2','$sunki2','$impulzi2'
FROM dual
WHERE NOT EXISTS (SELECT datum,ura
FROM podatki
WHERE datum='$datum' and ura='$ura'
I'm giving you my example of y table. Insert is almost the same like Bian Hooper wrote, except that I put the select FROM DUAL ont from other table.
Cind regards, Ivan
This is not an answer, it's just a note. The query like the one in the accepted answer does not work if the inserted values are duplicates, like here:
INSERT INTO `addr` (`email`, `name`)
SELECT * FROM (SELECT 'user#domain.tld', 'user#domain.tld') AS tmp
WHERE NOT EXISTS (
SELECT `email` FROM `addr` WHERE `email` LIKE 'user#domain.tld'
);
Error
SQL query: Copy Documentation
MySQL said: Documentation
#1060 - Duplicate column name 'user#domain.tld'
In the contrary, the query like the one from Mahbub Tito's answer works fine:
INSERT INTO `addr` (`email`, `name`)
SELECT 'user#domain.tld', 'user#domain.tld'
WHERE NOT EXISTS (
SELECT `email` FROM `addr` WHERE `email` LIKE 'user#domain.tld'
);
1 row inserted.
Tested in MariaDB
insert into customer_keyskill(customerID, keySkillID)
select 2,1 from dual
where not exists (
select customerID from customer_keyskill
where customerID = 2
and keySkillID = 1 )
You are inserting not Updating the result.
You can define the name column in primary column or set it is unique.
I had a problem, and the method Mike advised worked partly, I had an error Dublicate Column name = '0', and changed the syntax of your query as this`
$tQ = "INSERT INTO names (name_id, surname_id, sum, sum2, sum3,sum4,sum5)
SELECT '$name', '$surname', '$sum', '$sum2', '$sum3','$sum4','$sum5'
FROM DUAL
WHERE NOT EXISTS (
SELECT sum FROM names WHERE name_id = '$name'
AND surname_id = '$surname') LIMIT 1;";
The problem was with column names. sum3 was equal to sum4 and mysql throwed dublicate column names, and I wrote the code in this syntax and it worked perfectly,
I had a similar problem and I needed to insert multiple if not existing. So from the examples above I came to this combination... it's here just in case somebody would need it.
Notice:
I had to define name everywhere as MSSQL required it... MySQL works with * too.
INSERT INTO names (name)
SELECT name
FROM
(
SELECT name
FROM
(
SELECT 'Test 4' as name
) AS tmp_single
WHERE NOT EXISTS
(
SELECT name FROM names WHERE name = 'Test 4'
)
UNION ALL
SELECT name
FROM
(
SELECT 'Test 5' as name
) AS tmp_single
WHERE NOT EXISTS
(
SELECT name FROM names WHERE name = 'Test 5'
)
) tmp_all;
MySQL:
CREATE TABLE names (
OID int(11) NOT NULL AUTO_INCREMENT,
name varchar(32) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (OID),
UNIQUE KEY name_UNIQUE (name)
) ENGINE=InnoDB AUTO_INCREMENT=1;
or
MSSQL:
CREATE TABLE [names] (
[OID] INT IDENTITY (1, 1) NOT NULL,
[name] NVARCHAR (32) NOT NULL,
PRIMARY KEY CLUSTERED ([OID] ASC)
);
CREATE UNIQUE NONCLUSTERED INDEX [Index_Names_Name] ON [names]([name] ASC);
This query can be used in PHP code.
I have an ID column in this table, so I need check for duplication for all columns except this ID column:
#need to change values
SET #goodsType = 1, #sybType=5, #deviceId = asdf12345SDFasdf2345;
INSERT INTO `devices` (`goodsTypeId`, `goodsId`, `deviceId`) #need to change tablename and columnsnames
SELECT * FROM (SELECT #goodsType, #sybType, #deviceId) AS tmp
WHERE NOT EXISTS (
SELECT 'goodsTypeId' FROM `devices` #need to change tablename and columns names
WHERE `goodsTypeId` = #goodsType
AND `goodsId` = #sybType
AND `deviceId` = #deviceId
) LIMIT 1;
and now new item will be added only in case of there is not exist row with values configured in SET string