1

我有 3 个 Mysql 表:

[块值]

  • id_block_value
  • 文件标识

[元数据]

  • id_metadata
  • 元数据名称

[元数据值]

  • meta_id
  • 价值
  • blockvalue_id

在这些表中,有对: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而不是B

然后,我不知道什么是最好的。

对于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,由于Mysql中没有intersect,我试过了A and file_id in(B),但是看看perfs(> 4mn)......

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)

那么……我现在该怎么办?布尔运算有更好的方法吗?任何提示?我错过了什么?

编辑:什么数据看起来像:

该数据库在FILE表中包含插入的每个音频/视频文件的一行:

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

METADATA每个潜在信息在表中都有一行:

  • 301,身高
  • 302,文件扩展名

然后,BLOCK表中的一行定义了一个容器:

  • 101、视频
  • 102、音频
  • 104、一般

一个文件可以有多个元数据块,一个BLOCK_VALUE表包含 BLOCKS 的实例:

  • 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 个常规 (104)

值存储在METADATA_VALUE

  • 302, 406, "ts" // 文件扩展名,通用
  • 301, 402, "1080" // 高度,视频 1
  • 301, 403, "720" // 高度,视频 2
  • 301, 404, "352" // 高度,视频 3
4

3 回答 3

1

对于“OR”,为什么不尝试不使用 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")

对于“AND”,在元数据表上使用两次内连接,以确保仅获得满足这两个条件的 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 

“A”而不是“B”,在“B”条件下使用左连接而不是内连接。添加一个 WHERE 子句,指定您不希望“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
于 2013-05-13T14:36:54.360 回答
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

关于 AND 版本的 2 个快速说明:

这实际上是一种根据之前的 ORing 定义该 Intersection 的方法。

当 ANDind 你有 3 种可能性:

  • 不满足任何请求的条件(在 ORing 中不会出现)
  • 只有其中一个是满意的(在 ORing 中它会出现一次)
  • 两者都满足(在 ORing 中,如果没有指定 distinct,它会出现两次)。

所以我只是删除了 distinct 子句,放置一个 group by,并选择存在两次的记录。

或者只是继续使用存在子句:)


编辑以下评论:

好的,尽量保持简单... 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')

如果您在这里有超过 2 条记录,那么您有问题(元数据重复)。

现在 ANDing

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

尽管如此,我还是不明白为什么前面的查询不起作用。我担心如果你也删除 or 查询中的 DIstinct 子句,你会看到一些记录超过两次,这是没有意义的。顺便说一句,为了确定,你能告诉我这些表的主键是什么吗?

于 2013-05-13T14:52:49.473 回答
1

我打开一个新帖子只是为了保持“正确”解决方案的整洁..

好的,对不起,我似乎做出了错误的假设。我从没想过以完全相同的方式定义两个块。

所以,因为我是一个模仿者,我喜欢从 OR 解决方案 (:P) 中得到 AND,我得到了这两个解决方案。

ORing:我更喜欢 Chris 的解决方案……

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")

ANDing:我将使用您的 ORing 版本(带有 UNION all 的版本)

  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)

它应该比看到你粘贴和挖掘的性能的 ORing 快一点(我慢了 3 倍,升级时间-.-),但仍然比以前的查询快得多;)

无论如何,ANDing 是如何工作的?简而言之,它只是执行两个单独的查询并根据它们来自的分支命名记录,然后计算来自它们的不同文件 id

更新:另一种无需“命名”分支的方法:

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 不会......这是完美的,因为我不希望它们被删除(一般来说 union all 也比 union 快 :) ),这样我就可以忘记命名了。

于 2013-05-14T18:21:59.910 回答