Не удалось создать связь между двумя таблицами

У меня есть две таблицы:

CREATE TABLE `Users` (
  `user_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(50) NOT NULL DEFAULT '',
  `last_name` varchar(50) NOT NULL DEFAULT '',
  `login` varchar(50) NOT NULL DEFAULT '',
  `password` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

А ТАКЖЕ

CREATE TABLE `Books` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(50) NOT NULL DEFAULT '',
  `author` varchar(50) NOT NULL DEFAULT '',
  `year` int(4) NOT NULL,
  `available` int(3) NOT NULL DEFAULT '0',
  `availabledate` date NOT NULL,
  `user_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

Я пытаюсь создать связь между этими двумя, чтобы у одного пользователя могло быть несколько книг (user_id), но что бы я ни делал, я получаю ошибки. Либо

Не удается добавить или обновить дочернюю строку: ограничение внешнего ключа не работает (bookstore., ОГРАНИЧЕНИЕ books_fk FOREIGN KEY (user_id) ССЫЛКИ users (user_id) ON DELETE CASCADE ON UPDATE CASCADE)

или до того, как я не использовал unsigned int в таблице Books, и я сказал, что значение по умолчанию равно 0 (что я бы предпочел, но я не думаю, что смогу это сделать?) В этом случае я получил ошибку 150.


person Fengson    schedule 28.01.2014    source источник


Ответы (3)


Я бы рекомендовал изменить схему вашей базы данных. Почему?

  1. Может ли книга существовать без пользователя? Если да, у вас не должно быть внешнего ключа из книг, ссылающихся на пользователей. Может ли пользователь существовать без книги? Если да, у вас не должно быть внешнего ключа от пользователей, ссылающихся на книги.

  2. Может ли пользователь иметь несколько книг? А книга нескольких пользователей? Если да, то у вас отношения m:n. Это означает, что вам нужен бридж-стол.

В ваших таблицах вам не нужны внешние ключи:

CREATE TABLE `Users` (
  `user_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(50) NOT NULL DEFAULT '',
  `last_name` varchar(50) NOT NULL DEFAULT '',
  `login` varchar(50) NOT NULL DEFAULT '',
  `password` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

CREATE TABLE `Books` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(50) NOT NULL DEFAULT '',
  `author` varchar(50) NOT NULL DEFAULT '',
  `year` int(4) NOT NULL,
  `available` int(3) NOT NULL DEFAULT '0',
  `availabledate` date NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

А бридж-таблица будет выглядеть так:

CREATE TABLE books_users (
book_id int(11) unsigned NOT NULL,
user_id int(11) unsigned NOT NULL,
PRIMARY KEY (book_id, user_id),
KEY idx_user_id (user_id),
FOREIGN KEY fk_books (book_id) REFERENCES Books(id),
FOREIGN KEY fk_users (user_id) REFERENCES Users(user_id)
) ENGINE=InnoDB;

Это решает обе проблемы и является обычной практикой.

Чтобы запросить и пользователей, и книги в одном запросе, вы соединяете их следующим образом:

SELECT
whatever
FROM
Books b
INNER JOIN books_users bu ON b.id = bu.book_id
INNER JOIN users u ON bu.user_id = u.user_id
WHERE user_id = 1 /*for example*/
;

Если вы хотите что-то вставить в таблицы, просто выполните вставку и получите идентификатор, сгенерированный для строки с SELECT LAST_INSERT_ID();, затем вставьте этот идентификатор в таблицу моста books_users.

Обновления ни на что не влияют, их можно просто выполнить на users или books. Если вам действительно нужно обновить столбец auto_increment (что обычно не требуется и не рекомендуется), вы можете добавить ON UPDATE CASCADE после внешних ключей в таблице books_users.

person fancyPants    schedule 28.01.2014
comment
@Fengson Пожалуйста, примите во внимание мой ответ, хотя у Чинтана Гора больше голосов. Он решает проблему, заключающуюся в том, что вы не можете добавить внешний ключ в таблицу, но дизайн вашей таблицы все равно может быть неправильным. - person fancyPants; 28.01.2014
comment
Я создал таблицы, как вы сказали, потому что я хочу иметь только книги и пользователей. Но не могли бы вы сказать мне, как я могу сохранять и собирать информацию таким образом? Когда я размещаю заказ, что мне обновлять? А когда я хочу хорошо отображать asd? - person Fengson; 28.01.2014
comment
@Fengson Отредактировал мой ответ. Он отвечает на ваши вопросы? - person fancyPants; 28.01.2014

Измените эти строки и повторите попытку.

изменить user_id int(11) unsigned NOT NULL AUTO_INCREMENT на user_id int(11) NOT NULL AUTO_INCREMENT

а также

id int(11) unsigned NOT NULL AUTO_INCREMENT, to id int(11) NOT NULL AUTO_INCREMENT,

а также

user_id int(11) unsigned NOT NULL, до user_id int(11) NOT NULL,

Наконец попробуйте

CREATE TABLE `Users` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(50) NOT NULL DEFAULT '',
  `last_name` varchar(50) NOT NULL DEFAULT '',
  `login` varchar(50) NOT NULL DEFAULT '',
  `password` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

а также

CREATE TABLE `Books` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(50) NOT NULL DEFAULT '',
  `author` varchar(50) NOT NULL DEFAULT '',
  `year` int(4) NOT NULL,
  `available` int(3) NOT NULL DEFAULT '0',
  `availabledate` date NOT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
person Damith    schedule 28.01.2014

Если вы используете Mysql, вы должны использовать механизм базы данных InnoDB, чтобы установить связь между двумя таблицами. MyISAM не допустит эту связь.

alter table `users` add constraint `FK_users` FOREIGN KEY (`user_id`) REFERENCES `books` (`user_id`) ON DELETE NO ACTION  ON UPDATE NO ACTION 

или вы можете использовать этот запрос для каскадного редактирования удаления

alter table `users` add constraint `FK_users` FOREIGN KEY (`user_id`) REFERENCES `books` (`user_id`) ON DELETE CASCADE  ON UPDATE CASCADE 

Это ваш новый DDL для двух таблиц, попробуйте это

CREATE TABLE IF NOT EXISTS `books` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(50) NOT NULL DEFAULT '',
  `author` varchar(50) NOT NULL DEFAULT '',
  `year` int(4) NOT NULL,
  `available` int(3) NOT NULL DEFAULT '0',
  `availabledate` date NOT NULL,
  `user_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

-- --------------------------------------------------------

--
-- Table structure for table `users`
--

CREATE TABLE IF NOT EXISTS `users` (
  `user_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(50) NOT NULL DEFAULT '',
  `last_name` varchar(50) NOT NULL DEFAULT '',
  `login` varchar(50) NOT NULL DEFAULT '',
  `password` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=15 ;

--
-- Constraints for dumped tables
--

--
-- Constraints for table `users`
--
ALTER TABLE `users`
  ADD CONSTRAINT `FK_users` FOREIGN KEY (`user_id`) REFERENCES `books` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
person Chintan Gor    schedule 28.01.2014
comment
позвольте мне проверить, сэр, ваше определение - person Chintan Gor; 28.01.2014
comment
пожалуйста, проверьте, я добавил новый запрос, чтобы добавить отношение для редактирования и удаления - person Chintan Gor; 28.01.2014
comment
Конечно, MyISAM не беспокоит? - person Strawberry; 28.01.2014
comment
@Strawberry Да, сэр, посмотрите эту ссылку stackoverflow.com/questions/12971246/ - person Chintan Gor; 30.01.2014
comment
Точно. MyISAM просто игнорирует ограничение. Отношения не запрещены! - person Strawberry; 30.01.2014
comment
@Strawberry, вы имеете в виду, что я могу использовать запрос отношения в MyIsam, и он не выдаст никакой ошибки? хотя отношение не будет учитываться Mysql.. - person Chintan Gor; 30.01.2014
comment
Точно. Это просто игнорируется. :-) - person Strawberry; 30.01.2014
comment
Спасибо, сэр, я не пробовал этот запрос в движке MYIsam, потому что сначала люди подумали, что это не разрешено в этом движке, поэтому я никогда не пробовал это .... Еще раз спасибо: D - person Chintan Gor; 30.01.2014