SAS/PROC-SQL 从具有唯一键的表转换为具有相同键的多行表

Posted

技术标签:

【中文标题】SAS/PROC-SQL 从具有唯一键的表转换为具有相同键的多行表【英文标题】:SAS/PROC-SQL Convert from table with unique key to table with multiple rows has the same key 【发布时间】:2013-03-20 11:33:25 【问题描述】:

目前我有一张如下表:

U_ID SPOUSEDOB   FCHILDDOB   SCHILDDOB   ChangeDate
1    20/01/1980  01/01/1900  01/01/1900  01/01/2000
2    20/01/1950  20/01/1970  01/01/1900  01/01/2000
3    20/01/1960  20/01/1990  20/01/1995  01/01/2000
1    20/01/1980  20/01/1995  01/01/1900  01/01/2005
1    20/01/1980  20/01/1995  20/01/2006  01/01/2010

日期 01/01/1900,这意味着没有配偶/孩子。 我想像下面这样转换这个表:

Member_ID  U_ID  Relation DOB         ChangeDate
1          1     Spouse   20/01/1980  01/01/2000
2          2     Spouse   20/01/1950  01/01/2000
3          2     Child    20/01/1970  01/01/2000
4          3     Spouse   20/01/1960  01/01/2000
5          3     Child    20/01/1990  01/01/2000
6          3     Child    20/01/1995  01/01/2000
7          1     Child    20/01/1995  01/01/2005
8          1     Child    20/01/2006  01/01/2010

但在特定时间 (01/01/2006) 和 (01/01/2011),此表仍然无法提供回答此问题的最佳方式,用户 1 有几个孩子?答案是 1 和 2。 而且我也发现很难从表 1 转换为表 2,我被困在如何为相同的 user_id 创建新行。 关于如何改善这种情况或解决转换表问题的任何想法?非常感谢您的帮助。提前谢谢你。

【问题讨论】:

您有什么特别的原因要在 PROC SQL 中执行此操作吗?对这种操作使用 DATA 步似乎要容易得多。 当然没有附加到PROC SQL,我认为标题中的SAS意味着数据步骤也被考虑在内。如果您有任何想法,尤其是关于获得上述问题答案的最佳格式表(关于具体时间),将不胜感激。 【参考方案1】:

这是一个简单的 SAS 数据步。您可以调整它以使用 VNAME() 来定义关系(取决于您的其他变量的命名方式);例如,

relation = vname(DOBs[_t]);

然后使用 SUBSTR 或其他方法将其缩短为正确的文本。除此之外,它应该足够灵活,可以处理初始 HAVE 数据集中的任意数量的关系。

data want;
set have;
array DOBs SPOUSEDOB   FCHILDDOB   SCHILDDOB;
do _t = 1 to dim(DOBs);
  if DOBs[_t] ne '01JAN1900'd then do;
    relation=ifc(_t=1,'Spouse','Child'); *this could also be done using VNAME() to be more flexible;
    DOB=DOBs[_t];
    output;
  end;
end;
keep relation DOB ChangeDate U_ID;
format DOB Changedate Date9.;
run;

proc sort data=want;
by u_id descending relation dob changedate;
run;


data final;
set want;
by u_id descending relation dob changedate;
if first.dob;
run;

然后要处理它以仅选择在某个日期出生的人,如果您更喜欢使用 SQL,您可以使用 fthiella 发布的查询,或者您可以在 SAS proc 中进行过滤,例如:

proc means data=final;
where dob le '01JAN2006'd;
class relation;
var (whatever);
run;

如果您想要过滤的不是实际 DOB,请使用 ChangeDate。

【讨论】:

感谢乔的详细回答。【参考方案2】:

这会将您的表格从第一种格式转换为第二种格式:

SELECT
  U_ID,
  'Spouse' Relation,
  Spousedob DOB,
  MIN(STR_TO_DATE(ChangeDate, '%d/%m/%Y')) ChangeDate
FROM
  yourtable
WHERE
  Spousedob != '01/01/1900'
GROUP BY U_ID
UNION ALL
SELECT
  U_ID,
  'Child' Relation,
  FCHILDDOB DOB,
  MIN(STR_TO_DATE(ChangeDate, '%d/%m/%Y')) ChangeDate
FROM
  yourtable
WHERE FCHILDDOB != '01/01/1900'
GROUP BY U_ID
UNION ALL
SELECT
  U_ID,
  'Child' Relation,
  SCHILDDOB DOB,
  MIN(STR_TO_DATE(ChangeDate, '%d/%m/%Y')) ChangeDate
FROM yourtable
WHERE SCHILDDOB != '01/01/1900'
GROUP BY U_ID
ORDER BY ChangeDate, U_ID

但要回答您的问题,您可以使用以下查询:

SELECT (FCHILDDOB!='01/01/1900')+(SCHILDDOB!='01/01/1900')
FROM yourtable
WHERE
  (U_ID, ChangeDate) IN (
    SELECT U_ID, MAX(ChangeDate)
    FROM yourtable
    WHERE
      U_ID=1 AND MIN(STR_TO_DATE(ChangeDate, '%d/%m/%Y'))<'2011-01-01')

(我正在考虑将您的日期存储为 varchar,并且我正在使用 STR_TO_DATE 转换为日期)

编辑

您可以创建一个包含列(Member_ID auto_increment、U_ID、Relation、DOB、ChangeDate)的表yourtable2,然后使用以下命令将所有数据从yourtable 插入到yourtable2

INSERT INTO yourtable2 (U_ID, Relation, DOB, ChangeDate)
SELECT ... --- the select query above
ORDER BY ChangeDate, DOB, U_ID

然后计算你可以使用的孩子的名字:

SELECT COUNT(*)
FROM   yourtable2
WHERE  Relation='Child'
       AND U_ID=1
       AND ChangeDate <= '2011-01-01'

请看小提琴here。

【讨论】:

感谢您的回答。我将尝试更改格式查询。关于这个问题,似乎有一些误解。我想更改第一个表,因为它限制了孩子的数量(只有 2 个),因此我想更改表以列出所有家庭成员。这样我就可以将它扩展到不仅超过 2 个孩子,还可以扩展到孙子等其他关系。 @LukeHenz 我更新了我的答案,我希望这就是你要找的东西 感谢 fthiella 的详细回答,但由于 Joe 回答的额外表现,我无法接受您的回答。但是我已经赞成答案,我希望你不会介意。再次感谢。【参考方案3】:

这不起作用,因为我不了解您起始表中的关系。但它可能会帮助您使用普通的旧 SAS 数据步骤代码找到另一种解决方案:

data have;
   input U_ID SPOUSEDOB :ddmmyy10. FCHILDDOB :ddmmyy10.  
         SCHILDDOB :ddmmyy10. ChangeDate :ddmmyy10.;
datalines;
1    20/01/1980  01/01/1900  01/01/1900  01/01/2000
2    20/01/1950  20/01/1970  01/01/1900  01/01/2000
3    20/01/1960  20/01/1990  20/01/1995  01/01/2000
1    20/01/1980  20/01/1995  01/01/1900  01/01/2005
1    20/01/1980  20/01/1995  20/01/2006  01/01/2010
run;
data want(keep=Member_ID U_ID Relation DOB ChangeDate);
   attrib Member_ID  length=8;
   attrib U_ID       length=8;
   attrib Relation   length=$6;
   attrib DOB        length=8 format=ddmmyy10.;
   attrib ChangeDate length=8 format=ddmmyy10.;
   retain Member_ID 0;

   set have;

   if _n_ = 1 or U_ID ne 1 then do;
      Member_ID + 1;
      Relation = 'Spouse';
      DOB = SPOUSEDOB;
      output;
      end;

   if FCHILDDOB ne mdy(1,1,1900) then do;
      Member_ID + 1;
      Relation = 'Child';
      DOB = FCHILDDOB;
      output;
      end;
   if SCHILDDOB ne mdy(1,1,1900) then do;
      Member_ID + 1;
      Relation = 'Child';
      DOB = SCHILDDOB;
      output;
      end;
  run;

【讨论】:

以上是关于SAS/PROC-SQL 从具有唯一键的表转换为具有相同键的多行表的主要内容,如果未能解决你的问题,请参考以下文章

比较两个没有唯一键的表

具有唯一索引和主键的列给出唯一约束违规

如何从具有两个外键的表中访问列?

实体框架:如何从具有复合键的表中返回一行?

where 从具有列外键的表中查询

快速 SQL 问题:在 H2 中创建具有主键的表的正确语法?