Эффективный поиск уникальных значений в таблице базы данных - PullRequest
4 голосов
/ 11 февраля 2010

У меня есть таблица базы данных с очень большим количеством строк. Эта таблица представляет сообщения, которые регистрируются системой. Каждое сообщение имеет тип сообщения, и оно сохраняется в поле в таблице. Я пишу веб-сайт для запроса этого журнала сообщений. Если я хочу выполнить поиск по типу сообщения, то в идеале я хотел бы, чтобы в раскрывающемся списке были перечислены типы сообщений, появившиеся в базе данных. Типы сообщений могут меняться со временем, поэтому я не могу жестко запрограммировать типы в выпадающем списке. Я должен сделать какой-то поиск. Перебор всего содержимого таблицы для нахождения уникальных значений сообщений, очевидно, очень глуп, но глуп в поле базы данных. Я здесь прошу лучшего способа. Возможно, лучше было бы использовать отдельную таблицу поиска, которую периодически обновляет база данных, перечисляя только уникальные типы сообщений, из которых я могу заполнить раскрывающийся список.

Любые предложения будут высоко оценены.

Я использую платформу ASP.NET MVC и SQL Server 2005

Ответы [ 9 ]

9 голосов
/ 11 февраля 2010

Отдельная таблица поиска с идентификатором типа сообщения, хранящегося в вашем журнале Это уменьшит размер и увеличит эффективность журнала. Также это нормализует ваши данные.

5 голосов
/ 11 февраля 2010

Да, я бы определенно пошел с отдельной таблицей поиска. Затем вы можете заполнить его, используя что-то вроде:

INSERT TypeLookup (Type)
SELECT DISTINCT Type
FROM BigMassiveTable

Затем можно периодически запускать задание пополнения, чтобы получать из основной таблицы новые типы, которых еще нет в таблице поиска.

2 голосов
/ 11 февраля 2010
SELECT  DISTINCT message_type
FROM    message_log

- самый простой, но не очень эффективный способ.

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

SELECT  message_type
FROM    message_types mt
WHERE   message_type IN
        (
        SELECT  message_type
        FROM    message_log
        )

Это будет более эффективно, если индексировано message_log.message_type.

Если у вас нет этой таблицы, но вы хотите ее создать, и message_log.message_type проиндексирован, используйте рекурсивную CTE для эмуляции свободного сканирования индекса:

WITH    rows (message_type) AS
        (
        SELECT  MIN(message_type) AS mm
        FROM    message_log
        UNION ALL
        SELECT  message_type
        FROM    (
                SELECT  mn.message_type, ROW_NUMBER() OVER (ORDER BY mn.message_type) AS rn
                FROM    rows r
                JOIN    message_type mn
                ON      mn.message_type > r.message_type
                WHERE   r.message_type IS NOT NULL
                ) q
        WHERE   rn = 1
        )
SELECT  message_type
FROM    rows r
OPTION (MAXRECURSION 0)
1 голос
/ 11 февраля 2010

Создать индекс по типу сообщения:

CREATE INDEX IX_Messages_MessageType ON Messages (MessageType)

Затем, чтобы получить список уникальных типов сообщений , вы запускаете:

SELECT DISTINCT MessageType
FROM Messages
ORDER BY MessageType

Поскольку индекс физически отсортирован в порядке MessageType SQL Server может очень быстро и эффективно сканировать индекс, выбирая список уникальных типов сообщений.

Это неплохое исполнение - это то, в чем SQL Server хорош.


По общему признанию, вы можете сэкономить место, имея таблицу " типы сообщений ". И если вы одновременно отображаете только несколько сообщений: тогда поиск по закладке , так как он возвращается к таблице MessageTypes, не будет проблемой. Но если вы начнете отображать сотни или тысячи сообщений одновременно, то соединение с MessageTypes может стать довольно дорогим и ненужным, и будет быстрее хранить MessageType вместе с сообщением.

Но у меня не было бы проблем с созданием индекса для столбца MessageType и выбором distinct. SQL Server любит такие вещи. Но если вы обнаружите, что это реальная нагрузка на ваш сервер, как только вы получаете десятки попаданий в секунду, следуйте другому совету и кешируйте их в памяти.

Мое личное решение будет:

  • создать индекс
  • выберите отличный

и если у меня все еще были проблемы

  • кэш в памяти, срок действия которого истекает через 30 секунд

Что касается нормализованного / денормализованного вопроса. Нормализация экономит место за счет использования ЦП, когда соединения выполняются постоянно. Но логическая точка денорализации состоит в том, чтобы избежать дублирования данных, что может привести к противоречивым данным.

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

Или можно что-то сказать о том, что на момент сообщения тип сообщения был «Запрос клиента отправлен»?

1 голос
/ 11 февраля 2010

Я просто хотел заявить об очевидном: нормализовать данные.

message_types
message_type | message_type_name

messages
message_id | message_type | message_type_name

Тогда вы можете просто обойтись без кэшированного DISTINCT:

Для вашего выпадающего списка

SELECT * FROM message_types

Для вашего поиска

SELECT * FROM messages WHERE message_type = ? 

SELECT m.*, mt.message_type_name FROM messages AS m
JOIN message_types AS mt
ON ( m.message_type = mt.message_type)

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

0 голосов
/ 12 февраля 2010

Ответ заключается в том, чтобы использовать «DISTINCT», и каждое лучшее решение отличается для разных размеров таблицы. Тысячи строк, миллионы, миллиарды? Больше ? Это очень разные лучшие решения.

0 голосов
/ 12 февраля 2010

Как уже говорили другие, создайте отдельную таблицу типов сообщений. При добавлении записи в таблицу сообщений проверьте, существует ли уже тип сообщения в таблице. Если нет, добавьте это. В любом случае, затем отправьте идентификатор из таблицы типов сообщений в таблицу сообщений. Это должно дать вам нормализованные данные. Да, добавление записи занимает немного больше времени, но должно быть более эффективным при извлечении.

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

А именно, (a) Включите отметку времени в каждой записи сообщения. (b) Сохраните список типов сообщений, найденных по состоянию на последний раз, когда вы проверяли. (c) Каждый раз, когда вы проверяете, ищите любые новые типы сообщений, добавленные с последнего раза, например:

create table temp_new_types as
    (select distinct message_type
    from message
    where timestamp>last_type_check
);

insert into message_type_list (message_type)
select message_type
from temp_new_types
where message_type not in (select message_type from message_type_list);

drop table temp_new_types;

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

0 голосов
/ 11 февраля 2010

MessageType должен быть внешним ключом в основной таблице к таблице определения, содержащей коды и описания типов сообщений. Это значительно повысит производительность поиска.

Что-то вроде

DECLARE @MessageTypes TABLE(
        MessageTypeCode VARCHAR(10),
        MessageTypeDesciption VARCHAR(100)
)

DECLARE @Messages TABLE(
        MessageTypeCode VARCHAR(10),
        MessageValue VARCHAR(MAX),
        MessageLogDate DATETIME,
        AdditionalNotes VARCHAR(MAX)
)

Из этой схемы ваш поиск должен только запрашивать Типы сообщений

0 голосов
/ 11 февраля 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...