Mysql complex select statement - mysql

I have a case which seems very complex to me and I am currently doing it in 3 queries and with lots of application logic. I thought, it may be possible with only one single queries.
I have a three table,
1. conversations table
2. chats table
3. users table
Please see the two pictures so that you can see the entities with the actual data.
conversations table
chats table
Please look at the conversation table. Here I want to get all the rows where sender=1 or reciever=1 so expected results will be
id 3,4,5. Now, I also return the user details from these selected rows.
Here is the most difficult part for me. I don't want to retrieve users details of id 1 instead I want users details whose id are 2,3 and 4 notice 4 in sender column and 2 3 in reciever column
Now, you know the selected rows from conversations table. In second table, con_id is the foreign key of conversation table so I want to retrieve the last rows of each con_id. In the second picture, you can see id 2,3,4,5,6 has con_id = 3 but since I want the last one so it should select where id = 6 similarly all last row of each con_id
I am sorry for this long case, hope you got me and the problem.
Thanks in advance.
EDIT
Here is the sql tables and dummy data for you so that if you want you can test paste quickly
Expected results
id || sender || reciver || id(users id) || userName || id(chats id) || con_id || msg || msg_sender
3 1 2 2 iamsadek2 6 3 ... 2
4 1 3 3 sadek3 10 4 ... 3
5 4 1 4 adek4. 14. 5. ... 4
DROP TABLE IF EXISTS `chats`;
CREATE TABLE `chats` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`msg_sender` int(10) unsigned DEFAULT NULL,
`con_id` int(10) unsigned DEFAULT NULL,
`msg` text,
`file` varchar(255) DEFAULT NULL,
`deleted` int(10) unsigned DEFAULT NULL,
`seen` tinyint(1) DEFAULT '0',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `con_id` (`con_id`),
KEY `msg_sender` (`msg_sender`),
CONSTRAINT `chats_ibfk_1` FOREIGN KEY (`con_id`) REFERENCES `conversations` (`id`) ON DELETE CASCADE,
CONSTRAINT `chats_ibfk_2` FOREIGN KEY (`msg_sender`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `chats` WRITE;
/*!40000 ALTER TABLE `chats` DISABLE KEYS */;
INSERT INTO `chats` (`id`, `msg_sender`, `con_id`, `msg`, `file`, `deleted`, `seen`, `created_at`, `updated_at`)
VALUES
(2,1,3,'id 1 sending msg to id 2',NULL,NULL,0,'2018-06-26 19:36:06',NULL),
(4,2,3,'id 2 sending msg to id 1 second msg',NULL,NULL,0,'2018-06-26 19:36:37',NULL),
(5,1,3,'id 1 sending msg to id 2 msg 3',NULL,NULL,0,'2018-06-26 19:36:42',NULL),
(6,2,3,'id 2 sending msg to id 1 msg 4',NULL,NULL,0,'2018-06-26 19:36:46',NULL),
(7,1,4,'id 1 sending msg to id 3 msg 1',NULL,NULL,0,'2018-06-26 19:36:49',NULL),
(8,3,4,'id 3 sending msg to id 1 msg 2am',NULL,NULL,0,'2018-06-26 19:39:44',NULL),
(9,3,4,'id 3 sending msg to id 1 msg 3',NULL,NULL,0,'2018-06-26 19:39:55',NULL),
(10,3,4,'id 3 sending msg to id 1 msg 4',NULL,NULL,0,'2018-06-26 19:39:57',NULL),
(11,4,5,'id 4 sending msg to id 1 msg 1',NULL,NULL,0,'2018-06-26 19:40:46',NULL),
(12,4,5,'id 4 sending msg to id 1 msg 1',NULL,NULL,0,'2018-06-26 19:40:48',NULL),
(13,4,5,'id 4 sending msg to id 1 msg 3',NULL,NULL,0,'2018-06-26 19:40:50',NULL),
(14,1,5,'id 1 sending msg to id 4 msg 4',NULL,NULL,0,'2018-06-26 19:41:01',NULL),
(15,4,11,'id 4 sending msg to id 3 msg 1',NULL,NULL,0,'2018-06-26 19:42:37',NULL),
(16,3,11,'id 3 sending msg to id 4 msg 2',NULL,NULL,0,'2018-06-26 19:42:57',NULL),
(17,3,11,'id 3 sending msg to id 4 msg 2',NULL,NULL,0,'2018-06-26 19:42:59',NULL);
/*!40000 ALTER TABLE `chats` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table conversations
# ------------------------------------------------------------
DROP TABLE IF EXISTS `conversations`;
CREATE TABLE `conversations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`sender` int(10) unsigned DEFAULT NULL,
`reciever` int(10) unsigned DEFAULT NULL,
`status` tinyint(1) DEFAULT '0',
`type` tinyint(1) DEFAULT '0',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `sender` (`sender`),
KEY `reciever` (`reciever`),
CONSTRAINT `conversations_ibfk_1` FOREIGN KEY (`sender`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `conversations_ibfk_2` FOREIGN KEY (`reciever`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `conversations_reciever_foreign` FOREIGN KEY (`reciever`) REFERENCES `users` (`id`),
CONSTRAINT `conversations_sender_foreign` FOREIGN KEY (`sender`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `conversations` WRITE;
/*!40000 ALTER TABLE `conversations` DISABLE KEYS */;
INSERT INTO `conversations` (`id`, `sender`, `reciever`, `status`, `type`, `created_at`, `updated_at`)
VALUES
(3,1,2,0,0,'2018-06-26 19:32:35',NULL),
(4,1,3,0,0,'2018-06-26 19:32:50',NULL),
(5,4,1,0,0,'2018-06-26 19:33:11',NULL),
(6,2,3,0,0,'2018-06-26 19:33:22',NULL),
(11,3,4,0,0,'2018-06-26 19:33:22',NULL);
/*!40000 ALTER TABLE `conversations` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table users
# ------------------------------------------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`firstName` varchar(255) NOT NULL,
`lastName` varchar(255) NOT NULL,
`profilePic` varchar(255) DEFAULT 'user.png',
`address` varchar(255) DEFAULT NULL,
`lat` varchar(255) DEFAULT NULL,
`lang` varchar(255) DEFAULT NULL,
`ip` varchar(255) DEFAULT NULL,
`password` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`userName` varchar(255) NOT NULL,
`gender` varchar(11) DEFAULT NULL,
`userType` varchar(255) DEFAULT 'User',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`status` varchar(11) DEFAULT 'active',
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`),
UNIQUE KEY `users_username_unique` (`userName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
INSERT INTO `users` (`id`, `firstName`, `lastName`, `profilePic`, `address`, `lat`, `lang`, `ip`, `password`, `email`, `userName`, `gender`, `userType`, `created_at`, `updated_at`, `status`)
VALUES
(1,'sadek','hossain','user.png',NULL,NULL,NULL,NULL,'$2a$10$ayAJE7BxkgqXPGjVxeD8iu0GVWL6CXJFdLhGcHAN.i39lCqcAR5nS','sadek.hkm#gmail.com','iamsadek','male','User','2018-06-26 18:33:16','2018-06-26 18:33:16','active'),
(2,'sadek2','hossain2','user.png',NULL,NULL,NULL,NULL,'$2a$10$q.LNN48POO9g1INdEC/iTO1CJjGXNBLYZPbHkyRe.oHaZJi9b8GWe','sadek2.hkm#gmail.com','iamsadek2','male','User','2018-06-26 18:42:53','2018-06-26 18:42:53','active'),
(3,'sadek3','hossain3','user.png',NULL,NULL,NULL,NULL,'$2a$10$7xgKQDUw/tA6f8zb0uqSN.z7RnNuUVAoMuB6Eknm/cqzXk7BDcmIi','sadek3#gmail.com','sadek3','male','User','2018-06-26 19:02:30','2018-06-26 19:02:30','active'),
(4,'sadek3','hossain3','user.png',NULL,NULL,NULL,NULL,'$2a$10$7xgKQDUw/tA6f8zb0uqSN.z7RnNuUVAoMuB6Eknm/cqzXk7BDcmIi','sadek4#gmail.com','sadek4','male','User','2018-06-26 19:02:30','2018-06-26 19:02:30','active'),
(5,'sadek3','hossain3','user.png',NULL,NULL,NULL,NULL,'$2a$10$7xgKQDUw/tA6f8zb0uqSN.z7RnNuUVAoMuB6Eknm/cqzXk7BDcmIi','sadek5#gmail.com','sadek5','male','User','2018-06-26 19:02:30','2018-06-26 19:02:30','active'),
(6,'sadek3','hossain3','user.png',NULL,NULL,NULL,NULL,'$2a$10$7xgKQDUw/tA6f8zb0uqSN.z7RnNuUVAoMuB6Eknm/cqzXk7BDcmIi','sadek6#gmail.com','sadek6','male','User','2018-06-26 19:02:30','2018-06-26 19:02:30','active');
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
UNLOCK TABLES;

It can basically be done with some joins. The tricky part is the last message for chats. The subquery groups the chats by the con_id to get the maximum id. As id is AUTO_INCREMENT, the maximum id for a con_id is the last message of the conversation.
SELECT co1.id,
co1.sender,
co1.reciever,
us.id "id(users id)",
us.username,
ch1.id "id(chats id)",
ch1.con_id,
ch1.msg,
ch1.msg_sender
FROM conversations co1
INNER JOIN users us
ON CASE
WHEN co1.sender <> 1
THEN co1.sender
WHEN co1.reciever <> 1
THEN co1.reciever
END = us.id
INNER JOIN chats ch1
ON ch1.con_id = co1.id
INNER JOIN (SELECT max(ch2.id) id,
ch2.con_id
FROM chats ch2
GROUP BY ch2.con_id) ch3
ON ch3.con_id = ch1.con_id
AND ch3.id = ch1.id
WHERE 1 IN (co1.sender,
co1.reciever);
SQL Fiddle
Instead of an INNER JOIN with the subquery with the GROUP BY, a correlated subyquery would be another option.
SELECT co1.id,
co1.sender,
co1.reciever,
us.id "id(users id)",
us.username,
ch1.id "id(chats id)",
ch1.con_id,
ch1.msg,
ch1.msg_sender
FROM conversations co1
INNER JOIN users us
ON CASE
WHEN co1.sender <> 1
THEN co1.sender
WHEN co1.reciever <> 1
THEN co1.reciever
END = us.id
INNER JOIN chats ch1
ON ch1.con_id = co1.id
WHERE 1 IN (co1.sender,
co1.reciever)
AND ch1.id = (SELECT max(ch2.id)
FROM chats ch2
WHERE ch2.con_id = co1.id);
SQL Fiddle

TRy it now
SELECT id.conversations, sender.conversations, receiver.conversations, id.users, userName.users, id.chats, con_id.chats, msg.chats, msg_sender.chats
FROM conversations, chats, users
WHERE (sender.conversations OR receiver.conversations = 1) AND id.conversations=firstname.users
GROUP BY id.conversations

Related

Auto increment Id value increased by 1 after insert trigger

I have 3 tables as below:
CREATE TABLE `user_dummy` (
`user_id` INT(11) NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR(50) NOT NULL,
`user_email` VARCHAR(100) NOT NULL,
PRIMARY KEY (`user_id`)
)
ENGINE=InnoDB
;
CREATE TABLE `user_role` (
`user_role_id` INT(11) NOT NULL AUTO_INCREMENT,
`user_role_name` VARCHAR(255) NULL DEFAULT NULL,
PRIMARY KEY (`user_role_id`)
)
ENGINE=InnoDB
;
CREATE TABLE `user` (
`user_seq` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) NOT NULL,
`user_name` VARCHAR(50) NOT NULL,
`user_email` VARCHAR(100) NOT NULL,
`user_role_id` INT(11) NOT NULL,
PRIMARY KEY (`user_seq`),
INDEX `FKh2wc2dtfdo8maylne7mgubowq` (`user_role_id`),
CONSTRAINT `FKh2wc2dtfdo8maylne7mgubowq` FOREIGN KEY (`user_role_id`) REFERENCES `user_role` (`user_role_id`) ON UPDATE CASCADE ON DELETE CASCADE
)
ENGINE=InnoDB
;
I have created after insert trigger on user table.
i.e., when I insert 1 record into user_dummy table, it will insert records into table user table with all mappings of user_role.
trigger:
CREATE TRIGGER `user_dummy_after_insert` AFTER INSERT ON `user_dummy` FOR EACH ROW BEGIN
INSERT INTO user(user_id, user_name, user_email, user_role_id)
SELECT NEW.user_id, NEW.user_name, NEW.user_email, user_role_id
FROM user_role;
END
Above trigger is able to insert records into user table but the auto_increment value is incremented by 1 after each user_role record.
If you observe user_seq 3 is missing. And after inserting 4 records, auto_increment value set by trigger as 7.
How to fix this ?
Just an alternative: Better you could use the count function to count the total existing records and then increase it by one and assign according as. If you are interested to preserve the insertion sequence.

mysql foreign key insert

Using this two tables i'm trying to create a query to insert a new course, but i'm using a foreign key and I don't know how to pass that users(table) id into courses(table) id.
I'm trying to get something like this
I'm completely lost, on how to insert data into a table that contains a foreign key,
what i'm trying to do is:
first a person can register(user table)
2.Then the user will be available to add courses ( im using the foreign key to identify the courses for a specific user)
Users table
id | username |
---------------
1 | test |
Courses table
coursesid | coursesname | id(same id that in users table)
1 | courses test| 1
The Create Table commands are
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`password` char(64) COLLATE utf8_unicode_ci NOT NULL,
`salt` char(16) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
CREATE TABLE `course` (
`courseid` int(11) NOT NULL AUTO_INCREMENT,
`coursename` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`id` int(11) NOT NULL,
UNIQUE KEY (`courseid`)
FOREIGN KEY (od) REFERENCES users(id)
)
You can use subquery for find user id, e.g.:
insert into course
(courseid, coursename, id)
values
(1, 'test courses',
(SELECT id FROM users where username = 'test')
)
;
Of cause, if you know user id, you can insert directly this id:
insert into course
(courseid, coursename, id)
values
(1, 'test courses', 1)
;
Additional query for insert just last user id:
insert into course
(courseid, coursename, id)
values
(1, 'test courses',
(SELECT max(id) FROM users)
)
;

BLOB data returned in MySQL using AES_DECRYPT with ORDER clause

I'm creating a system in which users can store messages via PHP with a MySQL database, and I am using the MySQL AES_ENCRYPT function to encrypt the contents of these messages.
Here is my posts table:
CREATE TABLE IF NOT EXISTS `posts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` int(11) DEFAULT NULL,
`group` int(11) DEFAULT NULL,
`body` varbinary(1000) NOT NULL,
`ip` varchar(45) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`replyto` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `replyto` (`replyto`),
KEY `user` (`user`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
ALTER TABLE `posts`
ADD CONSTRAINT `posts_ibfk_3` FOREIGN KEY (`replyto`) REFERENCES `posts` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
ADD CONSTRAINT `posts_ibfk_4` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;
And my users table:
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ip` varchar(45) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`email` varchar(100) NOT NULL,
`name` varchar(100) NOT NULL,
`hash` varchar(128) NOT NULL,
`salt` varchar(32) NOT NULL,
`guid` varchar(36) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
The AES encryption key I'm using for the message body is a SHA-512-hashed concatenation between a prefix and suffix string, and the posting user's GUID in the middle. Thus, I have the following SQL query to select the most recent messages:
SELECT AES_DECRYPT(`posts`.`body`, SHA2(CONCAT('prefix',(`users`.`guid`),'suffix'),512)) AS 'realbody'
FROM `posts`
INNER JOIN `users` ON `posts`.`user` = `users`.`id`
ORDER BY `posts`.`id` DESC
Unfortunately, this does not return the decrypted messages, as you can see in the screenshot:
Instead, I'm getting this BLOB data. However, if I remove the ORDER BY clause from the query:
SELECT AES_DECRYPT(`posts`.`body`, SHA2(CONCAT('prefix',(`users`.`guid`),'suffix'),512)) AS 'realbody'
FROM `posts`
INNER JOIN `users` ON `posts`.`user` = `users`.`id`
Then suddenly, it works:
I really don't know what could be causing this. Does anybody have any ideas?
UPDATED CAST it to CHAR
SELECT `posts`.*, CAST(AES_DECRYPT(`posts`.`body`,SHA2(CONCAT('prefix',`users`.`guid`,'suffix'),512)) AS CHAR) as 'realbody'
FROM `posts` JOIN `users`
ON `posts`.`user` = `users`.`id`
ORDER BY `posts`.`id` DESC
Sample output:
| ID | USER | ... | REALBODY |
---...------------------------
| 2 | 2 | ... | Post 2 |
| 1 | 1 | ... | Post 1 |
Here is SQLFiddle demo

SQL Statement to INSERT into one table from two (using id from another w/no join criteria)

I have two tables:
CREATE TABLE `data_items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`data` longtext NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
`data_item_type` varchar(255) NOT NULL,
`data_source_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_AFA125D21A935C57` (`data_source_id`),
CONSTRAINT `FK_AFA125D21A935C57` FOREIGN KEY (`data_source_id`) REFERENCES `data_sources` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7192 DEFAULT CHARSET=utf8;
AND
CREATE TABLE `map_locations` (
`id` int(11) NOT NULL,
`latitude` decimal(9,6) DEFAULT NULL,
`longitude` decimal(9,6) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
`status` varchar(255) NOT NULL,
`message_codes` longtext NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `FK_B28E0DE7BF396750` FOREIGN KEY (`id`) REFERENCES `data_items` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I'm trying to accomplish a two-step process. The first query works fine:
INSERT INTO data_items (`data`, created, updated, data_item_type, data_source_id) SELECT data_items.data, now(), now(), data_items.data_item_type, 2
FROM data_items
WHERE data_items.data_source_id = 1
--> 1 and 2 are example values
Now, I want to take every id from 'data_items' (the auto-incs that just got created) and use those id's when inserting each new (corresponding row) in the 'map_locations' table.
So,
data_items
id | data_source_id
1 | 1
2 | 1
3 | 2
4 | 2
map_locations
id | content
1 | aaa
2 | bbb
....
3 | aaa
4 | bbb
I need to copy row (1, 2), use the id from data_items (3,4) and insert those rows.
Hope this makes sense..it's rather confusing.
TIA.
You need the newly created data_items to reference their original counterparts; the easiest way to do this would be to modify the schema so that the table includes a self-referencing foreign key:
ALTER TABLE data_items
ADD COLUMN underlying_id INT(11) NULL,
ADD FOREIGN KEY (underlying_id) REFERENCES data_items (id)
The insertion into data_items then becomes:
INSERT INTO data_items
(underlying_id, data, created, updated, data_item_type, data_source_id)
SELECT id, data, NOW(), NOW(), data_item_type, 2
FROM data_items
WHERE data_source_id = 1
One can now perform a self-join to obtain the newly created ids:
INSERT INTO map_locations
(id, content)
SELECT new.id, map_locations.content
FROM map_locations
JOIN data_items old USING (id)
JOIN data_items new ON new.underlying_id = old.id
WHERE old.data_source_id = 1
Ensure that you perform this query within the same transaction as the previous insertion, using the (default) REPEATABLE READ transaction isolation level, to ensure that any concurrent changes to the data_items table don't lead to inconsistent state.
Again, within the same transaction, clear the underlying_id reference:
UPDATE data_items SET underlying_id = NULL

How to automate insert id into child table with new created id from parent

How to make as default insert with id_user into child user_id? I will make at once update without extra select and insert in my program. Is this possible?
CREATE TABLE IF NOT EXISTS `users` (
`id_user` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(25) NOT NULL DEFAULT '',
PRIMARY KEY (`id_user`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `scores` (
`user_id` INT( 10 ) NOT NULL DEFAULT '0',
`score` INT( 10 ) NOT NULL DEFAULT '0',
PRIMARY KEY (`user_id`),
CONSTRAINT FOREIGN KEY ( `user_id` ) REFERENCES `users` ( `id_user` )
ON UPDATE CASCADE
) ENGINE = INNODB;
INSERT INTO users (name) VALUES ('i am');
UPDATE scores SET score = '10' WHERE user_id = '1';
Not sure I understand the example. If you just inserted user number 1, how come there is a record in scores with user_id = 1?.
Anyway, If your question is how to get the id of the user you just inserted into users so you can insert a child record into scores, you can run
SELECT LAST_INSERT_ID();
after the first insert, and use the result for the second insert (/update).