我有两个表,t1和t2:
t1 person | visit | code1 | type1 1 1 50 50 1 1 50 50 1 2 75 50 t2 person | visit | code2 | type2 1 1 50 50 1 1 50 50 1 1 50 50
SAS运行以下代码时:
DATA t3; MERGE t1 t2; BY person visit; RUN;
它生成以下数据集:
person | visit | code1 | type1 | code2 | type2 1 1 50 50 50 50 1 1 50 50 50 50 1 1 50 50 50 50 1 2 75 50
我想在SQL中复制此过程,我的想法是使用完全外部联接。除非有重复的行,否则此方法有效。当像上面的示例一样有重复的行时,完整的外部联接将产生下表:
person | visit | code1 | type1 | code2 | type2 1 1 50 50 50 50 1 1 50 50 50 50 1 1 50 50 50 50 1 1 50 50 50 50 1 1 50 50 50 50 1 1 50 50 50 50 1 2 75 50
我想知道如何使SQl表与SAS表匹配。
戈登的答案很接近。但它遗漏了一点。这是它的输出:
person visit code1 type1 seqnum person visit code2 type2 seqnum 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 2 2 2 NULL NULL NULL NULL NULL 1 1 3 3 3 1 2 1 3 1 NULL NULL NULL NULL NULL
第三行的空值是不正确的,而第四行的空值是正确的。
据我所知,在SQL中,除了将事情分解成几个查询外,没有什么好方法。我认为有五种可能性:
我认为后两个可以在一个查询中使用,但我认为第二个和第三个必须是单独的查询。当然,您可以将所有内容结合在一起。
因此,这是一个示例,使用一些更适合查看正在发生的情况的临时表。请注意,即使它们是“多余”的,现在也为code1和填充了第三行type1。我仅添加了五个条件中的三个- 您在最初的示例中具有三个-但其他两个条件并不难。
code1
type1
请注意,这是什么一个例子 远 在SAS更快- 因为SAS具有逐行概念,即它能够在同一时间去一行。对于大型表,SQL往往会花费更长的时间,除非可以非常整洁地对事物进行分区并拥有非常好的索引- 甚至到那时,我还从未见过SQL DBA可以像SAS那样在任何地方做得很好问题类型。这当然是您必须接受的-SQL具有自己的优势,其中之一可能是价格…
这是我的示例代码。我确信它不是非常优雅,希望SQL专家之一可以对其进行改进。这是为了在SQL Server(使用表变量)中工作而编写的,假设其他东西实现了窗口化,则同样的东西应该在其他变体中进行一些更改(以使用临时表)。(SAS当然不能做这件事- 甚至FedSQL都实现ANSI 1999,而不是ANSI2008。)这是基于Gordon的初始查询,然后在末尾用其他位进行修改的。任何想要改善此问题的人都可以随意编辑和/或复制到新的/现有的答案中。
declare @t1 table (person INT, visit INT, code1 INT, type1 INT); declare @t2 table (person INT, visit INT, code2 INT, type2 INT); insert into @t1 values (1,1,1,1) insert into @t1 values (1,1,2,2) insert into @t1 values (1,2,1,3) insert into @t2 values (1,1,1,1) insert into @t2 values (1,1,2,2) insert into @t2 values (1,1,3,3) select coalesce(t1.person, t2.person) as person, coalesce(t1.visit, t2.visit) as visit, t1.code1, t1.type1, t2.code2, t2.type2 from (select *, row_number() over (partition by person, visit order by type1) as seqnum from @t1 ) t1 inner join (select *, row_number() over (partition by person, visit order by type2) as seqnum from @t2 ) t2 on t1.person = t2.person and t1.visit = t2.visit and t1.seqnum = t2.seqnum union all select coalesce(t1.person, t2.person) as person, coalesce(t1.visit, t2.visit) as visit, t1.code1, t1.type1, t2.code2, t2.type2 from ( (select person, visit, MAX(seqnum) as max_rownum from ( select person, visit, row_number() over (partition by person, visit order by type1) as seqnum from @t1) t1_f group by person, visit ) t1_m inner join (select *, row_number() over (partition by person, visit order by type1) as seqnum from @t1 ) t1 on t1.person=t1_m.person and t1.visit=t1_m.visit and t1.seqnum=t1_m.max_rownum inner join (select *, row_number() over (partition by person, visit order by type2) as seqnum from @t2 ) t2 on t1.person = t2.person and t1.visit = t2.visit and t1.seqnum < t2.seqnum ) union all select t1.person, t1.visit, t1.code1, t1.type1, t2.code2, t2.type2 from @t1 t1 left join @t2 t2 on t2.person=t1.person and t2.visit=t1.visit where t2.code2 is null