没有键名时如何对数据进行从窄到宽的转换

Posted

技术标签:

【中文标题】没有键名时如何对数据进行从窄到宽的转换【英文标题】:How to do a narrow to wide transformation of data when there are no key names 【发布时间】:2019-11-03 01:51:47 【问题描述】:

我有一个如下所示的数据集:

school_id | class_id | recess_num | student_id
----------------------------------------------
27        | 6        | 2          | 12
27        | 6        | 2          | 53
27        | 6        | 2          | 23
27        | 6        | 2          | 47
27        | 14       | 2          | 6
27        | 14       | 2          | 51
27        | 14       | 2          | 42
27        | 14       | 2          | 60

这个想法是来自不同班级的某些学生同时出去休息。几个要点:

每个班级有相同数量的学生同时外出 每个班级每次外出的学生人数始终相同(假设一次有 4 个)

我想为这些数据创建一个宽表表示形式,其中每个课间休息的所有学生都被记录在一行中。由于学生的数量始终相同,我想为每个学生创建新列:

school_id | class_id | recess_num | student_1 | student_2 | student_3 | student_4
---------------------------------------------------------------------------------
27        | 6        | 2          | 12        | 53        | 23        | 47
27        | 14       | 2          | 6         | 51        | 42        | 60

仅使用 SQL 查询来完成此任务的最佳方法是什么?

【问题讨论】:

。 .用您正在使用的数据库标记您的问题。 【参考方案1】:

你可以做条件聚合:

select
    school_id,
    class_id,
    recess_num,
    max(case when rn = 1 then student_id end) student_1,
    max(case when rn = 2 then student_id end) student_2,
    max(case when rn = 3 then student_id end) student_3,
    max(case when rn = 4 then student_id end) student_4
from (
    select
        t.*, 
        row_number() 
            over(partition by school_id, class_id, recess_num order by student_id) rn
    from mytable t
) t
    group by 
    school_id,
    class_id,
    recess_num

内部查询对学校/班级/课间休息组中的学生进行排名,按 id 递增排序。然后外部查询使用条件聚合对数据进行透视。

请注意,这不会产生与您的预期结果完全相同的学生列顺序。但是,这似乎是一种按 id 对学生进行排序的更一致的方法(在这方面,您的预期结果似乎不一致)。

Demo on DB Fiddle

学校ID | class_id |凹槽编号 |学生_1 |学生_2 |学生_3 |学生_4 --------: | --------: | ---------: | --------: | --------: | --------: | --------: 27 | 6 | 2 | 12 | 23 | 47 | 53 27 | 14 | 2 | 6 | 42 | 51 | 60

【讨论】:

如果有多个相同的学生ID,可以使用排名【参考方案2】:
select 
    school_id, 
    class_id, 
    recess_num, 
    case when student_id=12 then student_id end as student1,
    case when student_id=53 then student_id end as student2,
    case when student_id=23 then student_id end as student3,
    case when student_id=47 then student_id end as student4,
from table 
group by 
    school_id, 
    class_id, 
    recess_num

【讨论】:

我需要比这更通用的东西,我想为所有class_ids 做,我给出的例子只是数据的一小部分 这对于所有班级 id 都是通用的,就好像 ypu cpuld 在 group by 除了学生 id 之外,其他列被列为通用。如果您的数据库支持枢轴,则可以将其用作枢轴用于将行转换为列。如果没有,那么您可以使用上面发布的 2 个回答中的任何一个。在上面的答案中,如果您确定使用提到的这些值,学生 ID 最多为 4 或更少,那么可以使用上面的其他方法,如果您有很多数据未知的值,则使用您选择的其他值

以上是关于没有键名时如何对数据进行从窄到宽的转换的主要内容,如果未能解决你的问题,请参考以下文章

athena presto - 从长到宽的多列

tidyR 从长到宽的数据?

如何把数据进行对数处理

以特定方式转换为宽格式

PHP - 当给定子键名时,递归地将每个数组元素的键设置为子元素的值

对目标变量进行对数转换后如何解释线性回归的结果?