动态行转列:处理不确定数量的行转列操作

目录

介绍

分析过程

数据样例

开始动手

添加辅助列

全连接换左连接

完成静态SQL

将动态部分设置到变量 

改编为动态SQL

验证

总结


介绍

行转列操作是一种常见的数据转换技术,它可以将原始的行数据转换为列数据,从而更方便地进行分析和可视化。然而,传统的行转列方式只能处理固定数量的行转列操作,无法处理不确定数量的行转列操作。为了解决这个问题,我们可以使用动态行转列技术。

动态行转列可以处理不确定数量的行转列操作,因为它使用动态 SQL 语句生成动态的列名。动态行转列具有高度的灵活性和扩展性,可以适应不同的数据结构和应用场景。在本文中,我们将介绍如何使用动态 SQL 实现动态行转列,并提供一个具体的案例演示。

分析过程

数据样例

假设我们有若干产品BOM信息,现有数据样例及期望得到的结果如下图:

d6f26847920c4628930cd1c72e3e1bb0.jpeg

这个不像网上普遍的销售数据的转换那么简单,多行转换为多列,同时还有多行数据需要合并。那我们先简化问题,假设分类是固定的,先写出SQL看看,然后再针对分类未知的情况做改进。

开始动手

我们先试试将行列进行转换,对着数据分析,看看离我们的目标有多远。先创建表,简单插入一些数据,然后行转列:

create table prod (
    main_element varchar(10) not null,
    element char(2) not null,
    num int not null,
    type varchar(4) not null
);

insert into prod values('产品1','A1',1,'A类');
insert into prod values('产品1','A2',2,'A类');
insert into prod values('产品1','A3',2,'A类');
insert into prod values('产品1','B1',1,'B类');
insert into prod values('产品1','B3',1,'B类');
insert into prod values('产品1','C1',1,'C类');
insert into prod values('产品2','A2',1,'A类');
insert into prod values('产品2','A3',1,'A类');
insert into prod values('产品2','B1',1,'B类');

select main_element,type,if(type='A类',element,'') as A类,
    if(type='B类',element,'') as B类,
    if(type='C类',element,'') as C类
from prod;

c8b6246e5f314cf1856f96549a76f839.png

结果如上,我们需要将同一主件的这些行合并,把A类、B类、C类的数据看成是在不同的表中,通过全连接可以实现合并。

添加辅助列

因为要分别筛选出A类、B类、C类的元件数据,因此原表的分类列也需要查询出来。另外不同的分类数据如果仅通过主件名称关联,会产生笛卡尔积,因此还需要为每个元件按主件分组编号来辅助关联,修改后的SQL及查询结果如下:

select main_element,type,row_number() over (partition by main_element,type order by element) seq,
    if(type='A类', element, '') AS 'A类',
    if(type='B类', element, '') AS 'B类',
    if(type='C类', element, '') AS 'C类'
from prod;

2fcccff01fab447ba50c3d07a94d4dbe.png

然后就是使用上面的结果,分别筛选出A类、B类、C类的数据,然后连接起来,各个分类的元件数量不一,应使用全连接。即使MySQL没有全连接,咱也不要使用左连接再union右连接,会增加后面动态SQL的复杂度。

全连接换左连接

那就想想别的办法,只要从上述数据获取所有的主件和元件编号,就可以简化为使用左连接了。使用with as将上述数据放入临时表(后续需要多次查询),先获取所有的主件和元件编号:

with prod1 as (
    select main_element,type,
        row_number() over (partition by main_element,type order by element) seq,
        if(type='A类', element, '') AS A类,
        if(type='B类', element, '') AS B类,
        if(type='C类', element, '') AS C类
    from prod
)
select distinct main_element,seq from prod1;

a6a6004b5701416ca243311e84a43c31.png

完成静态SQL

最后,从临时表prod1分别筛选出A类、B类、C类的数据,与上面的查询结果(放入另一临时表p)进行左连接,就得到我们需要的结果了:

-- 静态SQL
with prod1 as (
    select main_element,type,
        row_number() over (partition by main_element,type order by element) seq,
        if(type='A类', element, '') AS A类,
        if(type='B类', element, '') AS B类,
        if(type='C类', element, '') AS C类
    from prod
), p as (
    select distinct main_element,seq from prod1
)
select p.main_element,p1.A类,p2.B类,p3.C类
from p left join prod1 p1
on p.main_element=p1.main_element and p.seq=p1.seq and p1.type='A类'
left join prod1 p2
on p.main_element=p2.main_element and p.seq=p2.seq and p2.type='B类'
left join prod1 p3
on p.main_element=p3.main_element and p.seq=p3.seq and p3.type='C类';

dfadc8d7b70348d8a4d7abe9b9efe51c.png

到此,事情完成一半。因为分类是不固定的,我们的SQL中也不能出现明确的分类。现在回去查看上面的静态SQL,找到与具体分类相关的部分,它是可变的,而且是有规律的!如果将这几部分动态生成,咱们的SQL里面就不会出现明确的分类和与分类数量有关的代码了。

将动态部分设置到变量 

上述静态SQL中与具体分类相关的部分有三处。第一处是临时表prod1的查询,将分类转为列。第二处是要查询出来的列。第三处是有多少个分类就需要左连接多少次,每次的别名和过滤的类型不同。对于这三部分,分别通过查询产生,设置到三个不同的变量中。

-- 变量1
set @dynamic_sub_cols = (select group_concat(distinct concat('if(type=''',type,''', element, '''') AS ',type)) from prod);
select @dynamic_sub_cols;

变量1生成的结果是:

if(type='A类', element, '') AS A类,if(type='B类', element, '') AS B类,if(type='C类', element, '') AS C类
-- 变量2
set @dynamic_main_cols = (
    select group_concat('p',seq,'.',type) from (
        select type,row_number() over (order by type) seq from (select distinct type from prod) t
    ) t1
);
select @dynamic_main_cols;

变量2生成的结果是:

p1.A类,p2.B类,p3.C类
-- 变量3
set @dynamic_left_join = (
    select group_concat('left join prod1 p',seq,
        '\non p.main_element=p',seq,'.main_element and p.seq=p',seq,'.seq and p',seq,'.type=''',type,'''' separator '\n')
    from (select type,row_number() over (order by type) seq from (select distinct type from prod) t) t1
);
select @dynamic_left_join;

变量3生成的结果是:

left join prod1 p1
on p.main_element=p1.main_element and p.seq=p1.seq and p1.type='A类'
left join prod1 p2
on p.main_element=p2.main_element and p.seq=p2.seq and p2.type='B类'
left join prod1 p3
on p.main_element=p3.main_element and p.seq=p3.seq and p3.type='C类'

改编为动态SQL

然后将上述三个变量,分别替换掉前面静态SQL中的三处与具体分类相关的部分,得到动态SQL,将内容设置到变量中,通过执行变量中的动态SQL就可以得到之前的结果:

-- 动态SQL
set @dynami_sql = concat('with prod1 as (
    select main_element,type,
        row_number() over (partition by main_element,type order by element) seq,\n    ',
        @dynamic_sub_cols,'
    from prod
), p as (
    select distinct main_element,seq from prod1
)
select p.main_element,',@dynamic_main_cols,'
from p ',@dynamic_left_join);

select @dynami_sql;
prepare stmt from @dynami_sql;
execute stmt;

验证

我们现在再向产品表中插入一些数据,以验证结果:

insert into prod values('产品2','B3',2,'B类');
insert into prod values('产品2','B5',2,'B类');
insert into prod values('产品2','B6',2,'B类');
insert into prod values('产品2','C1',1,'C类');
insert into prod values('产品2','C2',1,'C类');
insert into prod values('产品2','C3',1,'C类');
insert into prod values('产品2','C4',1,'C类');
insert into prod values('产品2','C5',1,'C类');
insert into prod values('产品2','C6',1,'C类');
insert into prod values('产品3','D1',1,'D类');

然后执行上述变量1、变量2、变量3和动态SQL,得到如下结果,经验证,咱们的动态SQL满足要求:91dbdedc11784fcb9986cb2d82ac35d8.png

总结

动态行转列是一种高效、灵活、扩展性强的数据转换技术,可以处理不确定数量的行转列操作。在本文中,我们介绍了如何使用动态 SQL 实现动态行转列,并提供了一个具体的案例演示。通过本文可以了解到如何使用动态行转列处理不确定数量的行转列操作,从而更方便地进行数据分析和可视化。

 

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
### 回答1: Winform中的TableLayoutPanel是一个用于布局和控制子控件的容器控件。在TableLayoutPanel中,子控件以行和列的形式排列。为了查询TableLayoutPanel的第一行第一列上是什么控件,我们需要遍历TableLayoutPanel的子控件并访问其位置属性。 首先,我们可以通过TableLayoutPanel的Controls属性获得所有的子控件。然后,我们可以通过使用GetCellPosition方法来获取子控件的位置信息,该方法接受一个子控件作为参数,并返回其在TableLayoutPanel中的行和列的索引。 接下来,我们可以检查第一个子控件的位置信息,判断是否为第一行第一列。在TableLayoutPanel中,行和列的索引从零开始计数。因此,如果第一个子控件位于第一行第一列,则其行索引为0,列索引也为0。 最后,我们可以使用GetType方法获取第一个子控件的类型信息,从而确定第一行第一列上是什么控件。例如,如果第一个子控件是一个按钮,则我们可以通过GetType得到按钮的类型为System.Windows.Forms.Button。 综上所述,我们可以通过以上步骤来查询TableLayoutPanel第一行第一列上的控件,并确定其类型。 ### 回答2: 要查询TableLayoutPanel第一行第一列上的控件,可以通过以下代码实现: ```csharp // 假设TableLayoutPanel对象名为tableLayoutPanel1 // 如果第一行第一列上有控件,则可以通过Controls属性获取 if (tableLayoutPanel1.Controls.Count > 0) { // 第一个控件即为第一行第一列上的控件 Control firstControl = tableLayoutPanel1.GetControlFromPosition(0, 0); // 输出第一行第一列上的控件类型 Console.WriteLine("第一行第一列上的控件类型: " + firstControl.GetType().ToString()); } else { Console.WriteLine("第一行第一列上没有控件。"); } ``` 以上代码通过`GetControlFromPosition`方法可以获取指定单元格位置上的控件。由于TableLayoutPanel可以包含多个控件,所以首先需要判断该容器中是否有子控件。如果有子控件,则获取第一个控件并输出其类型;如果没有子控件,则输出“第一行第一列上没有控件”。 ### 回答3: 使用WinForm中的TableLayoutPanel控件可以实现表格布局。在查询TableLayoutPanel的第一行第一列上的控件时,可以使用如下代码进行操作: 首先,获取TableLayoutPanel中的所有行和列的数量,使用TableLayoutPanel的RowCount和ColumnCount属性获取: int rowCount = tableLayoutPanel.RowCount; // 获取行数 int colCount = tableLayoutPanel.ColumnCount; // 获取列数 接下来,通过索引获取指定位置的控件,使用TableLayoutPanel的GetControlFromPosition方法: Control targetControl = tableLayoutPanel.GetControlFromPosition(0, 0); // 获取第一行第一列的控件 然后,判断获取到的控件是否为空,以及它的类型: if (targetControl != null) { string controlType = targetControl.GetType().ToString(); // 获取控件的类型名 // 根据具体情况进行处理 } else { // 第一行第一列没有控件 } 最后,根据具体情况对获取到的控件进行进一步操作。 需要注意的是,以上代码假设TableLayoutPanel中的每个单元格都只包含一个控件。如果某个单元格包含多个控件,可以根据具体需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

创意程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值