it-roy-ru.com

Различия терминов "ограничения" и "внешние ключи" в MySQL?

Я смотрю на документы MySQL здесь и пытаюсь разобраться в различии между FOREIGN KEYs и CONSTRAINTs. Я думал, что FK был ограничением, но документы, кажется, говорят о них, как будто они разные вещи.

Синтаксис для создания FK (частично) ...

[CONSTRAINT [symbol]] FOREIGN KEY
    [index_name] (index_col_name, ...)
    REFERENCES tbl_name (index_col_name,...)

Так что предложение CONSTRAINT необязательно. Почему вы включаете это или не включаете? Если вы пропустите это, MySQL создает внешний ключ, но не ограничение? Или это больше похоже на «CONSTRAINT» - это не что иное, как имя для вашего FK, поэтому, если вы не укажете его, вы получите анонимный FK?

Любые разъяснения будут с благодарностью.

Спасибо,

Итан

43
Ethan

Да, внешний ключ - это тип ограничения. MySQL имеет неравномерную поддержку ограничений:

  • PRIMARY KEY: yes как ограничение таблицы и столбца.
  • FOREIGN KEY: да как ограничение таблицы, но только с механизмами хранения InnoDB и BDB; иначе анализируется, но игнорируется.
  • CHECK: анализируется, но игнорируется во всех механизмах хранения.
  • UNIQUE: да как ограничение таблицы и столбца.
  • NOT NULL: да как ограничение столбца.
  • DEFERRABLE и другие атрибуты ограничения: нет поддержки. 

Предложение CONSTRAINT позволяет явно назвать ограничение, чтобы сделать метаданные более читабельными, или использовать имя, когда вы хотите удалить ограничение. Стандарт SQL требует, чтобы предложение CONSTRAINT было необязательным. Если вы не включите его, СУБД автоматически создаст имя, и имя будет соответствовать реализации.

58
Bill Karwin

В общем (не обязательно MySQL), внешние ключи являются ограничениями, но ограничения не всегда являются внешними ключами. Подумайте об ограничениях первичного ключа, уникальных ограничениях и т.д.

Возвращаясь к конкретному вопросу, вы правы, пропуская часть CONSTRAINT [symbol] создаст FK с автоматически сгенерированным именем.

9
Dan C.

На данный момент наши DDL CREATE TABLE имеют этот формат - обратите внимание на синтаксис определения UNIQUE KEY и FOREIGN KEY, который мы использовали. 

CREATE TABLE my_dbschema.my_table (
    id INT unsigned auto_increment PRIMARY KEY,
    account_nbr INT NOT NULL,
    account_name VARCHAR(50) NOT NULL,
    active_flg CHAR(1) NOT NULL DEFAULT 'Y',
    vendor_nbr INT NOT NULL,
    create_ts TIMESTAMP NOT NULL DEFAULT current_timestamp,
    create_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    last_upd_ts TIMESTAMP NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp,
    last_upd_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    UNIQUE KEY uk1_my_table(account_nbr, account_name),
    FOREIGN KEY fk1_my_table(vendor_nbr) REFERENCES vendor(vendor_nbr)
    );

В этом формате MySQL автоматически создает INDEX-ы с именами uk1_my_table и fk1_my_table; но имя объекта FK является чем-то другим - my_table_ibfk_1 (то есть. tablename_ibfk_N - определено системой). Так что ALTER TABLE my_table DROP FOREIGN KEY fk1_my_table не будет работать (и, следовательно, расстраивать и поднимать тревоги), так как нет объекта FK db с таким именем.

Вот альтернативный формат DDL с ограничениями (Ссылка: https://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html ): -

CREATE TABLE my_dbschema.my_table (
    id INT unsigned auto_increment PRIMARY KEY,
    account_nbr INT NOT NULL,
    account_name VARCHAR(50) NOT NULL,
    active_flg CHAR(1) NOT NULL DEFAULT 'Y',
    vendor_nbr INT NOT NULL,
    create_ts TIMESTAMP NOT NULL DEFAULT current_timestamp,
    create_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    last_upd_ts TIMESTAMP NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp,
    last_upd_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    CONSTRAINT uk1_my_table UNIQUE KEY (account_nbr, account_name),
    CONSTRAINT fk1_my_table FOREIGN KEY (vendor_nbr) REFERENCES vendor(vendor_nbr)
    );

В этом формате MySQL все еще создает INDEX-ы с именами uk1_my_table и fk1_my_table автоматически, но имя объекта FK не является чем-то другим - это fk1_my_table, как упомянуто в DDL. Так что ALTER TABLE my_table DROP FOREIGN KEY fk1_my_table работает, но оставляет одноименный INDEX. 

И обратите внимание, что ALTER TABLE my_table DROP INDEX fk1_my_table изначально не будет работать (когда FK еще не удален) с сообщением об ошибке, что он используется в FK! Если команда DROP FK была выполнена успешно, только тогда работает DROP INDEX. 

Надеюсь, что это объясняет и помогает решить путаницу.

2
RxBx

Если я не ошибаюсь, ограничения нуждаются в индексах, поэтому, когда вы создаете, например, ограничение внешнего ключа, MySQL также автоматически создает индекс.

1
Mario Juárez

Не могу ответить за MySQL, но FK - это ограничения. Все, что приводит ваши данные в определенное состояние, является ограничением. Существует несколько видов ограничений: уникальный, первичный ключ, проверка и внешние ключи. Может быть, MySQL есть другие.

Иногда слова допускаются в командах, но не являются обязательными для удобства чтения, как FROM в инструкции DELETE.

1
Mark Brady

Это, наверное, самая запутанная топика в MySQL. 

Многие говорят, что, например, «PRIMARY KEY», «FOREIGN KEY» и «UNIQUE» - это на самом деле индексы! (Официальная документация MySQL включена здесь)

Многие другие, с другой стороны, говорят, что это скорее ограничения (что имеет смысл, потому что, когда вы их используете, вы действительно накладываете ограничения на соответствующие столбцы).

Если они на самом деле являются индексами, то какой смысл использовать условие ограничения для того, чтобы дать ему имя, поскольку вы должны иметь возможность использовать имя этого индекса при его создании?

Пример:

... ИНОСТРАННЫЙ КЛЮЧ index_name (col_name1, col_name2, ...)

Если FOREIGN KEY является индексом, то мы должны иметь возможность использовать index_name для его обработки. Однако мы не можем.

Но если это не индексы, а фактические ограничения, которые используют индексы для работы, то это имеет смысл.

В любом случае, мы не знаем. На самом деле, кажется, никто не знает.

0
Johann