在 SQL 中使行条目水平配对

Posted

技术标签:

【中文标题】在 SQL 中使行条目水平配对【英文标题】:Making Row Entries Pair Horizontally in SQL 【发布时间】:2013-05-03 14:51:14 【问题描述】:

所以这个问题与我之前提出的问题相似,但略有不同。

我正在查看被录取和退出计划的客户的数据。对于每次入院和出院,他们都会进行评估并进行评分,有时他们会在一段时间内多次入院和出院。

我需要能够将每个客户的录取分数与其下一个出院日期配对,这样我就可以查看所有在每次入院和出院时从入院到出院都有一定程度提高的客户。

这是我的数据结果现在如何格式化的虚拟示例:

这是我最喜欢的格式:

但我会采取任何正确的方向或类似的格式帮助,使我能够比较所有客户的所有录取和出院分数。

谢谢!

【问题讨论】:

尝试发布完整的表格或至少一些对测试有用的代码,而不是图片。 SQL Fiddle 可以派上用场,因此任何人都可以重用代码进行测试和回答。 你能不能也给出表结构?是否有 3 个表 - 用户、录取和出院信息? 约翰(乔恩)怎么只有出院?这真的发生在数据中吗? 【参考方案1】:

为了获得结果,您可以同时应用 UNPIVOT 和 PIVOT 函数。 UNPIVOT 会将datescore 的多列转换为行,然后您可以将这些行转回为列。

那么 unpivot 语法将类似于:

select person,
  casenumber,
  ScoreType+'_'+col col,
  value,
  rn
from
(
  select person,
    casenumber,
    convert(varchar(10), date, 101) date,
    cast(score as varchar(10)) score,
    scoreType,
    row_number() over(partition by casenumber, scoretype
                      order by case scoretype when 'Admit' then 1 end, date) rn            
  from yourtable
) d
unpivot
(
  value
  for col in (date, score)
) unpiv

见SQL Fiddle with Demo。这给出了一个结果:

| PERSON | CASENUMBER |             COL |      VALUE | RN |
-----------------------------------------------------------
|    Jon |       3412 |  Discharge_date | 01/03/2013 |  1 |
|    Jon |       3412 | Discharge_score |         12 |  1 |
|     Al |       3452 |      Admit_date | 05/16/2013 |  1 |
|     Al |       3452 |     Admit_score |         15 |  1 |
|     Al |       3452 |  Discharge_date | 08/01/2013 |  1 |
|     Al |       3452 | Discharge_score |         13 |  1 |

如您所见,此查询还创建了新列然后进行透视。所以最终的代码是:

select person, casenumber,
  Admit_Date, Admit_Score, Discharge_Date, Discharge_Score
from
(
  select person,
    casenumber,
    ScoreType+'_'+col col,
    value,
    rn
  from
  (
    select person,
      casenumber,
      convert(varchar(10), date, 101) date,
      cast(score as varchar(10)) score,
      scoreType,
      row_number() over(partition by casenumber, scoretype
                        order by case scoretype when 'Admit' then 1 end, date) rn

    from yourtable
  ) d
  unpivot
  (
    value
    for col in (date, score)
  ) unpiv
) src
pivot
(
  max(value)
  for col in (Admit_Date, Admit_Score, Discharge_Date, Discharge_Score)
) piv;

见SQL Fiddle with Demo。这给出了一个结果:

| PERSON | CASENUMBER | ADMIT_DATE | ADMIT_SCORE | DISCHARGE_DATE | DISCHARGE_SCORE |
-------------------------------------------------------------------------------------
|     Al |       3452 | 05/16/2013 |          15 |     08/01/2013 |              13 |
|  Cindy |       6578 | 01/02/2013 |          17 |     03/04/2013 |              14 |
|  Cindy |       6578 | 03/04/2013 |          14 |     03/18/2013 |              12 |
|    Jon |       3412 |     (null) |      (null) |     01/03/2013 |              12 |
|  Kevin |       9868 | 01/18/2013 |          19 |     03/02/2013 |              15 |
|  Kevin |       9868 | 03/02/2013 |          15 |         (null) |          (null) |
|   Pete |       4765 | 02/06/2013 |          15 |         (null) |          (null) |
|  Susan |       5421 | 04/06/2013 |          19 |     05/07/2013 |              15 |

【讨论】:

SQL Fiddle 是我最喜欢的新玩具。 @Ransom 请查看我的编辑,我添加了 row_number() 以保持行的唯一性,以便您为每个人返回单独的行。 @Ransom 在 UNPIVOT 之前的内部子查询中进行日期过滤,只需将 WHERE 子句添加到该查询。如果这不起作用,那么在您的外部选择语句中将出院日期转换为日期并应用您的过滤器。 @Ransom 是的,您应该能够在 PIVOT 之后进行过滤,您需要在 where 子句中将discharge_date 转换为日期或日期时间。 @Ransom 您需要在中间的日期周围加上单引号 = BETWEEN '2012-01-01' AND '2013-03-01'【参考方案2】:

SELECT 
ad.person, ad.CaseNumber, ad.Date as AdmitScoreDate, ad.Score as AdmitScore,
dis.date as DischargeScoreDate, dis.Score as DischargeScore
From
yourTable ad, yourTable dis

WHERE ad.person=dis.person and ad.ScoreType='Admit' and d is.ScoreType='Discharge';

【讨论】:

解释为什么会这样。你给了OP一条鱼。相反,教他们如何钓鱼。 对不起,这是我第一次在这个论坛上发帖。此查询是自连接查询的示例,其中使用不同的表别名查询一个表两次,因此它与在公共列上连接两个表相同。承认数据来自一个别名表,而出院数据来自另一个别名表,连接到普通人列,甚至更好地连接到案例编号列,以避免不同的人使用相同的名称。【参考方案3】:

如果你提到的所有列都在同一个表中,你可以加入同一个表

 SELECT t1.person,
  t1.caseNumber,
   t1.date adate,
  t1.score  ascore,
  t1.scoreType ascoreType,
  t2.date ddate,
  t2.score  dscore,
  t2.scoreType dscoretype
FROM patient t1
join patient t2
on t1.casenumber=t2.casenumber
and t1.scoreType!=t2.scoreType
and t1.scoreType='Admit'

但这不会向您显示已入院但尚未出院的人的记录。我不知道您是否也在寻找该信息。

SQL 小提琴link

希望这会有所帮助!

【讨论】:

以上是关于在 SQL 中使行条目水平配对的主要内容,如果未能解决你的问题,请参考以下文章

如何在 RowsFragment 中使行的标题始终可见

在 oracle sql 中配对记录

如何将低/高基数列配对为复合索引?

如何在不重复的情况下配对这个数组的内容?

ABtest显著性校验(配对T检验)

Android蓝牙开发——经典蓝牙:配对与解除配对 & 实现配对或连接时不弹出配对框