Дизайн MySQL с динамическим количеством полей

Мой опыт работы с MySQL очень прост. Простой материал достаточно прост, но я столкнулся с чем-то, что потребует немного больше знаний. Мне нужна таблица, в которой хранится небольшой список слов. Количество сохраненных слов может быть где-то между 1 и 15. Позже я планирую поиск по таблице этими словами. Я подумал о нескольких разных методах:

A.) Я мог бы создать базу данных с 15 полями и просто заполнить поля нулевыми значениями, когда данные будут меньше 15. Мне это не очень нравится. Это кажется действительно неэффективным.

B.) Другой вариант – использовать только одно поле и хранить данные в виде списка, разделенного запятыми. Всякий раз, когда я возвращаюсь к поиску, я просто запускаю регулярное выражение на поле. Опять же, это кажется действительно неэффективным.

Я надеюсь, что есть хорошая альтернатива этим двум вариантам. Любые советы будут очень оценены.

-Благодаря

C) использовать обычную форму ; используйте несколько строк с соответствующими ключами. пример:

mysql> SELECT * FROM blah; +----+-----+-----------+ | K | grp | name | +----+-----+-----------+ | 1 | 1 | foo | | 2 | 1 | bar | | 3 | 2 | hydrogen | | 4 | 4 | dasher | | 5 | 2 | helium | | 6 | 2 | lithium | | 7 | 4 | dancer | | 8 | 3 | winken | | 9 | 4 | prancer | | 10 | 2 | beryllium | | 11 | 1 | baz | | 12 | 3 | blinken | | 13 | 4 | vixen | | 14 | 1 | quux | | 15 | 4 | comet | | 16 | 2 | boron | | 17 | 4 | cupid | | 18 | 4 | donner | | 19 | 4 | blitzen | | 20 | 3 | nod | | 21 | 4 | rudolph | +----+-----+-----------+ 21 rows in set (0.00 sec) 

Это таблица, которую я опубликовал в этом другом вопросе о group_concat . Вы заметите, что для каждой строки есть уникальный ключ K Существует еще один ключ grp который представляет каждую категорию. Оставшееся поле представляет собой элемент категории, и для каждой категории могут быть переменные числа.

Какие еще данные связаны с этими словами?

Один типичный способ решения этой проблемы лучше всего описать на примере. Предположим, что ваша таблица фиксирует определенные слова, найденные в определенных документах. Один из типичных способов – присвоить каждому документу идентификатор. Давайте покажем, что каждый документ является веб-URL, поэтому у вас будет таблица примерно так:

 CREATE TABLE WebPage ( ID INTEGER NOT NULL, URL VARCHAR(...) NOT NULL ) 

Таблица ваших слов может выглядеть примерно так:

 CREATE TABLE Words ( Word VARCHAR(...) NOT NULL, DocumentID INTEGER NOT NULL ) 

Затем для каждого слова вы создаете новую строку в таблице. Чтобы найти все слова в определенном документе, выберите по идентификатору документа:

 SELECT Words.Word FROM Words, WebPage WHERE Words.DocumentID = WebPage.DocumentID AND WebPage.URL = 'http://whatever/web/page/' 

Чтобы найти все документы с определенным словом, выберите по слову:

 SELECT WebPage.URL FROM WebPage, Words WHERE Words.Word = 'hello' AND Words.DocumentID = WebPage.DocumentID 

Или некоторые такие.

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

Тогда не ответ, чтобы иметь отдельную таблицу для ключевых слов? Вам также необходимо иметь отношение «многие ко многим» между ключевыми словами и основной таблицей.

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

 ID int Word varchar(100) 

Таблица CAR имела бы структуру вроде:

 ID int Name varchar(100) 

Затем вам понадобится таблица CAR_WORD, чтобы удержать отношения «многие ко многим»:

 ID int CAR_ID int WORD_ID int 

И пример данных, которые нужно использовать для таблицы WORD:

 ID Word 001 Family 002 Sportscar 003 Sedan 004 Hatchback 005 Station-wagon 006 Two-door 007 Four-door 008 Diesel 009 Petrol 

вместе с данными выборки для таблицы CAR

 ID Name 001 Audi TT 002 Audi A3 003 Audi A4 

то данные примера пересечения CAR_WORD могут быть:

 ID CAR_ID WORD_ID 001 001 002 002 001 006 003 001 009 

которые придают Audi TT правильные характеристики.

и, наконец, SQL для поиска будет выглядеть примерно так:

 SELECT c.name FROM CAR c INNER JOIN CAR_WORD x ON c.id = x.id INNER JOIN WORD w ON x.id = w.id WHERE w.word IN('Petrol', 'Two-door') 

Уф! Не собирался писать так много, он выглядит сложным, но я всегда стараюсь, как бы стараюсь, не упрощать вещи.

Я бы создал таблицу с идентификатором и одним полем, а затем сохранил результаты как несколько записей. Это дает много преимуществ. Например, вы можете программным образом применять свой 15-дневный лимит вместо того, чтобы делать это в своем дизайне, поэтому, если вы когда-нибудь передумаете, это будет довольно легко. Ваши запросы на поиск по данным также будут намного быстрее запускаться, регулярные выражения занимают много времени для запуска (сравнительно). Плюс использование varchar для поля позволит вам сжать ваш стол намного лучше. И индексирование на столе должно быть намного проще (более эффективно) с этим дизайном.

Сделайте дополнительную работу и сохраните 15 слов в виде 15 строк в таблице, то есть нормализуйте данные. Это может потребовать, чтобы вы немного передумали свою стратегию, но поверьте мне, когда клиент приходит и говорит: «Можете ли вы изменить этот предел до 20 …», вы будете рады, что сделали.

В зависимости от того, что вы хотите выполнить:

  1. Использовать полнотекстовый индекс в таблице строк.

  2. Три таблицы: одна для исходной строки, одна для уникальных слов (после укоренения слов?) И таблица соединений. Это также позволит вам выполнять более сложные поиски, такие как «вернуть все строки, содержащие как минимум три из следующих пяти слов», или «вернуть все строки, где« лиса »происходит после« собака »».

    CREATE TABLE string (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, строка TEXT NOT NULL)

    CREATE TABLE word (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, слово VARCHAR (14) NOT NULL UNIQUE, UNIQUE INDEX (слово ASC))

    CREATE TABLE word_string (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, string_id INT NOT NULL, word_id INT NOT NULL, word_order INT NOT NULL, FOREIGN KEY (string_id) ССЫЛКИ (string.id), FOREIGN KEY (word_id) ССЫЛКИ (word.id) , INDEX (word_id ASC))

    // Пример данных INSERT INTO string (string) VALUES ('Это тестовая строка'), («Быстрая красная лиса перепрыгнула через ленивую коричневую собаку»)

    INSERT INTO word (word) VALUES ('this'), ('test'), ('string'), ('quick'), ('red'), ('fox'), ('jump'), ( 'over'), («ленивый»), («коричневый»), («собака»)

    INSERT INTO word_string (string_id, word_id, word_order) VALUES (0, 0, 0), (0, 1, 3), (0, 2, 4), (1, 3, 1), (1, 4, 2) , (1, 5, 3), (1, 6, 4), (1, 7, 5), (1, 8, 7), (1, 9, 8), (1, 10, 9)

    // Пример запроса – найти все строки, содержащие «fox» и «quick» SELECT UNIQUE string.id, string.string FROM string INNER JOIN word_string ON string.id = word_string.string_id INNER JOIN word AS fox ON fox.word = 'fox 'AND word_string.word_id = fox.id INNER JOIN word AS quick ON quick.word =' ​​quick 'AND word_string.word_id = word.id

Вы правы, что A не годится. B тоже нехорошо, так как он не соответствует первой нормальной форме (каждое поле должно быть атомарным). В вашем примере ничего не говорится о том, что вы выиграете, избегая 1NF.

Вам нужна таблица для вашего списка слов с каждым словом в своей строке.