【问题标题】:Boolean operations on mysql results对mysql结果的布尔运算
【发布时间】:2013-05-13 14:24:20
【问题描述】:

我有 3 个 Mysql 表:

[block_value]

  • id_block_value
  • file_id

[元数据]

  • 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 或 B
  • 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

【问题讨论】:

    标签: mysql sql merge intersect booleanquery


    【解决方案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 快:)),这样我就可以忘记命名了。

    【讨论】:

    • 令人印象深刻。我希望我能给你更多的声望点!感谢您的时间和技能(感谢 ChrisCamp 也很努力)
    【解决方案2】:

    对于“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
    

    【讨论】:

    • metadata_name 同时是“高度”和“文件扩展名”? (顺便说一句,您是否尝试过在第二个块中使用“and exists(select 1 ...”而不是“and file id in(select distinct...)”?
    • 确实,同一查询中的 2 个 metadata_name 返回一个空集(但您对 OR 是正确的:这里不需要 UNION)。一旦我找出正确的语法,我会尝试exists
    • 好点 Davide,元数据表上的双连接指定 A 和 B 条件都存在怎么样?修改了我的答案...
    • 不用担心,我用正确的 MV 别名修改了答案。这会让您更接近解决方案吗?
    • 您可以在问题中提供任何示例数据吗?
    【解决方案3】:

    或版本: (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") 
    

    AND 版本:

    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 的方法..

    当你发现你有 3 种可能性时:

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

    所以我只是删除了 distinct 子句,放入 group by,然后选择出现两次的记录。

    或者继续使用exists子句:)


    编辑以下 cmets:

    好的,尽量保持简单... 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 子句,你会看到一些记录超过两次,这是没有意义的。 顺便说一句,为了确定,你能告诉我这些表的主键是什么吗?

    【讨论】:

    • 好吧,使用 AND 版本,我得到:12 行集合(0.00 秒)。速度很快!但是集合是错误的(虽然它包含 6962)。
    • 好的,谢谢你的信息,我更新了我的答案。你确定你没有元数据重复,对吧?
    • mmmmh...这很奇怪...您能否运行此查询,以便我查看重复发生的位置? SELECT * 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 AND FILE_ID=169
    • 假设这些块具有相同的高度值是否正确?
    • 我能想到的是,对于同一个文件,您有两个具有相同高度的不同块(例如图像和视频)(查询中缺少 BLOCK 表)...最后一个查询,我保证。 .. SELECT * FROM METADATA_VALUE MV INNER JOIN BLOCK_VALUE BV ON BV.ID_BLOCK_VALUE=MV.BLOCK_VALUE_ID INNER JOIN METADATA M ON MV.META_ID=M.METADATA_ID WHERE ((METADATA_NAME='height' AND VALUE='1080') OR (METADATA_NAME= '文件扩展名' AND VALUE='mpeg')) AND BLOCK_VALUE_ID IN (2228,2240,2244,2255,2259,2271) 和 file_id=169
    猜你喜欢
    • 2014-10-14
    • 1970-01-01
    • 2011-09-05
    • 2017-09-28
    • 1970-01-01
    • 2020-03-22
    • 2015-08-22
    • 2015-06-17
    • 2020-08-22
    相关资源
    最近更新 更多