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

我想选择第一行中任何一个foo1foo2匹配foo3这些列中的任何一个的所有行。也就是说,我想要至少一个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

有几种方法可以获取结果集。

这是一种方法,(如果您不关心哪个 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 回答