【问题标题】:How to combine results from multiple tables with different columns?如何将多个表的结果与不同的列组合起来?
【发布时间】:2011-04-30 22:39:49
【问题描述】:

我有几张表,它们的列数和类型各不相同,并且有一个共同的列。

+--------+---------+------------+-------------+
| person | beardID | beardStyle | beardLength |
+--------+---------+------------+-------------+

+--------+-------------+----------------+
| person | moustacheID | moustacheStyle |
+--------+-------------+----------------+

我想获取与共享列的给定值匹配的所有结果。我可以使用多个这样的选择语句来做到这一点:

SELECT * FROM beards WHERE person = "bob"

SELECT * FROM moustaches WHERE person = "bob"

但这需要多次mysql API调用,看起来效率低下。我希望我可以使用 UNION ALL 在单个 API 调用中获取所有结果,但 UNION 要求表具有相同数量和相似类型的列。我可以编写一个 SELECT 语句,通过添加具有 NULL 值的列来手动填充每个表的结果,但是对于具有更多列的更多表来说,这将很快变得难以管理。

我正在寻找一个大致像这样的结果集:

+--------+---------+------------+-------------+-------------+----------------+
| person | beardID | beardStyle | beardLength | moustacheID | moustacheStyle |
+--------+---------+------------+-------------+-------------+----------------+
| bob    | 1       | rasputin   | 1           |             |                |
+--------+---------+------------+-------------+-------------+----------------+
| bob    | 2       | samson     | 12          |             |                |
+--------+---------+------------+-------------+-------------+----------------+
| bob    |         |            |             | 1           | fu manchu      |
+--------+---------+------------+-------------+-------------+----------------+

有没有一种快速且可维护的方法来实现这一目标?还是我最好为每个表运行单独的查询?

澄清:

我不是在寻找笛卡尔积。我不希望胡须和小胡子的每种组合都排成一行,我希望每个胡须都排成一行,每个小胡子都排成一行。

因此,如果有 3 个相配的胡须和 2 个相配的胡须,我应该得到 5 行,而不是 6 行。

【问题讨论】:

  • 虽然它可以在大多数数据库中使用FULL JOIN 完成,并且在 MySQL 中使用 ITroubs 的解决方法,但我严重怀疑这是否会带来任何显着的性能提升,而你是可能只会创建一堆你的继任者不会感谢你的晦涩代码。在大多数情况下,在非常低的百分比下,清晰准确的代码胜过性能提升。

标签: php mysql join union


【解决方案1】:

这应该可以正常工作:

SELECT * FROM `beards` b LEFT OUTER JOIN `mustaches` ON (0) WHERE  person = "bob"
UNION ALL
SELECT * FROM `beards` b RIGHT OUTER JOIN `mustaches` ON (0) WHERE  person = "bob"

您不必自己处理列。左右外连接完成这项工作。 不幸的是 mysql 没有完全连接。这就是为什么你必须通过工会这样做

SELECT * FROM `customer` b LEFT OUTER JOIN `charges` ON (0) LEFT OUTER JOIN `day` ON (0)
UNION
SELECT * FROM `customer` b RIGHT OUTER JOIN `charges` ON (0) LEFT OUTER JOIN `day` ON (0)
UNION
SELECT * FROM `customer` b LEFT OUTER JOIN `charges` ON (0) RIGHT OUTER JOIN `day` ON (0)

这是我做的本地测试

【讨论】:

  • 很棒,就像一个魅力。因此,如果我理解正确,您使用 LEFT 和 RIGHT OUTER JOIN 而不指定连接条件以将充满 NULL 的列添加到每个表的结果中(以便结果集具有匹配的表),然后对结果进行 UNION。
  • 是的。联接只是为了使您的结果具有胡须和胡须的所有列,并且通过使 ON 语句为 false 它仅返回胡须或胡须中的所有行,具体取决于所使用的联接
  • 所以要添加第三个表,我会为新表的每个 SELECT 添加一个额外的 OUTER JOIN ON (0),再加上 UNION ALL 和一个新的选择语句 OUTER JOINing the new table to those两张表以同样的方式。
  • 刚刚看到你说的正是我所说的XD
  • 我在我的解决方案中添加了一个三表示例
【解决方案2】:

我认为查询每个表中的数据会更好。

另一种可能性是将所有列中的数据连接成一个大字符串(您可以选择一些符号来分隔列的值),然后您应该能够使用 union all 子句来组合每个查询的结果 - 但是您将不得不解析每一行。并且数据类型将会丢失。

【讨论】:

  • 主要缺点是计算开销和 mysql 对 group_concat 的字符串大小有限!看看我的解决方案。我认为这将是最简单的方法。
  • @ITroubs:我应该说很好的解决方案
【解决方案3】:

我对此感到很开心,不确定是否完全可以添加更多内容,但它实现了目标。

create table beard (
person varchar(20)
,beardID int
,beardStyle varchar(20)
,beardLength int )

create table moustache(
person varchar(20)
,moustacheID int
,moustacheStyle varchar(20))


insert into beard 
select 'bob', 1, 'rasputin', 1
union select 'bob', 2, 'samson', 12

insert into moustache
select 'bob', 1, 'fu manchu'

declare @facialhair table (
person varchar(20)
,beardID int
,beardStyle varchar(20)
,beardLength int
,moustacheID int
,moustacheStyle varchar(20))

declare @i int
declare @name varchar(20)

set @name = 'bob'
set @i = (select COUNT(*) from beard where person = @name)
        + (select COUNT(*) from moustache where person = @name) 

print @i

while @i > 0
    begin 
        insert into @facialhair (person, beardID, beardStyle, beardLength)
        select person, beardID, beardStyle, beardLength
        from beard
        where person = @name
    set @i = @i-@@ROWCOUNT

        insert into @facialhair (person, moustacheID, moustacheStyle)
        select person, moustacheID, moustacheStyle
        from moustache
        where person = @name
    set @i = @i-@@ROWCOUNT
    end

select *
from @facialhair

【讨论】:

    【解决方案4】:
    SELECT *
    FROM   beards
           JOIN moustaches
             ON moustaches.person = beards.person
    WHERE  person = "bob"  
    

    【讨论】:

    • 我希望为每个胡须检索一行,为每个小胡子检索一行,而不是为每个胡须和小胡子检索一行。
    【解决方案5】:

    加入个人....

    选择 t1.(星号),t2.(星号) 从 胡须 t1 内部联接 小胡子 t2 On t2.person = t1.person

    【讨论】:

    • 你试过了吗? INNER JOIN 从两个表中检索匹配记录的笛卡尔积。因此,胡须和小胡子的每种组合都会产生一个结果。根本不是问题中描述的内容。
    • @Robert:不,INNER JOIN 不会产生笛卡尔积。
    • 对不起,我不太了解 JOIN。 INNER JOIN 与采用笛卡尔积和过滤连接条件有什么区别?
    • INNER JOIN 选择选择关系 A 中的所有记录和关系 B 中具有共同值的所有记录,并输出包含这些行的结果集。笛卡尔积是创建一个结果集的结果,其中关系 A 和 B 中的所有行都没有押韵或理由地拼接在一起。 IE。对于关系 A 中的每一行,关系 B 中的每一行都被添加到结果集中。
    • 那不就是 INNER JOIN 的作用吗?对于关系 A 中的每一行,关系 B 中的每一行都添加到结果集中,然后过滤该集合,以便只返回具有共同值的行。如果有区别,我仍然没有看到。
    猜你喜欢
    • 1970-01-01
    • 2020-03-25
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-27
    • 1970-01-01
    相关资源
    最近更新 更多