【问题标题】:Joining Multiple Tables Based on Two Cross Reference Tables基于两个交叉引用表连接多个表
【发布时间】:2013-05-29 21:30:02
【问题描述】:

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

例如:

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

我觉得这两种方法都非常低效,可以改进。除了重构数据库,还有没有更高效的解决方案?

【问题讨论】:

    标签: mysql sql database join


    【解决方案1】:

    如果您无法重组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 |
    

    【讨论】:

    • 感谢您的详细回复。我现在最终使用了别名,但将来我可能最终会使用它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-17
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    相关资源
    最近更新 更多