2010-02-11 3 views
4

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

Любые предложения были бы очень признательны.

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

+0

Сколько строк вы говорите? Миллионы? 10 с/100 миллионов? Больше? – AdaTheDev

ответ

9

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

2
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) 
+0

Не будет ли это относительно эффективной мыслью, потому что SQL Server будет кэшировать результаты, и они не должны меняться очень часто. –

+0

@RandomBen: 'SQL Server' кэширует страницы данных, а не результаты. В любом случае необходимо выполнить полную проверку таблицы или индекса, даже если все страницы таблицы (или индекса) будут кэшированы. Это достаточно долго для достаточно большой таблицы. – Quassnoi

5

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

INSERT TypeLookup (Type) 
SELECT DISTINCT Type 
FROM BigMassiveTable 

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

+0

+1 для обработки текущего обслуживания таблицы поиска. Если таблица и таблица поиска имеют «временные метки вкладок», то периодическое задание может быть более эффективным путем проверки только «новых» записей для новых типов сообщений. Кроме того, триггер INSERT на «BigMassiveTable» выполнил бы эту работу без обычного пакетного задания. – pilcrow

+0

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

0

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

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

+1

Вы не можете индексировать представление по запросу с предложением DISTINCT. – Quassnoi

0

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

Что-то вроде

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

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

Из этой конструкции, ваш поиск ДОЛЖЕН только запрос MessageTypes

1

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

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

Делает смысл. +1 Не знаете, кто и почему ниспровергнут. IMO downvotes без объяснений не очень приятно. –

+1

Почему это помечено как спам? –

1

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

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 секунд

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

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

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

0

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

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

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

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

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

Смежные вопросы