2013-05-13 4 views
1

У меня есть 3 таблицы: Mysqlбулевы операции по MySQL результаты

[block_value]

  • id_block_value
  • file_id

[метаданные]

  • id_metadata
  • metadata_name

[metadata_value]

  • meta_id
  • значение
  • blockvalue_id

В этих таблицах Ther е являются пары: metadata_name = value И список пар ставятся в блоках (id_block_value)

(A) Если я хочу высота = 1080:

SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "height" and value = "1080"); 

+---------+ 
| file_id | 
+---------+ 
|  21 | 
|  22 | 
(...) 
| 6962 | 
(...) 
| 8146 | 
| 8147 | 
+---------+ 
794 rows in set (0.06 sec) 

(B) Если я хочу расширение файла = MPEG:

SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "file extension" and value = "mpeg"); 

+---------+ 
| file_id | 
+---------+ 
| 6889 | 
| 6898 | 
| 6962 | 
+---------+ 
3 rows in set (0.06 sec) 

НО, если я хочу:

  • А и В
  • А или В
  • А и Б не

Тогда, я не знаю, что лучше.

Для A or B, я пробовал A union B, который, кажется, делает трюк.

SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "height" and value = "1080") 
UNION 
SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "file extension" and value = "mpeg"); 
+---------+ 
| file_id | 
+---------+ 
|  21 | 
|  22 | 
|  34 | 
(...) 
| 6889 | 
| 6898 | 
+---------+ 
796 rows in set (0.13 sec) 

Для A and B, поскольку у нас нет intersect в Mysql, я попытался A and file_id in(B), но посмотрите на perfs (> 4 млн) ...

SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "height" and value = "1080") 
and file_id in(
SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "file extension" and value = "mpeg")); 

+---------+ 
| file_id | 
+---------+ 
| 6962 | 
+---------+ 
1 row in set (4 min 36.22 sec) 

Я попытался B and file_id in(A) тоже, что намного лучше , но я никогда не узнаю, как это сделать первым.

SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "file extension" and value = "mpeg") 
and file_id in(
SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "height" and value = "1080")); 

+---------+ 
| file_id | 
+---------+ 
| 6962 | 
+---------+ 
1 row in set (0.75 sec) 

Итак ... что мне теперь делать? Есть ли лучший способ для булевых операций? Любой отзыв? Я что-то пропустил?

EDIT: какие данные выглядит следующим образом:

Эта база данных содержит строку в FILE таблице для каждого аудио/видео файла, вставленного:

  • 10, /path/to/file.ts
  • 11, /path/to/file2.mpeg

Существует ряд в METADATA таблице для каждого потенциального информация:

  • 301, высота
  • 302, расширение файла

Затем строка в BLOCK таблицы определяют контейнер:

  • 101, видео
  • 102, аудио
  • 104, General

Файл может иметь несколько блоков метаданных у BLOCK_VALUE таблица содержит примеры блоков:

  • 402, 101, 10 // Видео 1
  • 403, 101, 10 // Видео 2
  • 404, 101, 10 // Видео 3
  • 405, 102, 10 // Аудио
  • 406, 104, 10 // Общая

В этом примере файл 10 имеет 5 блоков: 3 Видео (101) + 1 аудио (102) + 1 General (104)

Значения сохраняются в METADATA_VALUE

  • 302, 406, «TS "// расширение файла, общее
  • 301, 402, "+1080" // высота, видео 1
  • 301, 403, "720" // высота, видео 2
  • 301, 404, "352"//высота, Видео 3

ответ

1

Я открываю новый пост только сохранить «правильное» решение аккуратный ..

Хорошо, извините, казалось, что я делал ошибочное предположение. Я никогда не думал о том, что два блока определяются точно так же.

Так, так как я подражатель, и мне нравится моя получить, что и от OR решения: Оринг, я получил эти два решения ..

(P): Мне нравится решение Криса лучше ...

SELECT DISTINCT file_id 
    FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
    WHERE (metadata_name = "height" and value = "1080") 
     OR (metadata_name = "file extension" and value = "mpeg") 

операция AND: Я буду использовать версию Оринг (один с UNION все

SELECT FILE_ID FROM (
    SELECT DISTINCT 1, file_id 
      FROM metadata_value MV 
     INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
     INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
       WHERE (metadata_name = "height" and value = "1080") 
    UNION ALL 
    SELECT DISTINCT 2, file_id 
      FROM metadata_value MV 
     INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
     INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
       WHERE (metadata_name = "file extension" and value = "mpeg") 
    ) IHATEAND 
    GROUP BY FILE_ID 
    HAVING COUNT(1)>1 

Что дает:

+---------+ 
| FILE_ID | 
+---------+ 
| 6962 | 
+---------+ 
1 row in set (0.24 sec) 

она должна быть немного меньше, чем быстро уплотнительное кольцо видя выполненные вами спектакли и мины (я в 3 раза медленнее, время обновления --.-), но все же значительно быстрее, чем предыдущие запросы;)

В любом случае, как работает ANDing? Положительно просто, он просто выполняет два отдельных запроса и записывает записи в соответствии с ветвью, из которой они происходят, а затем подсчитывает разные идентификаторы файлов, исходящие от них.

ОБНОВЛЕНИЕ: другой способ сделать это, не указывая «имя», ветви:

SELECT FILE_ID FROM (
    SELECT file_id 
     FROM metadata_value MV 
     INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
     INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
      WHERE (metadata_name = "height" and value = "1080") 
    GROUP BY FILE_ID 
    UNION ALL 
    SELECT file_id 
     FROM metadata_value MV 
     INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
     INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
    WHERE (metadata_name = "file extension" and value = "mpeg") 
    GROUP BY FILE_ID 
    ) IHATEAND 
GROUP BY FILE_ID 
HAVING COUNT(1)>1 

Здесь результаты одинаковы (и спектакли, а) и я эксплуатирую тот факт, что в то время как UNION автоматически сортирует дублирующие и удаляет дубликаты, UNION ALL не ... что отлично, потому что я не хочу, чтобы их удаляли (и вообще союз все также быстрее, чем союз :)), таким образом я могу забыть об именовании.

+0

Впечатляющий. Хотел бы я дать вам больше репутаций! Спасибо миллион за ваше время и ваши навыки (и спасибо ChrisCamp, который тоже очень старался) – Cyrille

1

Для «ИЛИ», почему бы не попробовать его без UNION ... я что-то упустил?

SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "height" and value = "1080") 
OR (metadata_name = "file extension" and value = "mpeg") 

Для «И», использовать внутреннее соединение в таблице метаданных дважды, чтобы обеспечить, чтобы получить только file_id-х, которые удовлетворяют обоим условиям ...

SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    AND (M.metadata_name = "height" and MV.value = "1080") 
    INNER JOIN metadata M2 ON MV.meta_id = M2.id_metadata 
    AND (M2.metadata_name = "file extension" and MV.value = "mpeg") 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 

«А», а не «B», используйте левое соединение, а не внутреннее соединение в состоянии «B».Добавить ИНЕК уточнения, что вы не ожидаете никаких результатов для "B"

SELECT DISTINCT file_id 
FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    AND (M.metadata_name = "height" and MV.value = "1080") 
    LEFT JOIN metadata M2 ON MV.meta_id = M2.id_metadata 
    AND (M2.metadata_name = "file extension" and MV.value = "mpeg") 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE M2.id_metadata is NULL 
+2

metadata_name в то же время «высота» и «расширение файла»? (Кстати, вы пытались использовать во втором блоке "и существует (выберите 1 ..." вместо "и идентификатор файла в (select distinct ...)"? – Davide

+0

Действительно, 2 metadata_name в одном запросе возвращает пустой set (Но вы правы для 'OR':' UNION' здесь не требуется). Я попробую 'exists', как только я выясню правильный синтаксис. – Cyrille

+1

Хорошая точка Davide, как насчет двойного соединения в таблице метаданных указав, что существуют как условия A, так и B? Модифицированный мой ответ ... – ChrisCamp

1

или версии: (бесстыдного копирования и вставка из ответа ChrisCamp в)

SELECT distinct file_id 
    FROM metadata_value MV 
     INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
     INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
WHERE (metadata_name = "height" and value = "1080") 
    OR (metadata_name = "file extension" and value = "mpeg") 

И Версия:

SELECT file_id 
    FROM metadata_value MV 
    INNER JOIN metadata M ON MV.meta_id = M.id_metadata 
    INNER JOIN block_value BV ON MV.blockvalue_id = BV.id_block_value 
    WHERE (metadata_name = "height" and value = "1080") 
     OR (metadata_name = "file extension" and value = "mpeg") 
    group by file_id having count(1)>1 

2 быстрых заметок о версии И:

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

Когда ANDind у вас есть 3 возможности:

  • ни один из запрошенных условий не удовлетворен (в ORing не появится)
  • только один из они удовлетворяются (в ORing он появится один раз)
  • оба выполнены (в ORing он будет отображаться дважды, если отдельный не указан).

Поэтому я просто удалил отдельное предложение, поместил группу и выбрал записи, присутствующие дважды.

Или просто продолжать использовать такое положение :)


Редактировать следующие комментарии:

нормально, пытаясь держать вещи простыми ... id_block_values, удовлетворяющие одному из двух условий:

SELECT BLOCK_VALUE_ID 
    FROM METADATA_VALUE MV 
    INNER JOIN 
     METADATA M 
    ON MV.META_ID=M.METADATA_ID 
    WHERE (METADATA_NAME='height' AND VALUE='1080') 
    OR (METADATA_NAME='file extension' AND VALUE='mpeg') 

Если у вас более двух записей здесь, у вас есть проблема (дублирование метаданных).

Теперь операция AND

SELECT FILE_ID 
    FROM BLOCK_VALUE BV 
    INNER JOIN 
     ( SELECT BLOCK_VALUE_ID 
      FROM METADATA_VALUE MV 
      INNER JOIN 
       METADATA M 
       ON MV.META_ID=M.METADATA_ID 
      WHERE (METADATA_NAME='height' AND VALUE='1080') 
       OR (METADATA_NAME='file extension' AND VALUE='mpeg') 
    ) X 
    ON BV.ID_BLOCK_VALUE=X.BLOCK_VALUE_ID 
GROUP BY FILE_ID HAVING COUNT(1)>1 

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

+0

Ну, с версией AND я получаю: 12 строк в наборе (0,00 сек). Очень быстро! Но набор неправильный (он содержит 6962, хотя). – Cyrille

+0

(file_id | count (1)): (148 | 3) (154 | 3) (155 | 4) (169 | 6) (317 | 2) (332 | 3) (333 | 3) (486 | 4) (487 | 4) (490 | 4) (525 | 3) (6962 | 2) – Cyrille

+1

Хорошо, спасибо за информацию, я обновил свой ответ. Вы уверены, что у вас нет дублирования метаданных, не так ли? – Davide

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