1

という名前の次のテーブルがあるとしますdata

ID   foo1     foo2    foo3
1    11       22      33
2    22       17      92
3    31       33      53
4    53       22      11
5    43       23      9

foo1、または最初の行のこれらの列のいずれかに一致foo2するすべての行を選択したいと思います。foo3つまり、少なくとも 1 つのfoos が最初の行にも表示されるすべての行が必要です。上記の例では、行 1、2、3、4 を選択したいのですが、次のようなものを使用できると思いました

SELECT * FROM data WHERE foo1 IN (SELECT foo1,foo2,foo3 FROM data WHERE ID=1)
                      OR foo2 IN (SELECT foo1,foo2,foo3 FROM data WHERE ID=1)
                      OR foo3 IN (SELECT foo1,foo2,foo3 FROM data WHERE ID=1)

しかし、これはうまくいかないようです。もちろん使えます

WHERE foo1=(SELECT foo1 FROM data WHERE ID=1) 
   OR foo1=(SELECT foo2 FROM data WHERE ID=1) 
   OR ...

しかし、それには多くの行が含まれます。私の実際のデータセットには実際には 16 列あるので、本当に腰が痛くなります。そうするためのより洗練された方法はありますか?

また、ヒット数もカウントしたい場合はどうすればよいですか (上記の例では、1 行目で 4、4 行目で 2、2,3 行目で 1 となります)。

4

2 に答える 2

3
SELECT data.*,
      (data.foo1 IN (t.foo1, t.foo2, t.foo3))
    + (data.foo2 IN (t.foo1, t.foo2, t.foo3))
    + (data.foo3 IN (t.foo1, t.foo2, t.foo3)) AS number_of_hits
FROM   data JOIN data t ON t.id = 1
WHERE  data.foo1 IN (t.foo1, t.foo2, t.foo3)
    OR data.foo2 IN (t.foo1, t.foo2, t.foo3)
    OR data.foo3 IN (t.foo1, t.foo2, t.foo3)

sqlfiddleで参照してください。

実際、振り返ってみると、データを正規化することを検討できます。

CREATE TABLE data_new (
  ID         BIGINT  UNSIGNED NOT NULL,
  foo_number TINYINT UNSIGNED NOT NULL,
  val        INT,
  PRIMARY KEY (ID, foo_number),
  INDEX (val)
);

INSERT INTO data_new
  (ID, foo_number, val)
          SELECT ID, 1, foo1 FROM data
UNION ALL SELECT ID, 2, foo2 FROM data
UNION ALL SELECT ID, 3, foo3 FROM data;

DROP TABLE data;

次に、次のことができます。

SELECT   ID,
         MAX(IF(foo_number=1,val,NULL)) AS foo1,
         MAX(IF(foo_number=2,val,NULL)) AS foo2,
         MAX(IF(foo_number=3,val,NULL)) AS foo3,
         number_of_hits
FROM     data_new JOIN (
  SELECT   d1.ID, COUNT(*) AS number_of_hits
  FROM     data_new d1 JOIN data_new d2 USING (val)
  WHERE    d2.ID = 1
  GROUP BY d1.ID
) t USING (ID)
GROUP BY ID

sqlfiddleで参照してください。

実行計画からわかるように、これは大規模なデータ セットに対してかなり効率的です。

于 2012-12-18T23:34:59.503 に答える
1

結果セットを取得するには、いくつかの方法があります。

これが1つのアプローチです(fooNと一致するfooNが気にならず、その「最初の」行も返したい場合)。

SELECT DISTINCT d.* 
  JOIN ( SELECT foo1 AS foo FROM data WHERE id = 1
          UNION ALL
         SELECT foo2 FROM data WHERE id = 1
          UNION ALL
         SELECT foo3 FROM data WHERE id = 1
       ) f
  JOIN data d
    ON  f.foo IN (d.foo1, d.foo2, d.foo3)

その ON 句は、次のように書くこともできます。

    ON d.foo1 = f.foo
    OR d.foo2 = f.foo
    OR d.foo2 = f.foo

ヒットの「カウント」を取得するには...

SELECT d.id
     , d.foo1
     , d.foo2
     , d.foo3
     , SUM( IFNULL(d.foo1=f.foo,0)
           +IFNULL(d.foo2=f.foo,0)
           +IFNULL(d.foo3=f.foo,0)
       ) AS count_of_hits
  JOIN ( SELECT foo1 AS foo FROM data WHERE id = 1
          UNION ALL
         SELECT foo2 FROM data WHERE id = 1
          UNION ALL
         SELECT foo3 FROM data WHERE id = 1
       ) f
  JOIN data d
    ON  f.foo IN (d.foo1, d.foo2, d.foo3)
 GROUP
    BY d.id
     , d.foo1
     , d.foo2
     , d.foo3


いつものようにeggyalは正しいです。ヒット数の取得は、実際にははるかに簡単です。SUM(1) または COUNT(1) 集計を使用するだけでよく、これらすべての比較を実行する必要はありません。必要なすべての比較は既に実行済みです。

SELECT d.id
     , d.foo1
     , d.foo2
     , d.foo3
     , COUNT(1) AS count_of_hits
  JOIN ( SELECT foo1 AS foo FROM data WHERE id = 1
          UNION ALL
         SELECT foo2 FROM data WHERE id = 1
          UNION ALL
         SELECT foo3 FROM data WHERE id = 1
       ) f
  JOIN data d
    ON  f.foo IN (d.foo1, d.foo2, d.foo3)
 GROUP
    BY d.id
     , d.foo1
     , d.foo2
     , d.foo3
于 2012-12-18T23:42:55.050 に答える