Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/topic.php on line 737
Форумы портала PHP.SU :: Не работают механизмы внешних ключей.

 PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (1): [1]   

> Без описания
maximushka
Отправлено: 05 Февраля, 2011 - 21:26:07
Post Id


Новичок


Покинул форум
Сообщений всего: 40
Дата рег-ции: Нояб. 2009  


Помог: 0 раз(а)




Не могу на реальной практике испытать возможности механизма внешних ключей, не работает ни один из 3ех возможностей:
1. вывод ошибки при добавлении записи которая во внешнем ключе содержит значение, которого нет в поле другой таблицы, на которое ссылается он.
2. каскадное удаление.
3. каскадное обновление.
Вот мой пример:
CODE (SQL):
скопировать код в буфер обмена
  1.  
  2. CREATE TABLE `users`
  3. (
  4. `id`    INT UNSIGNED NOT NULL AUTO_INCREMENT ,
  5. `nik` VARCHAR(15) NOT NULL,
  6. `password` CHAR(32) NOT NULL,
  7. `time` INT UNSIGNED NOT NULL,
  8. PRIMARY KEY (`id`),
  9. UNIQUE INDEX (`nik`)
  10. )CHARACTER SET=UTF8;
  11.  
  12. CREATE TABLE `messages`
  13. (
  14. `time` INT UNSIGNED DEFAULT 6 ,
  15. `no`    INT  NOT NULL AUTO_INCREMENT,
  16. `user_id` INT UNSIGNED NOT NULL,
  17. `text` BLOB,
  18. PRIMARY KEY (`time`,`no`),
  19. KEY (`user_id`),
  20. FOREIGN KEY (`user_id`)
  21.         REFERENCES `users` (`id`)
  22.         ON DELETE CASCADE
  23.         ON UPDATE CASCADE
  24. )CHARACTER SET=UTF8;
  25.  
  26. INSERT INTO `users` (`nik`,`password`,`time`)
  27. VALUES ('MAX','8f95be5ec3c0c103e51de269d39f5592', 1296120857),
  28. ('ANNA','d9681d05860552e9c3113da381f916fc', 1294423538);
  29.  
  30. INSERT INTO `messages` (`user_id`, `text`) VALUES (3,'Без связки!'),(1,'Со связкой'),(2,'И этот со связкой');
  31. DELETE FROM `users` WHERE `id`=1;
  32. UPDATE `users` SET `id`=4 WHERE `id`=2;
  33. SELECT * FROM `messages`;
  34.  

Он успешно проходит вместо того чтобы дать сообщение об ошибке при первой вставке, и выдаёт такую таблицу (справа я написал комментарий к строкам что меня не устраивает, и что я ожидал):
Цитата:

time no user_id text
6 1 3 Без связки! - Вывод об ошибке, т.к. нет пользователя с номером 3
6 2 1 Со связкой - это сообщения должно быть удалено с удалением пользователя с номером 1
6 3 2 И этот со связкой - у этого должен каскадно обновиться user_id и стать равным четырём
 
 Top
Мелкий Супермодератор
Отправлено: 05 Февраля, 2011 - 21:29:05
Post Id



Активный участник


Покинул форум
Сообщений всего: 11926
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 618 раз(а)




дефолтно создаются таблицы MyISAM, не поддерживающие внешние ключи. Используйте innodb


-----
PostgreSQL DBA
 
 Top
maximushka
Отправлено: 06 Февраля, 2011 - 11:30:37
Post Id


Новичок


Покинул форум
Сообщений всего: 40
Дата рег-ции: Нояб. 2009  


Помог: 0 раз(а)




Мелкий пишет:
дефолтно создаются таблицы MyISAM, не поддерживающие внешние ключи. Используйте innodb

Спасибо, вы пожалуй правы.
Но в этом случае на запрос:
CODE (SQL):
скопировать код в буфер обмена
  1.  
  2. CREATE TABLE `messages`
  3. (
  4. `time` INT UNSIGNED DEFAULT 6 ,
  5. `no`    INT UNSIGNED NOT NULL AUTO_INCREMENT,
  6. `user_id` INT UNSIGNED NOT NULL,
  7. `text` BLOB,
  8. PRIMARY KEY (`time`,`no`),
  9. FOREIGN KEY (`user_id`)
  10.         REFERENCES `users` (`id`)
  11.         ON DELETE CASCADE
  12.         ON UPDATE CASCADE
  13. )TYPE=INNODB CHARACTER SET=UTF8;
  14.  

Выдаётся ошибка:
Цитата:
1075 Incorrect table definition; there can be only one auto column and it must be defined as a key

Когда я убираю AUTO_INCREMENT или убираю инкрементовое поле `no` или поле `time` из первичного ключа, то запрос проходит без ошибки и таблица создаётся.
Видимо INNODB почему-то не любит если в составе составного первичного ключа используется одно поле с AUTO_INCREMENT, а для меня этот инкремент нужен именно в таком качестве, потому что в одну секунду может поступить несколько сообщений, а уникальность каждого соблюсти надо. Я понимаю что можно обойтись одним инкрементным полем `no` в качестве первичного ключа, но хочется знать можно ли сделать именно по своей задумке? Зачем?
Ну, например, для того чтобы использовать поле `no` меньшего размера для экономии памяти, например TINYINT, если известно, что за 1 секунду не может поступить более 255 сообщений. Однако за всю историю существования чата сообщений может быть куда более 255, и разумеется нужно делать ключ для сообщений побольше чем в 1 байт(TINYINT), а время каждого сообщения в секундах от 1970 года тоже требуется хранить, и получается уже не совсем рационально.
 
 Top
TM123
Отправлено: 06 Февраля, 2011 - 11:40:15
Post Id


Новичок


Покинул форум
Сообщений всего: 35
Дата рег-ции: Нояб. 2010  
Откуда: Москва


Помог: 0 раз(а)




Ну вообще если у вас одно поле AUTO_INCREMENT, то у вас комбинация всегда будет уникальной!!!, за исключением принудительной вставки AUTO_INCREMENT поля своим значением, но если есть такая возможность, тогда зачем делать поле AUTO_INCREMENT.

Такой логики придерживается MySQL и это в общем то правильная логика, поэтому вас и обругивает база.


-----
Программим потихоньку http://www[dot]altycon[dot]ru
 
 Top
garvey
Отправлено: 06 Февраля, 2011 - 11:43:02
Post Id



Частый посетитель


Покинул форум
Сообщений всего: 528
Дата рег-ции: Май 2010  
Откуда: Minsk


Помог: 3 раз(а)




Вот таким способом нужно создавать InnoDB сущности:
CODE (SQL):
скопировать код в буфер обмена
  1. CREATE TABLE `table_name` (
  2. ...FIELDS
  3. ) ENGINE=InnoDB;

Согласно формам нормализации, таблица всегда должна иметь уникальный первичный ключ. У вас его нет.
 
 Top
maximushka
Отправлено: 06 Февраля, 2011 - 11:57:32
Post Id


Новичок


Покинул форум
Сообщений всего: 40
Дата рег-ции: Нояб. 2009  


Помог: 0 раз(а)




TM123 пишет:
за исключением принудительной вставки AUTO_INCREMENT поля своим значением, но если есть такая возможность, тогда зачем делать поле AUTO_INCREMENT.
я такой возможностью в таблице `messages` и не планировал пользоваться, поле с инкрементом всегда само устанавливается во вставляемой записи. Не пойму с чего вы это взяли?
garvey пишет:
ENGINE=InnoDB;
а чем ENGINE отличается от TYPE?
garvey пишет:
таблица всегда должна иметь уникальный первичный ключ. У вас его нет.
Как это у меня его нет? я же указал:
CODE (SQL):
скопировать код в буфер обмена
  1.  
  2. PRIMARY KEY (`time`,`no`)
, он вполне уникальный и первичный ключ. Правда составной.
 
 Top
maximushka
Отправлено: 06 Февраля, 2011 - 18:58:34
Post Id


Новичок


Покинул форум
Сообщений всего: 40
Дата рег-ции: Нояб. 2009  


Помог: 0 раз(а)




Достиг желаемого на голом SQL, правда несколько тяжеловато вышло.
Значит проблема была в том, что таблицы InnoDB не поддерживают инкрементное поле в составе составного ключа, которое мне вообщем-то нужно было позарез, в обход этой ситуации я наметил такую конструкцию вставки, ведь инкремент работает при вставки:
CODE (SQL):
скопировать код в буфер обмена
  1. INSERT INTO `messages`(`time`,`no`,`user_id`, `text`)
  2. SELECT @p:=123, IFNULL(MAX(`m2`.`no`),0)+1,1,'Без связки!' FROM `messages` AS `m2` WHERE `m2`.`time`=@p;

Т.е. в работе можно убедиться выполнив, например:
CODE (SQL):
скопировать код в буфер обмена
  1. INSERT INTO `messages`(`time`,`no`,`user_id`, `text`)
  2. SELECT @p:=123, IFNULL(MAX(`m2`.`no`),0)+1,1,'Без связки!' FROM `messages` AS `m2` WHERE `m2`.`time`=@p;
  3. INSERT INTO `messages`(`time`,`no`,`user_id`, `text`)
  4. SELECT @p:=123, IFNULL(MAX(`m2`.`no`),0)+1,1,'Без связки!' FROM `messages` AS `m2` WHERE `m2`.`time`=@p;
  5. INSERT INTO `messages`(`time`,`no`,`user_id`, `text`)
  6. SELECT @p:=123, IFNULL(MAX(`m2`.`no`),0)+1,1,'Без связки!' FROM `messages` AS `m2` WHERE `m2`.`time`=@p;
  7. INSERT INTO `messages`(`time`,`no`,`user_id`, `text`)
  8. SELECT @p:=125, IFNULL(MAX(`m2`.`no`),0)+1,1,'Без связки!' FROM `messages` AS `m2` WHERE `m2`.`time`=@p;
  9. INSERT INTO `messages`(`time`,`no`,`user_id`, `text`)
  10. SELECT @p:=125, IFNULL(MAX(`m2`.`no`),0)+1,1,'Без связки!' FROM `messages` AS `m2` WHERE `m2`.`time`=@p;

и получив правильно идущие значения искусственного автоинкремента в поле `no`:

Цитата:
time no
123 1
123 2
123 3
125 1
125 2
 
 Top
Страниц (1): [1]
Сейчас эту тему просматривают: 0 (гостей: 0, зарегистрированных: 0)
« SQL и Архитектура БД »


Все гости форума могут просматривать этот раздел.
Только зарегистрированные пользователи могут создавать новые темы в этом разделе.
Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.
 



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB