TSQL - 连接三个表,每个表有一个 MTO 列

TSQL - Join Three Tables with a single MTO column each

好吧,我有一个需要三数据库合并的无前端查询的查理狐步舞,在三个子查询从单独的数据库中获得我需要的东西之后,我意识到我完全不知道如何进行多个 MTO 组合,而不会破坏所有内容或拥有前端。

另请注意:OTO = 一对一,MTO = 多对一 抱歉,真的应该在第一次编辑之前说。

带有示例数据的 SQLFiddle,JOIN-fail 和 UNION-fail 的变体,以及带有目标描述的 table:http://sqlfiddle.com/#!3/07e6a/1


我得到了什么

三个表:
tblA: id, type, svc, other_data
tblB: id, type, svc
tblC: id, svc

他们遵循的规则:
0. id 是每个 table 之间的共享值(可用于 JOINing)
1. ids 必须在 tblAtblB 中有一个条目,而 tblC 只是有时有条目。
2.所有svc列与id是多对一关系,其他所有列与[是一对一关系=81=]id.
3.svctblAtblB可以为空 ,但是,table 都提供了至少一个始终存在的 OTO 值(如果它们有 none,则只需将 null 推入它们的 svc
4. idsvc 'array' 在单个 table 中没有人被欺骗(如这些 tables 是 SELECT 不同的) 5. tblA.other_data 实际上是多列,但这根本不重要,因为任何支持一个额外 OTO 列的解决方案都应该支持多个,因为它是 OTO。最坏的情况是我可以将这些列移动到包装器查询中。这不是问题。


我想要什么

tblD: id, tblA.other_data, tblA.type, tblB.type, tblA.svc, tblB.svc, tblC.svc

其中每个 svc 列都是 MTO,但是可以用最少的行数来表达这一点(即,tblD 应该具有 Theta 的 space 复杂度(最大(tblA.svc, tblB.svc, tblC.svc)) ).

最好是 svc 单独按字母顺序排列的顺序,但是,这并不是关键任务(或者更准确地说,有一个 unstable解决方案给了我一个不同的问题来解决,而不是我遇到的问题。

本质上,我需要一种 JOIN/UNION/WHATEVER 的形式,它只将某些列视为对添加的任何新列有意义,而不是所有列。 w*(x+y+z)而不是w*x*y*z,其中w是OTO信息,x、y、z都是独立的MTO信息集。


我尝试了什么

天真地,第一个想法可能只是链接 JOIN (tblA INNER tblB LEFT tblC) on id, 然后输出我想要的列
但是这样做是吃亏的。对于每个 tblA.svc,您都会得到每个 tblB.svc 的实例,并且对于每个实例,您都会得到一个每个 tblC.svc 的实例。它的 Theta(a*b*c) 大小复杂性正在接近天哪,即使在小集合上也是如此,而且这种复杂性会破坏你可能拥有的任何人类可读性。

您还可以尝试围绕 UNION 进行包装,并巧妙地 'caching' 使其在性能上不会太糟糕。您最终得到一列(而不是三列;标记 table 它来自的列,您有我的权宜之计)),或者每条记录的非空对角线。无论哪种方式,space 的复杂性都降低到 Theta(a+b+c)(更容易忍受),并且它在没有前端过滤器的情况下是人类可读的。然而,仍然不理想。

我想出的第三个解决方案(还没有写)是从形成 tblD 开始,三个输入 tables 中的任何一个具有平均大多数行(希望以后节省手动 INSERT),然后在可以时使用 UPDATE 扫描其他两个输入 tables,在不能时使用 INSERT。然而,感觉像是一个核选项,会让 JIT 优化器哭泣,数据库对我很失望,所以我没有尝试,因为以前的解决方案已经足够好了。
我还没有实现这个的另一个原因是因为这看起来像这样一个 'simple' 概念,我觉得有一些内置的方法可以做到这一点,这将使 JIT 快乐和数据库......好吧,不那么生气了。

这真的是 'best way' 得到我想要的东西,而不是让前端为数据库做这件事吗?

我的意思是,有了前端,做起来相当简单:传递 OTO 和三个按 id 排序的 MTO table,用三个 walk 走 OTO -直到 MTO 的下一个内部结构,让您一次通过所有四个 tables 并且只写入输出流,而 DB 在响应大小方面做得更少(更少的数据重复)......但是我没有前端可以利用这个(无论如何)。


样本数据

这个数据是理论上的,因为它与教育推广有关,我不想不小心漏掉一些东西。 'type' 字段只是关于可能存在什么的想法,它对问题来说并不重要,因为它是 OTO,如果需要,我可以将它放入包装器中。

为简单起见,我排除了 tblA.other_data 的样本

tblA:

id, type, svc
 1, 'li', 'Fin Aid/Scholarships'
 1, 'li', 'Tutoring: Math'
 1, 'li', 'Tutoring: English'
 2, 'fg', null
 3, 'fg', 'Career Counseling'

tblB:

id, type, svc
 1, 'g1', 'CollPrep'
 1, 'g1', 'Academic Support'
 2, 'g1', 'SciSum'
 2, 'g1', 'EngAcad'
 2, 'g1', 'MathAcad'
 3, 'g2', null

tblC:

id, svc
 3, 'Workshops'

将产生待定:

id, typeA, typeB, svcA                  , svcB              , svcC
 1, 'li' , 'g1' , 'Fin Aid/Scholarships', 'CollPrep'        , null
 1, 'li' , 'g1' , 'Tutoring: Math'      , 'Academic Support', null
 1, 'li' , 'g1' , 'Tutoring: English'   , null              , null
 2, 'fg' , 'g1' , null                  , 'SciSum'          , null
 2, 'fg' , 'g1' , null                  , 'EngAcad'         , null
 2, 'fg' , 'g1' , null                  , 'MathAcad'        , null
 3, 'fg' , 'g2' , 'Career Counseling'   , null              , 'Workshops'

注意:这个 table 没有完全符合要求的顺序,但正如我之前提到的,我不太关心 id 之外的顺序(或其他 OTO 列之一,这是微不足道的)

我也会提供示例代码,但我真的看不出它有什么帮助,因为我的问题更多的是概念性问题,而不是 "in this instance" 问题。不过,我确实知道实际看到我正在尝试做的事情会有所帮助,因此示例数据。

老实说,我也有点(非常)尴尬的后端是多么hacky...假设我是大约十年来这个职位的第一个非临时工,大部分时间前端在 MS Access 中,数据库看起来几乎不像最后一个 stable 人在做我的工作时那样。



抢占 'similar question' 标志:

join two tables with different number of rows(in sql server) - 有点相似,但问题源于 FULL OUTER JOIN 用例,而不是多个 MTO 关系。

- 非常相似......但那里使用的所有技巧都取决于您的 MTO 关系中已知的 Many,以及将额外的行转换为列的愿望。我不能利用第一个(不设置任意最大值),并且非常不想要后者。

好的,感谢您发布一些可使用的数据。它使这实际上成为可能。这是执行此操作的一种方法。我必须创建一些 CTE 来添加排序,这样我们才能知道哪一行属于哪一行。

这不太对,因为还有一些业务规则我不太了解。这应该足够接近,您可以将最后几件事调整为您想要的方式。

with SortedTableA as
(
    select *, ROW_NUMBER() over (partition by id order by svc) as RowNum
    from tblA
)
, SortedTableB as
(
    select *, ROW_NUMBER() over (partition by id order by svc) as RowNum from tblB
)
, SortedTableC as
(
    select *, ROW_NUMBER() over (partition by id order by svc) as RowNum from tblC  
)

select isnull(a.id, b.id) as id
    , ISNULL(a.type, b.type) as typeA
    , ISNULL(b.type, a.type) as typeB
    , a.svc as svcA
    , b.svc as svcB
    , c.svc as svcC
from SortedTableA a
full outer join SortedTableB b on a.id = b.id and a.RowNum = b.RowNum
left join tblC c on c.id = a.id
order by isnull(a.id, b.id), ISNULL(a.RowNum, b.RowNum)