What is the best table structure to store dialogs between users in private messages?
Each user can send personal message to many recepients.
Each message has flag for sender: is message deleted or not
Each message has flag for receiver: is message unread, read or deleted
Each message can be deleted (set flag 'deleted')
PrivateMessages' main page should look like this:
E.g. User1 sends Message1 to User2 and User3.
On private message page I have to show 2 same messages:
sent Message1 to user2
sent Message1 to user3
next step - User2 replies to Message2, I'll see on the same page following:
received Message2 from user2 (reply on Message1)
sent Message1 to user3
next step, I answer to message3, I'll see
sent Message3 to user2
sent Message1 to user3
and so on.
Can anyone provide a table-structure?
I'm using MySQL 5.5
Main question. How can I get only the last non-deleted message of each dialog?
UPD.
I need to see on main page dialog list, between current user and other users (with pagination, sorted by Date DESC).
I will answer your main question first, then show the table structure I will use for this.
To get only the last non-deleted message of a particular dialog:
select
Message.Id
,Message.Subject
,Message.Content
from Message
join Junc_Message_To on Fk_Message = Message.Id
where Junc_Message_To.Fk_User = {RECIPIENT_ID}
and Message.Fk_User__From = {SENDER_ID}
and Junc_Message_To.Deleted is null
order by Junc_Message_To.Sent desc
limit 1
A simple three table structure could be used.
Table 1 stores user records - one record per user.
Table 2 stores message record - one record per message, foreign key relates to the user that sent the message.
Table 3 stores the correlation between messages and users that have had the messages sent to them.
Here is the SQL that is used to create the above table diagram:
create table `User` (
`Id` int not null auto_increment ,
`Username` varchar(32) not null ,
`Password` varchar(32) not null ,
primary key (`Id`) ,
unique index `Username_UNIQUE` (`Username` ASC) )
engine = InnoDB
create table `Message` (
`Id` int not null auto_increment ,
`Fk_User__From` int not null ,
`Subject` varchar(256) not null ,
`Content` text not null ,
primary key (`Id`) ,
index `Fk_Message_User__From` (`Fk_User__From` ASC) ,
constraint `Fk_Message_User__From`
foreign key (`Fk_User__From` )
references `User` (`Id` )
on delete cascade
on update cascade)
engine = InnoDB
create table `Junc_Message_To` (
`Fk_Message` int not null ,
`Fk_User` int not null ,
`Sent` datetime not null ,
`Read` datetime not null ,
`Deleted` datetime not null ,
PRIMARY KEY (`Fk_Message`, `Fk_User`) ,
INDEX `Fk_Junc_Message_To__Message` (`Fk_Message` ASC) ,
INDEX `Fk_Junc_Message_To__User` (`Fk_User` ASC) ,
constraint `Fk_Junc_Message_To__Message`
foreign key (`Fk_Message` )
references `Message` (`Id` )
on delete cascade
on update cascade,
constraint `Fk_Junc_Message_To__User`
foreign key (`Fk_User` )
references `User` (`Id` )
on delete cascade
on update cascade)
engine = InnoDB
I've done this in the past with a MessageRecipient table that simply contains the MessageID, ReceiverID, and Status. I had FolderID in that table as well, but you don't have that requirement. The Message table did not store any information about the recipient at all.
It is a join to retrieve a users messages, but does prevent duplication of the message subject and body between recipients.
Here's my approach at this, based on the information you provided.
User table is a give in. Mine is just id and name.
We obviously need a table to store messages. We need to know who authored it, the subject, the message content, and (probably) when it was created/sent.
We need to know who the message_recipients are. Technically even the message.author is sent a copy of the message (in most cases), but it is usually put in a folder='Sent'. Everyone else probably got it in their folder="Inbox". User's could then move the message to their folder='Trash' or delete it completely. If for some reason you need to retain messages after the user has deleted them, you could do so by making a folder='Deleted' with a folder.type='System'. If not, just delete the record in the message_recipients table for that message_recipient.user.
So here is the info for that. See the test cases for querying after the schema and data.
Schema:
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` tinytext NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
CREATE TABLE `message` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`author` int(11) unsigned NOT NULL,
`subject` varchar(255) NOT NULL,
`message` mediumtext NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_m_author` (`author`),
CONSTRAINT `fk_m_author` FOREIGN KEY (`author`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `message_folder_type`;
CREATE TABLE `message_folder_type` (
`name` varchar(40) NOT NULL,
`type` enum('System','User') NOT NULL DEFAULT 'User',
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `message_recipient`;
CREATE TABLE `message_recipient` (
`message` int(11) unsigned NOT NULL,
`user` int(11) unsigned NOT NULL,
`folder` varchar(40) NOT NULL,
PRIMARY KEY (`message`,`user`),
KEY `fk_mr_user` (`user`),
KEY `fk_mr_message_folder` (`folder`),
CONSTRAINT `fk_mr_message_folder` FOREIGN KEY (`folder`) REFERENCES `message_folder_type` (`name`) ON UPDATE CASCADE,
CONSTRAINT `fk_mr_message` FOREIGN KEY (`message`) REFERENCES `message` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_mr_user` FOREIGN KEY (`user`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Test data:
INSERT INTO `user` VALUES ('1', 'Bob');
INSERT INTO `user` VALUES ('2', 'Harry');
INSERT INTO `user` VALUES ('3', 'Salley');
INSERT INTO `user` VALUES ('4', 'Jim');
INSERT INTO `user` VALUES ('5', 'Jake');
INSERT INTO `user` VALUES ('6', 'Randall');
INSERT INTO `user` VALUES ('7', 'Ashley');
INSERT INTO `message` VALUES ('1', '4', 'Message 1', 'this is a message', '2011-03-01 15:47:07');
INSERT INTO `message` VALUES ('2', '2', 'Message 2', 'this is a reply to message 1', '2011-03-02 15:47:28');
INSERT INTO `message` VALUES ('3', '7', 'Message 3', 'another cool message', '2011-03-02 15:48:15');
INSERT INTO `message` VALUES ('4', '4', 'Message 4', 'blah blah blah Sally', '2011-03-09 15:48:43');
INSERT INTO `message_folder_type` VALUES ('Deleted', 'System');
INSERT INTO `message_folder_type` VALUES ('Inbox', 'User');
INSERT INTO `message_folder_type` VALUES ('Sent', 'User');
INSERT INTO `message_folder_type` VALUES ('Trash', 'User');
INSERT INTO `message_recipient` VALUES ('1', '1', 'Inbox');
INSERT INTO `message_recipient` VALUES ('1', '2', 'Inbox');
INSERT INTO `message_recipient` VALUES ('2', '4', 'Inbox');
INSERT INTO `message_recipient` VALUES ('2', '5', 'Inbox');
INSERT INTO `message_recipient` VALUES ('3', '5', 'Inbox');
INSERT INTO `message_recipient` VALUES ('1', '4', 'Sent');
INSERT INTO `message_recipient` VALUES ('2', '2', 'Sent');
INSERT INTO `message_recipient` VALUES ('3', '7', 'Sent');
INSERT INTO `message_recipient` VALUES ('4', '4', 'Sent');
INSERT INTO `message_recipient` VALUES ('1', '3', 'Trash');
INSERT INTO `message_recipient` VALUES ('4', '3', 'Trash');
Test Case: Get the last, non-deleted, message of each dialog
I'm not completely sure what this means, but I'll assume "in a given user's inbox" and "not in the System Deleted folder" as part of my query.
SELECT message.`subject`, message.message, message.`author`
FROM message_recipient
INNER JOIN message ON message.id = message_recipient.message
WHERE
message_recipient.user = 4
AND message_recipient.folder != 'Deleted'
ORDER BY message.created DESC
This gives, based on the test data provided, the following results:
Subject Message Author
Message 4 blah blah blah Sally 4
Message 2 this is a reply to message 1 2
Message 1 this is a message 4
If I was an architector of the DB, I'd make structure like this (approx.)
CREATE TABLE statuses(
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
description VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE INDEX name (name)
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;
CREATE TABLE users(
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (id),
UNIQUE INDEX name (name)
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;
CREATE TABLE messages(
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
reply_to INT(11) UNSIGNED NOT NULL,
sender INT(11) UNSIGNED NOT NULL,
recipient INT(11) UNSIGNED NOT NULL,
subject VARCHAR(255) DEFAULT NULL,
message TEXT DEFAULT NULL,
`time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
INDEX FK_messages_messages_id (reply_to),
INDEX FK_messages_users_id_recipient (recipient),
INDEX FK_messages_users_id_sender (sender),
CONSTRAINT FK_messages_messages_id FOREIGN KEY (reply_to)
REFERENCES messages (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FK_messages_users_id_recipient FOREIGN KEY (recipient)
REFERENCES users (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT FK_messages_users_id_sender FOREIGN KEY (sender)
REFERENCES users (id) ON DELETE NO ACTION ON UPDATE NO ACTION
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;
CREATE TABLE messages_statuses(
message_id INT(11) UNSIGNED NOT NULL,
status_id INT(11) UNSIGNED NOT NULL,
PRIMARY KEY (message_id, status_id),
INDEX FK_messages_statuses_statuses_id (status_id),
CONSTRAINT FK_messages_statuses_messages_id FOREIGN KEY (message_id)
REFERENCES messages (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT FK_messages_statuses_statuses_id FOREIGN KEY (status_id)
REFERENCES statuses (id) ON DELETE CASCADE ON UPDATE CASCADE
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;
I don't see anything hard here but if you'll got any questions - feel free to ask.
id* INT, sender_id INT, recipient_id INT, message TEXT,
flag_s_deleted = 0 TINYINT, flag_r_deleted = 0 TINYINT, flag_r_read = 0 TINYINT,
sent_datetime DATETIME
"How can I get only the last
non-deleted message of each dialog?"
here you are:
select * from (...) where
(sender_id = ID1 and recipient_id = ID2 and flag_s_deleted = 0)
or (sender_id = ID2 and recipient_id = ID1 and flag_r_deleted = 0)
order by sent_date desc LIMIT 1
last message between you (ID1) and other person (ID2)
create database testMessage
go
use testMessage
go
CREATE TABLE [user] (
userid int NOT NULL IDENTITY,
name nvarchar(200) NOT NULL,
PRIMARY KEY (userid)
)
go
CREATE TABLE [message] (
msg_id int NOT NULL IDENTITY,
userid int NOT NULL,
msgContent nvarchar(200) NOT NULL,
created datetime NOT NULL default getdate(),
PRIMARY KEY (msg_id)
)
go
ALTER TABLE [message]
ADD FOREIGN KEY (userid) REFERENCES [user](userid)
ON DELETE CASCADE
ON UPDATE CASCADE
go
CREATE TABLE message_folder_type (
message_folder_type_name varchar(40) NOT NULL,
[type] varchar(10) NOT NULL DEFAULT 'User',
PRIMARY KEY (message_folder_type_name)
)
go
CREATE TABLE message_recipient (
message_recipient int NOT NULL,
userid int NOT NULL,
message_folder_type_name varchar(40) NOT NULL,
PRIMARY KEY (message_recipient,userid)
)
go
ALTER TABLE message_recipient
ADD FOREIGN KEY (message_folder_type_name) REFERENCES message_folder_type(message_folder_type_name)
ON DELETE CASCADE
ON UPDATE CASCADE
ALTER TABLE message_recipient
ADD FOREIGN KEY (message_recipient) REFERENCES [message](msg_id)
ON DELETE CASCADE
ON UPDATE CASCADE
ALTER TABLE message_recipient
ADD FOREIGN KEY (userid) REFERENCES [user](userid)
INSERT INTO [user] VALUES ('Bob');
INSERT INTO [user] VALUES ('Harry');
INSERT INTO [user] VALUES ('Salley');
INSERT INTO [user] VALUES ('Jim');
INSERT INTO [user] VALUES ('Jake');
INSERT INTO [user] VALUES ('Randall');
INSERT INTO [user] VALUES ('Ashley');
INSERT INTO [message] VALUES ('4', 'this is a message', '2011-03-01 15:47:07');
INSERT INTO [message] VALUES ('2', 'this is a reply to message 1', '2011-03-02 15:47:28');
INSERT INTO [message] VALUES ('7', 'another cool message', '2011-03-02 15:48:15');
INSERT INTO [message] VALUES ('4', 'blah blah blah Sally', '2011-03-09 15:48:43');
INSERT INTO message_folder_type VALUES ('Deleted', 'System');
INSERT INTO message_folder_type VALUES ('Inbox', 'User');
INSERT INTO message_folder_type VALUES ('Sent', 'User');
INSERT INTO message_folder_type VALUES ('Trash', 'User');
INSERT INTO message_recipient VALUES ('1', '1', 'Inbox');
INSERT INTO message_recipient VALUES ('1', '2', 'Inbox');
INSERT INTO message_recipient VALUES ('2', '4', 'Inbox');
INSERT INTO message_recipient VALUES ('2', '5', 'Inbox');
INSERT INTO message_recipient VALUES ('3', '5', 'Inbox');
INSERT INTO message_recipient VALUES ('1', '4', 'Sent');
INSERT INTO message_recipient VALUES ('2', '2', 'Sent');
INSERT INTO message_recipient VALUES ('3', '7', 'Sent');
INSERT INTO message_recipient VALUES ('4', '4', 'Sent');
INSERT INTO message_recipient VALUES ('1', '3', 'Trash');
INSERT INTO message_recipient VALUES ('4', '3', 'Trash');
SELECT [message].msg_id, [message].msgContent
FROM message_recipient
INNER JOIN message ON [message].msg_id = message_recipient.message_recipient
WHERE
message_recipient.userid = 4
AND message_recipient.message_folder_type_name != 'Deleted'
ORDER BY message.created DESC
fast action for sqlserver
Related
I have two tables:
--
--
--
CREATE TABLE `staff` (
PRIMARY KEY (`staff_id`),
`staff_id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`staff_type` ENUM('expert', # Perito
'injured' # Danneggiato
) NOT NULL
)ENGINE=InnoDB;
INSERT INTO `x8k1y_staff`
(`staff_id`, `name`, `staff_type`)
VALUES
(1, 'Name surname', 'expert'),
(2, 'John Doe', 'injured');
--
--
--
CREATE TABLE `risk_location` (
PRIMARY KEY (`risk_loc_id`),
`risk_loc_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`injured` SMALLINT UNSIGNED NOT NULL,
CONSTRAINT `fk_injured`
FOREIGN KEY (`injured`)
REFERENCES `staff` (`staff_id`)
ON DELETE CASCADE,
)ENGINE=InnoDB;
But I want to raise an MySQL Error if a user tries to insert a staff_id that doesn't have a value of "injured" in staff_type.
For example:
INSERT INTO risk_location (`injured`)
VALUES (1); # <--- Here I want an error, because the `staff_type` of id 1 is `expert`
INSERT INTO risk_location (`injured`)
VALUES (2); # <--- This is ok
Thank you
You can't do that with a constarint
but you can use a trigger
eventually you need a DELIMITER before and after, depending how you add a trigger
CREATE TABLE `staff` (
PRIMARY KEY (`staff_id`),
`staff_id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`staff_type` ENUM('expert', # Perito
'injured' # Danneggiato
) NOT NULL
)ENGINE=InnoDB;
INSERT INTO `staff`
(`staff_id`, `name`, `staff_type`)
VALUES
(1, 'Name surname', 'expert'),
(2, 'John Doe', 'injured');
CREATE TABLE `risk_location` (
PRIMARY KEY (`risk_loc_id`),
`risk_loc_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`injured` SMALLINT UNSIGNED NOT NULL,
CONSTRAINT `fk_injured`
FOREIGN KEY (`injured`)
REFERENCES `staff` (`staff_id`)
ON DELETE CASCADE
)ENGINE=InnoDB;
CREATE TRIGGER before_risk_location_insert
BEFORE INSERT
ON risk_location FOR EACH ROW
BEGIN
DECLARE _staff_type varchar(10);
SELECT staff_type
INTO _staff_type
FROM staff
WHERE staff_id = NEW.injured;
IF _staff_type = 'expert' THEN
signal sqlstate '45000' set message_text = 'user is expert';
END IF;
END
INSERT INTO risk_location (`injured`)
VALUES (1); # <--- Here I want an error, because the `staff_type` of id 1 is `expert`
user is expert
INSERT INTO risk_location (`injured`)
VALUES (2); # <--- This is ok
db<>fiddle here
Basically I am trying to build a 'Search by Attribute' function, so a user can choose a field or combination of fields in an EAV database structure e.g.:
CREATE TABLE `form` (
`formID` int(11) NOT NULL AUTO_INCREMENT,
`formName` varchar(255) NOT NULL,
PRIMARY KEY (`formID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `form` (`formID`, `formName`) VALUES
(1, 'A Form');
CREATE TABLE `formFields` (
`fieldID` int(11) NOT NULL AUTO_INCREMENT,
`formID` int(11) NOT NULL,
`fieldName` varchar(255) NOT NULL,
PRIMARY KEY (`fieldID`),
KEY `formID` (`formID`),
CONSTRAINT `formFields_ibfk_1` FOREIGN KEY (`formID`) REFERENCES `form` (`formID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `formFields` (`fieldID`, `formID`, `fieldName`) VALUES
(1, 1, 'Fruit'),
(2, 1, 'Car Make'),
(3, 1, 'Colour');
CREATE TABLE `item` (
`itemID` int(11) NOT NULL AUTO_INCREMENT,
`formID` int(11) NOT NULL,
`notes` varchar(1000) DEFAULT NULL,
PRIMARY KEY (`itemID`),
KEY `formID` (`formID`),
CONSTRAINT `item_ibfk_1` FOREIGN KEY (`formID`) REFERENCES `form` (`formID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `item` (`itemID`, `formID`, `notes`) VALUES
(1, 1, 'Some notes....'),
(2, 1, 'don\'t find this one');
CREATE TABLE `itemDetails` (
`itemID` int(11) NOT NULL,
`fieldID` int(11) NOT NULL,
`itemValue` varchar(1000) NOT NULL,
UNIQUE KEY `itemID_fieldID` (`itemID`,`fieldID`),
KEY `fieldID` (`fieldID`),
CONSTRAINT `itemDetails_ibfk_1` FOREIGN KEY (`itemID`) REFERENCES `item` (`itemID`),
CONSTRAINT `itemDetails_ibfk_2` FOREIGN KEY (`fieldID`) REFERENCES `formFields` (`fieldID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `itemDetails` (`itemID`, `fieldID`, `itemValue`) VALUES
(1, 1, 'apple'),
(1, 2, 'ford'),
(1, 3, 'orange'),
(2, 1, 'banana'),
(2, 2, 'toyota'),
(2, 3, 'blue');
CREATE OR REPLACE VIEW item_viewDetails AS
SELECT ff.formID, ff.fieldID, id.itemID, ff.fieldName, id.itemValue
FROM formFields ff
LEFT JOIN itemDetails id ON ff.fieldID = id.fieldID;
I have a view set up to return the itemID, formID, fieldID, fieldName and value. each item will have a minimum number of rows based on how many fields are in the form (the value is null if no itemDetails record exists)
currently I have a select statement that can search for 1 field eg: `WHERE view.fieldID = X AND view.value = Y
But I would also like to be able to search for multiple fields at the same time in an AND fashion. but of course i cannot do WHERE (view.fieldID = X AND view.value = Y) AND (view.fieldID = A AND view.value = B) as the details are accross multiple rows with a shared itemID. Is there any way to do this in mysql? or do I have to just OR it and then do checking on the application side to discard invalid results?
I have come up with a solution that does work, but I don't know if it is the best as it involves a sub-query with a view.
SELECT * FROM (
SELECT
COUNT(i.itemID) AS matches,
ia.*
FROM item_viewDetails ia
INNER JOIN item i ON i.itemID = ia.itemID
WHERE (ia.fieldID = 2 AND ia.itemValue = 'toyota') OR (ia.fieldID = 3 AND ia.itemValue = 'blue')
GROUP BY ia.itemID) AS m
WHERE m.matches = 2;
then as I generate the query, I just have to count the number of fields I am trying to match.
As I can get records from multiple tables omitting duplicate, for example I have this table "code attached tables":
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for interests
-- ----------------------------
DROP TABLE IF EXISTS `interests`;
CREATE TABLE `interests` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of interests
-- ----------------------------
INSERT INTO `interests` VALUES ('1', 'Sport');
INSERT INTO `interests` VALUES ('2', 'Technology');
INSERT INTO `interests` VALUES ('3', 'Games');
INSERT INTO `interests` VALUES ('4', 'Security');
INSERT INTO `interests` VALUES ('5', 'Movies');
-- ----------------------------
-- Table structure for interests_has_user
-- ----------------------------
DROP TABLE IF EXISTS `interests_has_user`;
CREATE TABLE `interests_has_user` (
`interests_id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`interests_id`,`user_id`),
KEY `fk_interests_has_user_user1_idx` (`user_id`),
KEY `fk_interests_has_user_interests_idx` (`interests_id`),
CONSTRAINT `fk_interests_has_user_interests` FOREIGN KEY (`interests_id`) REFERENCES `interests` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_interests_has_user_user1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of interests_has_user
-- ----------------------------
INSERT INTO `interests_has_user` VALUES ('1', '1');
INSERT INTO `interests_has_user` VALUES ('2', '1');
INSERT INTO `interests_has_user` VALUES ('3', '1');
INSERT INTO `interests_has_user` VALUES ('4', '1');
INSERT INTO `interests_has_user` VALUES ('5', '1');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
`email` varchar(45) DEFAULT NULL,
`password` varchar(45) DEFAULT NULL,
`country` varchar(45) DEFAULT NULL,
`state` varchar(45) DEFAULT NULL,
`city` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'Name', 'email#email.com', '123123', '1', '1', '1');
I need to get records from the table[ user ] and the table[ interests_has_user ], where a user can have many interests at the table table[ interests_has_user ], I'm doing the query this way:
SELECT
user.id,
user.name,
user.email,
user.country,
user.state,
user.city,
interests_has_user.interests_id,
interests_has_user.user_id
FROM
user
LEFT JOIN interests_has_user
ON user.id = interests_has_user.user_id
WHERE user.id = 1;
And I throw all records of the table table[ interests_has_user ], but in all rows the user is repeated, image attached with the result.
Note: What is shaded in yellow should be empty or null fields.
What the best solution for this, use INNER JOIN, or separate consutas.
I appreciate your help, thank you very much.
INNER JOIN will not return users without interests. So it is not what you want.
If you want to lighten-up the query results you could do two queries:
First find out the user details
SELECT
user.id,
user.name,
user.email,
user.country,
user.state,
user.city
FROM
user
WHERE user.id = 1
Then the interests ids.
SELECT user.id, interests_has_user.interests_id, interests_has_user.user_id
FROM user
LEFT JOIN interests_has_user ON user.id = interests_has_user.user_id
WHERE user.id = 1
Dim datetimepicker1 As String = Format(System.DateTime.Now, "yyyy-MM-dd HH:mm:ss")
Try
Dim cmd As MySqlCommand = New MySqlCommand
With cmd
.CommandText = "INSERT INTO tbl_product (`prod_name`,`prod_desc`, `cat_id`, `uom_id`,`uom_num`, `dept_id`, `brand_id`, `size_id`, `type_id`, `remarks`, `date`) values (#prod_name,#prod_desc,#cat_id,#uom_id,#uom_num,#dept_id,#brand_id,#size_id,#type_id,#remarks,#date)"
.Connection = SQLConnection
.CommandType = CommandType.Text
.Parameters.AddWithValue("#prod_name", TextBox1.Text)
.Parameters.AddWithValue("#prod_desc", TextBox2.Text)
.Parameters.AddWithValue("#cat_id", ComboBox1.Text)
.Parameters.AddWithValue("#uom_id", ComboBox2.Text)
.Parameters.AddWithValue("#uom_num", TextBox3.Text)
.Parameters.AddWithValue("#dept_id", ComboBox3.Text)
.Parameters.AddWithValue("#brand_id", ComboBox4.Text)
.Parameters.AddWithValue("#size_id", ComboBox5.Text)
.Parameters.AddWithValue("#type_id", ComboBox6.Text)
.Parameters.AddWithValue("#remarks", RichTextBox1.Text)
.Parameters.AddWithValue("#date", datetimepicker1)
.ExecuteNonQuery()
End With
MsgBox(" SIze Successfully added")
Catch ex As Exception
MsgBox(ex.Message.ToString)
End Try
cannot add or update child row foreign key constraint fails this is my error
this is my table structure*************8
--
-- Table structure for table tbl_brand
CREATE TABLE IF NOT EXISTS tbl_brand (
brand_id int(11) NOT NULL AUTO_INCREMENT,
brand_name varchar(200) NOT NULL,
brand_desc varchar(200) DEFAULT NULL,
PRIMARY KEY (brand_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
--
-- Dumping data for table tbl_brand
INSERT INTO tbl_brand (brand_id, brand_name, brand_desc) VALUES
(1, 'Nike ', 'Nike Air '),
(2, 'Crocs ', 'Class A '),
(3, 'SafeGuard ', 'SafeGuard ');
--
-- Table structure for table tbl_category
CREATE TABLE IF NOT EXISTS tbl_category (
cat_id int(11) NOT NULL AUTO_INCREMENT,
cat_name varchar(200) NOT NULL,
cat_desc varchar(200) NOT NULL,
PRIMARY KEY (cat_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table tbl_category
INSERT INTO tbl_category (cat_id, cat_name, cat_desc) VALUES
(1, 'Bath Soap ', 'Bath Soap '),
(2, 'Detergent ', 'Detergent ');
--
-- Table structure for table tbl_dept
CREATE TABLE IF NOT EXISTS tbl_dept (
dept_id int(11) NOT NULL AUTO_INCREMENT,
dept_name varchar(200) NOT NULL,
dept_desc varchar(200) NOT NULL,
PRIMARY KEY (dept_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table tbl_dept
INSERT INTO tbl_dept (dept_id, dept_name, dept_desc) VALUES
(1, 'Shoes ', 'Shoes '),
(2, 'Soap ', 'Soap ');
--
-- Table structure for table tbl_product
CREATE TABLE IF NOT EXISTS tbl_product (
prod_id int(11) NOT NULL AUTO_INCREMENT,
prod_name varchar(200) NOT NULL,
prod_desc varchar(200) DEFAULT NULL,
cat_id int(11) NOT NULL,
dept_id int(11) NOT NULL,
brand_id int(11) NOT NULL,
type_id int(11) NOT NULL,
uom_id int(11) NOT NULL,
size_id int(11) NOT NULL,
date datetime NOT NULL,
remarks varchar(200) DEFAULT NULL,
uom_num int(60) DEFAULT NULL,
PRIMARY KEY (prod_id),
KEY tbl_product_ibfk_9 (type_id),
KEY tbl_product_ibfk_10 (uom_id),
KEY tbl_product_ibfk_11 (size_id),
KEY tbl_product_ibfk_12 (dept_id),
KEY tbl_product_ibfk_13 (cat_id),
KEY tbl_product_ibfk_14 (brand_id)
); ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Table structure for table tbl_size
CREATE TABLE IF NOT EXISTS tbl_size (
size_id int(11) NOT NULL AUTO_INCREMENT,
size_name varchar(100) NOT NULL,
PRIMARY KEY (size_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
--
-- Dumping data for table tbl_size
INSERT INTO tbl_size (size_id, size_name) VALUES
(1, 'Small(S) '),
(2, 'Medium(M) '),
(3, 'Large(L) ');
--
-- Table structure for table tbl_type
CREATE TABLE IF NOT EXISTS tbl_type (
type_id int(11) NOT NULL AUTO_INCREMENT,
type_name varchar(200) NOT NULL,
PRIMARY KEY (type_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
--
-- Dumping data for table tbl_type
INSERT INTO tbl_type (type_id, type_name) VALUES
(1, 'BaskertBall Shoes '),
(2, 'Jersey Shorts '),
(3, 'running shoes '),
(8, 'Bath Soap ');
--
-- Table structure for table tbl_uom
CREATE TABLE IF NOT EXISTS tbl_uom (
uom_id int(11) NOT NULL AUTO_INCREMENT,
uom_name varchar(200) NOT NULL,
PRIMARY KEY (uom_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
--
-- Dumping data for table tbl_uom
INSERT INTO tbl_uom (uom_id, uom_name) VALUES
(1, 'kilogram(kg) '),
(2, 'Gram(g) '),
(3, 'Milligram(Mg) '),
(4, 'Liter(L) '),
(5, 'Milliliters(ml) '),
(6, 'Pieces(pcs) '),
(7, 'foot(ft) ');
--
-- Table structure for table tbl_user
CREATE TABLE IF NOT EXISTS tbl_user (
user_id int(11) NOT NULL AUTO_INCREMENT,
user_code varchar(200) DEFAULT NULL,
user_password varchar(200) DEFAULT NULL,
user_name varchar(200) DEFAULT NULL,
user_level int(1) DEFAULT NULL,
datetime datetime NOT NULL,
com_code varchar(11) NOT NULL,
PRIMARY KEY (user_id),
UNIQUE KEY user_code (user_code)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
--
-- Dumping data for table tbl_user
INSERT INTO tbl_user (user_id, user_code, user_password, user_name, user_level, datetime, com_code) VALUES
(1, '1024', '1024', 'Vincent Dematera', 3, '2012-01-10 18:18:33', '001'),
(7, '14 ', '14', 'Megan Bueno', 1, '2012-10-30 21:56:14', '002'),
(8, '13', '13', 'Anonymous', 1, '2012-10-20 21:51:00', '002'),
(9, '9', '9', 'boom', 1, '0000-00-00 00:00:00', '003');
--
-- Constraints for dumped tables
--
-- Constraints for table tbl_product
ALTER TABLE tbl_product
ADD CONSTRAINT tbl_product_ibfk_9 FOREIGN KEY (type_id) REFERENCES tbl_type (type_id),
ADD CONSTRAINT tbl_product_ibfk_10 FOREIGN KEY (uom_id) REFERENCES tbl_uom (uom_id),
ADD CONSTRAINT tbl_product_ibfk_11 FOREIGN KEY (size_id) REFERENCES tbl_size (size_id),
ADD CONSTRAINT tbl_product_ibfk_12 FOREIGN KEY (dept_id) REFERENCES tbl_dept (dept_id),
ADD CONSTRAINT tbl_product_ibfk_13 FOREIGN KEY (cat_id) REFERENCES tbl_category (cat_id),
ADD CONSTRAINT tbl_product_ibfk_14 FOREIGN KEY (brand_id) REFERENCES tbl_brand (brand_id),
ADD CONSTRAINT tbl_product_ibfk_2 FOREIGN KEY (type_id) REFERENCES tbl_type (type_id),
ADD CONSTRAINT tbl_product_ibfk_3 FOREIGN KEY (uom_id) REFERENCES tbl_uom (uom_id),
ADD CONSTRAINT tbl_product_ibfk_4 FOREIGN KEY (size_id) REFERENCES tbl_size (size_id),
ADD CONSTRAINT tbl_product_ibfk_5 FOREIGN KEY (dept_id) REFERENCES tbl_dept (dept_id),
ADD CONSTRAINT tbl_product_ibfk_6 FOREIGN KEY (cat_id) REFERENCES tbl_category (cat_id),
ADD CONSTRAINT tbl_product_ibfk_7 FOREIGN KEY (brand_id) REFERENCES tbl_brand (brand_id);
SET FOREIGN_KEY_CHECKS=1;
You have a significant number of foreign key constraints for your table product. For example, any "size_id" you enter into table products HAS to correspond to a "size_id" in table size.
What your vb.net program is telling you is that one of the id values you are entering into table product does not exist in its corresponding table. My best guess would be that one of your id values (ie. size_id, uom_id, etc) is empty, blank, or zero.
You have a number of options - you can:
turn foreign key off for the table if you wish with SET foreign_key_checks = 0;
you can remove the foreign keys altogether
you can fix your code to ensure you are adhering to the foreign keys before you even attempt to insert a value
First execute these tables and data dumps :-
CREATE TABLE IF NOT EXISTS `Tags` (
`id_tag` int(10) unsigned NOT NULL auto_increment,
`tag` varchar(255) default NULL,
PRIMARY KEY (`id_tag`),
UNIQUE KEY `tag` (`tag`),
KEY `id_tag` (`id_tag`),
KEY `tag_2` (`tag`),
KEY `tag_3` (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=18 ;
INSERT INTO `Tags` (`id_tag`, `tag`) VALUES
(1, 'key1'),
(2, 'key2');
CREATE TABLE IF NOT EXISTS `Tutors_Tag_Relations` (
`id_tag` int(10) unsigned NOT NULL default '0',
`id_tutor` int(10) default NULL,
KEY `Tutors_Tag_Relations` (`id_tag`),
KEY `id_tutor` (`id_tutor`),
KEY `id_tag` (`id_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `Tutors_Tag_Relations` (`id_tag`, `id_tutor`) VALUES
(1, 1),
(2, 1);
The following query finds all the tutors from Tutors_Tag_Relations table which have reference to at least one of the terms "key1" or "key2".
SELECT td . *
FROM Tutors_Tag_Relations AS td
INNER JOIN Tags AS t ON t.id_tag = td.id_tag
WHERE t.tag LIKE "%key1%"
OR t.tag LIKE "%key2%"
Group by td.id_tutor
LIMIT 10
Please help me modify this query so that it returns all the tutors from Tutors_Tag_Relations table which have reference to both the terms "key1" and "key2" (AND logic instead of OR logic). Please suggest an optimized query considering huge number of data records (the query should NOT individually fetch two sets of tutors matching each keyword and then find the intersection).
Update
Taking the question to the next level. Please run the following fresh queries :-
===================================================================================
CREATE TABLE IF NOT EXISTS learning_packs_tag_relations (
id_tag int(10) unsigned NOT NULL DEFAULT '0',
id_tutor int(10) DEFAULT NULL,
id_lp int(10) unsigned DEFAULT NULL,
KEY Learning_Packs_Tag_Relations_FKIndex1 (id_tag),
KEY id_lp (id_lp),
KEY id_tag (id_tag)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS learning_packs (
id_lp int(10) unsigned NOT NULL AUTO_INCREMENT,
id_status int(10) unsigned NOT NULL DEFAULT '2',
id_author int(10) unsigned NOT NULL DEFAULT '0',
name varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (id_lp)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=21 ;
CREATE TABLE IF NOT EXISTS tutors_tag_relations (
id_tag int(10) unsigned NOT NULL DEFAULT '0',
id_tutor int(10) DEFAULT NULL,
KEY Tutors_Tag_Relations (id_tag),
KEY id_tutor (id_tutor),
KEY id_tag (id_tag)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS users (
id_user int(10) unsigned NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL DEFAULT '',
surname varchar(155) NOT NULL DEFAULT '',
PRIMARY KEY (id_user)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=52 ;
CREATE TABLE IF NOT EXISTS tutor_details (
id_tutor int(10) NOT NULL AUTO_INCREMENT,
id_user int(10) NOT NULL,
PRIMARY KEY (id_tutor)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=60 ;
CREATE TABLE IF NOT EXISTS tags (
id_tag int(10) unsigned NOT NULL AUTO_INCREMENT,
tag varchar(255) DEFAULT NULL,
PRIMARY KEY (id_tag),
UNIQUE KEY tag (tag)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
ALTER TABLE learning_packs_tag_relations
ADD CONSTRAINT Learning_Packs_Tag_Relations_ibfk_1 FOREIGN KEY (id_tag) REFERENCES tags (id_tag) ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE learning_packs
ADD CONSTRAINT Learning_Packs_ibfk_2 FOREIGN KEY (id_author) REFERENCES users (id_user) ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE tutors_tag_relations
ADD CONSTRAINT Tutors_Tag_Relations_ibfk_1 FOREIGN KEY (id_tag) REFERENCES tags (id_tag) ON DELETE NO ACTION ON UPDATE NO ACTION;
INSERT INTO test.users (
id_user ,
name ,
surname
)
VALUES (
NULL , 'Vivian', 'Richards'
), (
NULL , 'Sachin', 'Tendulkar'
);
INSERT INTO test.users (
id_user ,
name ,
surname
)
VALUES (
NULL , 'Don', 'Bradman'
);
INSERT INTO test.tutor_details (
id_tutor ,
id_user
)
VALUES (
NULL , '52'
), (
NULL , '53'
);
INSERT INTO test.tutor_details (
id_tutor ,
id_user
)
VALUES (
NULL , '54'
);
INSERT INTO test.tags (
id_tag ,
tag
)
VALUES (
1 , 'Vivian'
), (
2 , 'Richards'
);
INSERT INTO test.tags (id_tag, tag) VALUES (3, 'Sachin'), (4, 'Tendulkar');
INSERT INTO test.tags (id_tag, tag) VALUES (5, 'Don'), (6, 'Bradman');
INSERT INTO test.learning_packs (id_lp, id_status, id_author, name) VALUES ('1', '1', '52', 'Cricket 1'), ('2', '2', '52', 'Cricket 2');
INSERT INTO test.tags (id_tag, tag) VALUES ('7', 'Cricket'), ('8', '1');
INSERT INTO test.tags (id_tag, tag) VALUES ('9', '2');
INSERT INTO test.learning_packs_tag_relations (id_tag, id_tutor, id_lp) VALUES ('7', '52', '1'), ('8', '52', '1');
INSERT INTO test.learning_packs_tag_relations (id_tag, id_tutor, id_lp) VALUES ('7', '52', '2'), ('9', '52', '2');
===================================================================================
About the new system -
- The system now has 4 more tables - tutors, Users (linked to tutor_details), learning_packs, learning_packs_tag_relations
- Tutors create packs - tag relations for tutors stored in tutors_tag_relations and those for packs stored in learning_packs_tag_relations.
Now I want to search learning_packs, with the same AND logic. Help me modify the following query so that searching pack name or tutor's name, surname results all active packs (either directly those packs or packs created by those tutors).
==================================================================================
select lp.*
from Learning_Packs AS lp
LEFT JOIN Learning_Packs_Tag_Relations AS lptagrels ON lp.id_lp = lptagrels.id_lp
LEFT JOIN Tutors_Tag_Relations as ttagrels ON lp.id_author = ttagrels.id_tutor
LEFT JOIN Tutor_Details AS td ON ttagrels.id_tutor = td.id_tutor
LEFT JOIN Users as u on td.id_user = u.id_user
JOIN Tags as t on (t.id_tag = lptagrels.id_tag) or (t.id_tag = ttagrels.id_tag)
where lp.id_status = 1 AND ( t.tag LIKE "%Vivian%" OR t.tag LIKE "%Richards%" )
group by lp.id_lp HAVING count(lp.id_lp) > 1 limit 0,20
As you can see, searching "Cricket 1" returns that pack but searching Vivian Richards does not return the same pack.
Please help
Pretty simple if using Group and Having. This should get what you are looking for.
SELECT id_tutor
FROM Tutors_Tag_Relations AS td
INNER JOIN Tags AS t ON t.id_tag = td.id_tag
WHERE t.tag LIKE "%key1%"
or t.tag LIKE "%key2%"
group by id_tutor
having count(id_tutor)>1