2015-05-12 2 views
1

У меня есть таблица с 3 столбцами:Наиболее производительный SQL

item_id (decimal), key_name (varchar), key_string_value (varchar) 

Он населен сотнями и тысячами строк. Ниже приведены первые 6 строк, которые дают вам представление о данных.

1. 1    product    product1 
2. 1    topic    topic1 
3. 1    segment    segment1 
4. 2    product    product2 
5. 2    topic    topic1 
6. 2    segment    segment1 

Каждый отдельный item_id имеет 3 строки, связанную с ним, и описывающей его метаданными, как тема, продукт, сегмент. Я пытаюсь написать SQL-запрос, который делает следующее и работает очень хорошо.

Теперь мне нужно, чтобы выбрать все item_ids, который будет достаточно определенное сочетание этих метаданных, например:

  • пойми меня все item_id «s, для которых topic='topic1' и product='product2' и segment='Segment1'

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

+3

Это зависит от используемой СУБД - то, что оптимально для MySQL, может быть не оптимальным для MSSQL и т. Д. Кроме того, важно знать, какие индексы существуют, и если они кластеризованы. Можете быть более конкретными? –

+0

Есть ли в вашей таблице первичный ключ? Есть ли у него какие-либо (другие) индексы? Являются ли 'key_string_value' для одного и того же ключевого имени в основном по-разному или наиболее привлекательны из небольшого числа альтернатив? –

+0

@Eugen: Мы закончим использование Oracle для этого. Пока мы тестируем MySQL. Существует первичный ключ, который является просто столбцом с номером autonumbered. Существует составной индекс на key_value и key_string_value. – Forexlead

ответ

1

Вы можете использовать HAVING и условную агрегацию для этого:

SELECT item_id 
FROM YourTable 
GROUP BY item_id 
HAVING MAX(CASE WHEN key_name = 'topic' AND key_string_value ='topic1' THEN 1 END) = 1 
    AND MAX(CASE WHEN key_name = 'product' AND key_string_value ='product2' THEN 1 END) = 1 
    AND MAX(CASE WHEN key_name = 'segment' AND key_string_value ='segment1' THEN 1 END) = 1 

Это самое производительное? Тебе придется протестировать, но я ожидаю, что он будет бить самосоединения и коррелированные подзапросы.

+0

Запрос выполнялся как шарм и возвращал результат через 0,5 секунды. Я продолжу работать над этим, но это твердое начало в направлении, которое я искал. Спасибо. – Forexlead

+0

@Forexlead Мое удовольствие, не забудьте также проверить план запроса для предложений индекса. Я не думаю, что вы найдете более быстрый метод, но вы можете улучшить производительность этого метода путем индексации. –

+0

Привет, Харт, Теперь, только если я могу сделать это немного сложнее. Предположим, что я должен запрашивать только по теме и продукту, но тогда вам нужно заказать по сегменту и другому key_name, как я могу это сделать? Поэтому давайте скажем, что таблица имеет другое ключевое имя для каждой item_id, называемой коллекцией. – Forexlead

1

Корреляционные подзапросы, как правило, относятся к наименее эффективному решению любой проблемы, если только БД не может преобразовать их в соединения, поскольку некоторые планировщики запросов могут выполнять в некоторых случаях. Однако в любом процессе оптимизации целесообразно проверить и сравнить планы, которые БД на самом деле придумала для разных запросов и разных условий, используя механизм БД для этого (например, EXPLAIN PLAN в некоторых базах данных).

Мой первый проход в этой задаче будет принимать эту форму:

SELECT product.item_id 
FROM 
    (
    SELECT item_id 
    FROM my_table 
    WHERE key_name = 'product' AND key_string_value = 'product2' 
) product 
    JOIN (
    SELECT item_id 
    FROM my_table 
    WHERE key_name = 'topic' AND key_string_value = 'topic1' 
) topic 
    ON product.item_id = topic.item_id 
    JOIN (
    SELECT item_id 
    FROM my_table 
    WHERE key_name = 'segment' AND key_string_value = 'segment1' 
) segment 
    ON topic.item_id = segment.item_id 

Это предполагает, что таблица имеет ограничение первичного ключа - или, по крайней мере, ограничение уникальности - на (item_id, key_name); если нет, то встроенные представления должны использовать SELECT DISTINCT. Кроме того, он может значительно выиграть от индекса на (key_name), или даже больше от индекса на (key_name, key_string_value).

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

Update: Учитывая, что мы теперь найти действительно есть индекс (key_name, key_string_value), и что большое разнообразие key_string_value с делает такой показатель весьма избирательно, я склонен думать, что вышеописанный подход будет делать достаточно Что ж. При тестировании не забудьте указать SELECT DISTINCT, если вы не можете иначе полагаться на встроенные виды, чтобы избежать дублирования item_id.

+0

Запрос работает, но требуется около 25 секунд для возврата. Справедливости ради, мне все еще нужно добавить ограничение первичного ключа. У меня уже есть составной индекс на (key_name, key_string_value) на месте. Завтра я проведу тест после добавления ограничения. Вернется завтра. – Forexlead

+0

У меня не может быть PK на составной части item_id и key_name, так как может быть несколько строк с одинаковыми значениями item_id и key_name, имеющими разные значения в key_string_value. Тем не менее, у меня есть все индексы. Я буду продолжать изучать, есть ли способы сделать это более совершенным. – Forexlead

0

Прежде всего: я не думаю, что у этого есть хорошо работающее решение без индекса на item_id, поэтому я думаю, что вы должны сменить свой ПК на композит (item_id, key_name) - вам все равно нужно быть уникальным.

Предполагая, что вы сделали это, у нас есть быстрый путь к item_id и key_name, которые я считаю необходимостью.

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

SELECT 
    one.item_id 
FROM table_name AS one 
INNER JOIN table_name AS two 
    ON two.item_id=one.item_id 
INNER JOIN table_name AS three 
    ON three.item_id=one.item_id 
WHERE one.key_name='product' 
AND one.key_string_value='product1' 
AND two.key_name='topic' 
AND two.key_string_value='topic1' 
AND three.key_name='segment' 
AND three.key_string_value='segment1' 
; 

Важно применять селектор с самой высокой селективностью к вождению Таблица. то есть one - в моем запросе я предположил, что product имеет более высокую селективность, чем topic или segment.

+0

У меня уже есть указатель на item_id. Я не могу иметь PK на составной части item_id и key_name, так как может быть несколько строк с одинаковыми значениями item_id и key_name, имеющими разные значения в key_string_value.Пока что ИБП и условная агрегация кажутся наиболее эффективными. – Forexlead

+0

@Forexlead вы должны обновить свой вопрос, чтобы упомянуть, что может быть более одного значения для пары item_id/key_name - это смена игры! –

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