基于两个交叉引用表连接多个表

时间:2013-05-29 21:30:02

标签: mysql sql database join

我有一个数据库(MySQL 5.1),它使用交叉引用表(下例中的local_ref)来获取值的数字ID。我已经介绍了另一个交叉引用表(foreign_ref,下面),以将这些数字ID引用到另一个数据库中的索引。通常这不是一个复杂的连接,但是,我有多个列使用交叉引用表中的键(val1和val2,下面)。

E.g:

mysql> select * from foo;
+-----+------+------+
| id  | val1 | val2 |
+-----+------+------+
| 100 | A    | B    |
| 200 | A    | D    |
| 300 | B    | C    |
+-----+------+------+

mysql> select * from local_ref;
+----+-------+
| id | value |
+----+-------+
|  1 | A     |
|  2 | B     |
|  3 | C     |
|  4 | D     |
|  5 | E     |
+----+-------+

mysql> select * from foreign_ref;
+----------+------------+
| local_id | foreign_id |
+----------+------------+
|        1 |         10 |
|        2 |         20 |
|        3 |         30 |
|        4 |         40 |
+----------+------------+

我需要的是以下内容:

+-----+---------+---------+
| id  | val1_id | val2_id |
+-----+---------+---------+
| 100 | 10      | 20      |
| 200 | 10      | 40      |
| 300 | 20      | 30      |
+-----+---------+---------+

知道原始表没有按原样进行规范化,我通过以下两种方式实现了结果:

将两个交叉引用表混淆两次:

SELECT
FOO.id, F_R1.foreign_id, F_R2.foreign_id
FROM FOO 
JOIN
Local_Ref as L_R1 ON (FOO.val1 = L_R1.value)
JOIN
Local_Ref as L_R2 ON (FOO.val2 = L_R2.value)
JOIN
Foreign_Ref as F_R1 ON (L_R1.id = F_R1.local_id)
JOIN
Foreign_Ref as F_R2 ON (L_R2.id = F_R2.local_id)

两次连接交叉引用表并为每个连接设置别名。

SELECT
FOO.id, joint1.foreign_id, joint2.foreign_id
FROM
FOO
JOIN
(
SELECT * FROM Local_Ref JOIN Foreign_Ref ON Local_Ref.id = Foreign_Ref.local_id
) as joint1
ON FOO.val1 = joint1.value
JOIN
(
SELECT * FROM Local_Ref JOIN Foreign_Ref ON Local_Ref.id = Foreign_Ref.local_id
) as joint2
ON FOO.val2 = joint2.value

我觉得这两种方法效率都很低,可以改进。除了重建数据库,还有更有效的解决方案吗?

1 个答案:

答案 0 :(得分:0)

如果无法重构foo表,那么获得结果的另一种方法是取消忽略该表中的数据。

MySQL没有unpivot功能,但您使用了UNION ALL查询。

基本语法为:

select id, 'val1' col, val1 value
from foo
union all
select id, 'val2' col, val2 value
from foo

请参阅Demo

这将把具有多列的去规范化表转换为多行,这样更容易加入。一旦数据采用这种格式,那么您可以连接到其他表一次,而不是两次。最后,您可以应用带有CASE表达式的聚合函数将值转换为列:

select f.id,
  max(case when f.col = 'val1' then fr.foreign_id end) val1_id,
  max(case when f.col = 'val2' then fr.foreign_id end) val2_id
from
(
  select id, 'val1' col, val1 value
  from foo
  union all
  select id, 'val2' col, val2 value
  from foo
) f
inner join local_ref l
  on f.value = l.value
inner join foreign_ref fr
  on l.id = fr.local_id
group by f.id

SQL Fiddle with Demo。这给出了一个结果:

|  ID | VAL1_ID | VAL2_ID |
---------------------------
| 100 |      10 |      20 |
| 200 |      10 |      40 |
| 300 |      20 |      30 |