用于合并来自多个表的数据的SQL查询,这些表可能有也可能没有匹配的行?

时间:2013-02-25 18:32:19

标签: sql-server tsql outer-join

例如,假设我们正在进行研究,学生可以进行多达10项不同的测试,数据库中的每个表都存储了所有学生对一次测试的反应。每个测试后的表格命名为:T1,T2,...,T10。假设每个表都有一个主键列“用户名”,用于标识每个学生。学生可能已经或可能没有完成每项考试,因此每个学生可能会或可能没有每个学生的记录。

从所有表中返回所有测试数据的正确SQL查询是什么,每个学生一行(每个用户一行)?我希望最简单的查询可以返回正确的结果。我还想将用户名字段合并到最终查询中的单个用户名字段中。

为了澄清,我理解SQL有一个主要限制,因为它不支持选择所有列的语法,除了一个或多个字段,如“select * [^ ExcludeColumn1] [^ ExcludeColumn2] ”。为了避免在最终查询中专门命名所有列,可以将所有Username列保留在那里,只要它在开头包含一个名为RowID的合并用户名字段。

对于整体查询,一个选项是在所有十个表的用户名列上执行union all,然后在所有表中选择不同的用户名,然后对不同用户名列表执行一系列左连接。全部10张桌子。这将导致非常简单的查询,其中每个左连接在相同的不同用户名集上执行,但我想避免对不同的用户名进行单独的前期查询。 (虽然这是最好的选择,但请告诉我)。它看起来像这样:

select * from
(select distinct coalesce(t1.Username,t2.Username,...,t10.Username) as RowID from t1,t2,t3,t4,t5,t6,t7,t8,t9,t10) distinct_usernames
left join t1 on t1.Username =  distinct_usernames.RowID
left join t2 on t2.Username =  distinct_usernames.RowID
...
left join t10 on t10.Username =  distinct_usernames.RowID

虽然这很简单且易于编写,但效率极低,并且需要花费数小时才能在每行5000多行的测试表上运行,因此通过调整,在几秒钟内运行的等效版本是:

select * from (
select distinct Username as RowID from (
select Username from t1
union all
select Username from t2
union all
...
select Username from t10
) all_usernames) distinct_usernames
left join t1 on t1.Username = distinct_usernames.RowID
left join t2 on t2.Username = distinct_usernames.RowID
...
left join t10 on t10.Username = distinct_usernames.RowID

我认为我上面的内容可能是最有效和最正确的查询(只需几秒钟就可以运行并返回正确的结果集),但我也想过也许可以通过某种完全连接来简化它。问题是完全连接会使两个以上的表混淆,因为没有预先确定用户名,每个后续表必须匹配前面表中任何的记录,从而产生一个查询附加表在匹配用户名时有“[previous table count] + 1”条件。

1 个答案:

答案 0 :(得分:2)

假设Username在每个表格中都是唯一的,您的第二个查询将是我首先尝试的方式,稍微修改一下,删除distinct并简单地使用union(这意味着不是union all

select *
from (
        select Username from t1
        union
        select Username from t2
        union
        -- ...
        select Username from t10
    ) distinct_usernames
    left join t1 on t1.Username = distinct_usernames.Username
    left join t2 on t2.Username = distinct_usernames.Username
    -- ...
    left join t10 on t10.Username = distinct_usernames.Username

从那里我将确保将用户名编入索引,甚至可以将其用作clustered index。我在过去通过在执行开始时将distinct_usernames作为临时表(可能是索引或索引视图)来实现优化运气,但只有测试才能确定是否值得。

完整的外部联接需要一堆or条件或coalesce个参数,尽管只需要几个表就可以尝试查看性能是否存在。我不能试图猜测你的查询引擎最喜欢什么。

此外,只需获取所需的列名即可通过查询sys.columnsinformation_schema.columns并使用dynamic SQL将查询构建为字符串然后执行该查询来完成。