1

私は3つのMysqlテーブルを持っています:

[ブロック値]

  • id_block_value
  • file_id

[メタデータ]

  • id_metadata
  • メタデータ名

[メタデータ値]

  • メタ 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
  • BではなくA

じゃあ何が一番いいのかわからない。

について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

「正しい」ソリューションを整頓するためだけに新しい投稿を開いています..

わかりました、申し訳ありませんが、私は間違った仮定をしていたようです。2 つのブロックがまったく同じように定義されているとは考えもしませんでした。

だから、私は模倣者であり、OR ソリューションから AND を取得するのが好きなので (:P)、これら 2 つのソリューションにたどり着きました..

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 はどのように機能しますか? 簡単に言えば、2 つの別々のクエリを実行し、レコードのブランチに応じてレコードに名前を付け、それらからのさまざまなファイル 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 に答える
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」の場合、メタデータ テーブルで内部結合を 2 回使用して、両方の条件を満たす 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」ではなく「A」、「B」条件で内部結合ではなく左結合を使用します。"B" の結果を期待しないことを指定する WHERE 句を追加します。

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

OR バージョン: (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 の観点からその交差点を定義する方法です..

ANDind の場合、次の 3 つの可能性があります。

  • 要求された条件のいずれも満たされていない (ORing では表示されない)
  • そのうちの 1 つだけが満たされます (ORing では 1 回表示されます)。
  • 両方が満たされます (ORing では、distinct が指定されていない場合、2 回表示されます)。

したがって、distinct 句を削除し、group by を配置して、存在するレコードを 2 回選択しました。

または、exists句を使い続けるだけです:)


次のコメントを編集します。

わかりました、物事を単純にしようとしています... id_block_values は次の 2 つの条件のいずれかを満たしています。

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 句も削除すると、一部のレコードが 2 回以上表示されるのではないかと心配していますが、これは意味がありません。念のため、テーブルの主キーを教えてください。

于 2013-05-13T14:52:49.473 に答える