如何在 SQLite 中进行透视或以宽格式选择以长格式存储的表?

Posted

技术标签:

【中文标题】如何在 SQLite 中进行透视或以宽格式选择以长格式存储的表?【英文标题】:How to pivot in SQLite or i.e. select in wide format a table stored in long format? 【发布时间】:2010-11-17 05:57:35 【问题描述】:

我想要一张表格,其中存储了long format 中的学生数据以及他们在一次查询中获得的所有科目的分数。

这是我的表结构:

表:markdetails

## studid ## ## subjectid ##  ## marks ##
     A1            3                50
     A1            4                60
     A1            5                70
     B1            3                60
     B1            4                80
     C1            5                95

表:student info

实际结构:

## studid ##  ## name ##
      A1          Raam
      B1          Vivek
      c1          Alex

我希望结果集具有以下wide format 结构作为透视的结果:

表:Student Info

## studid ## ## name## ## subjectid_3 ## ## subjectid_4 ## ## subjectid_5 ##
      A1        Raam        50                60                 70
      B1        Vivek       60                80                null
      c1        Alex       null              null                95

如何在 SQLite 中完成此操作?

【问题讨论】:

【参考方案1】:

由于作者没有提供 SQL 来创建架构,因此这里适用于任何想要尝试来自 @Eric 的解决方案的人。

create table markdetails (studid, subjectid, marks);
create table student_info (studid, name);

insert into markdetails values('A1', 3, 50);
insert into markdetails values('A1', 4, 60);
insert into markdetails values('A1', 5, 70);
insert into markdetails values('B1', 3, 60);
insert into markdetails values('B1', 4, 80);
insert into markdetails values('C1', 5, 95);

insert into student_info values('A1', 'Raam');
insert into student_info values('B1', 'Vivek');
insert into student_info values('C1', 'Alex');

这是使用casegroup by 的替代解决方案。

select
    si.studid,
    si.name,
    sum(case when md.subjectid = 3 then md.marks end) subjectid_3,
    sum(case when md.subjectid = 4 then md.marks end) subjectid_4,
    sum(case when md.subjectid = 5 then md.marks end) subjectid_5
from student_info si
join markdetails md on
        md.studid = si.studid
group by si.studid, si.name
;

为了比较,这里是来自@Eric 解决方案的相同选择语句:

select
    u.stuid,
    u.name,
    s3.marks as subjectid_3,
    s4.marks as subjectid_4,
    s5.marks as subjectid_5
from
    student_info u
    left outer join markdetails s3 on
        u.stuid = s3.stuid
        and s3.subjectid = 3
    left outer join markdetails s4 on
        u.stuid = s4.stuid
        and s4.subjectid = 4
    left outer join markdetails s5 on
        u.stuid = s5.stuid
        and s5.subjectid = 5
;

当有大量数据时,看看哪一个会表现得更好会很有趣。

【讨论】:

我有机会在一张大约有 150,000 行的桌子上对此进行了测试。一个复杂的问题是我事先不知道列数,所以我必须做一些预处理来确定需要的列数。此外,并非所有行都具有相同数量的数据。使用外部连接方法,我的 PC 花了 50 秒。使用 case when 方法,需要 15 秒。使用 reshape2 和 plyr 的组合(我使用 R 来运行 sqlite),大约需要 1,040 秒。但是,您的里程可能会有所不同。 @Chow,完全同意。我的表有 280,000 行,大约需要 20 秒。这个答案应该放在首位。 @haridsv 更好的答案。另外,请阅读:modern-sql.com/use-case/pivot 对于尝试使用字符串的任何人,请使用 MAX 而不是在枢轴CASE 行中的SUM。否则,您的字符串将被解释为可能会产生奇怪结果的数字。 您是否添加了适当的索引?例如(subjectid), (studid) 和 on (studid, name)【参考方案2】:

首先您需要将当前表更改为临时表:

alter table student_info rename to student_name

然后,您需要重新创建 student_info

create table student_info add column (
    stuid VARCHAR(5) PRIMARY KEY,
    name VARCHAR(255),
    subjectid_3 INTEGER,
    subjectid_4 INTEGER,
    subjectid_5 INTEGER
)

然后,填充student_info:

insert into student_info
select
    u.stuid,
    u.name,
    s3.marks as subjectid_3,
    s4.marks as subjectid_4,
    s5.marks as subjectid_5
from
    student_temp u
    left outer join markdetails s3 on
        u.stuid = s3.stuid
        and s3.subjectid = 3
    left outer join markdetails s4 on
        u.stuid = s4.stuid
        and s4.subjectid = 4
    left outer join markdetails s5 on
        u.stuid = s5.stuid
        and s5.subjectid = 5

现在,只需删除您的临时表:

drop table student_temp

这就是您可以快速更新表格的方法。

SQLite 缺少pivot 函数,所以你能做的最好的就是硬编码一些左连接。 left join 将匹配其连接条件中的任何行,并为第一个或左表中不满足第二个表的连接条件的任何行返回 null

【讨论】:

感谢 Eric ...查询可以很好地获取有关学生的全部详细信息。但我想修改表格中的内容和列。我想你没有得到我的问题。我想要换桌子。 @arams:太棒了,很高兴听到它!如果它解决了您的问题,请点赞/标记为答案! 可能是第一行的错字。也许它应该改为alter table student_info rename to student_temp @Eric 我的应用程序需要更多列(准确地说是 369 列;与 OP 应用程序中的 3 个主题相反)。但是一个 sqlite 语句不能执行超过 64 个连接。您能否建议在此处进行编辑以完成超过 64 个连接?或者有什么解决方法? “现在,放下你的临时表:drop table student_temp,这个student_temp 临时表是从哪里来的,埃里克?【参考方案3】:

很棒的附录!帮助我以低工作量和系统负载解决了类似的问题。我正在使用 Raspberry Pi 获取 1wire-interface DS18B20 温度传感器数据,如下所示:

CREATE TABLE temps (Timestamp DATETIME, sensorID TEXT, temperature NUMERIC);

示例:

sqlite> .headers on
sqlite> .mode column
sqlite> select * from temps where timestamp > '2014-02-24 22:00:00';

Timestamp            sensorID         temperature
-------------------  ---------------  -----------
2014-02-24 22:00:02  28-0000055f3f10  19.937
2014-02-24 22:00:03  28-0000055f0378  19.687
2014-02-24 22:00:04  28-0000055eb504  19.937
2014-02-24 22:00:05  28-0000055f92f2  19.937
2014-02-24 22:00:06  28-0000055eef29  19.812
2014-02-24 22:00:07  28-0000055f7619  19.625
2014-02-24 22:00:08  28-0000055edf01  19.687
2014-02-24 22:00:09  28-0000055effda  19.812
2014-02-24 22:00:09  28-0000055e5ef2  19.875
2014-02-24 22:00:10  28-0000055f1b83  19.812
2014-02-24 22:10:03  28-0000055f3f10  19.937
2014-02-24 22:10:04  28-0000055f0378  19.75
2014-02-24 22:10:04  28-0000055eb504  19.937
2014-02-24 22:10:05  28-0000055f92f2  19.937

使用 SUBSTR() 命令,我将时间戳“标准化”为 10 分钟周期。通过 JOIN,sensorID 使用查找表“sensors”更改为 SensorName

CREATE VIEW [TempsSlot10min] AS
SELECT SUBSTR(datetime(timestamp),1,15)||'0:00' AS TimeSlot,
SensorName,
temperature FROM
temps JOIN sensors USING (sensorID, sensorID);

示例:

sqlite> select * from TempsSlot10min where timeslot >= '2014-02-24 22:00:00';

TimeSlot             SensorName  temperature
-------------------  ----------  -----------
2014-02-24 22:00:00  T1          19.937
2014-02-24 22:00:00  T2          19.687
2014-02-24 22:00:00  T3          19.937
2014-02-24 22:00:00  T4          19.937
2014-02-24 22:00:00  T5          19.812
2014-02-24 22:00:00  T6          19.625
2014-02-24 22:00:00  T10         19.687
2014-02-24 22:00:00  T9          19.812
2014-02-24 22:00:00  T8          19.875
2014-02-24 22:00:00  T7          19.812
2014-02-24 22:10:00  T1          19.937
2014-02-24 22:10:00  T2          19.75
2014-02-24 22:10:00  T3          19.937
2014-02-24 22:10:00  T4          19.937
2014-02-24 22:10:00  T5          19.875

现在,神奇的事情发生在上面提到的 CASE 指令上。

CREATE VIEW [PivotTemps10min] AS
SELECT TimeSlot,
AVG(CASE WHEN sensorName = 'T1' THEN temperature END) AS T1,
AVG(CASE WHEN sensorName = 'T2' THEN temperature END) AS T2,
...
AVG(CASE WHEN sensorName = 'T10' THEN temperature END) AS T10
FROM TempsSlot10min
GROUP BY TimeSlot;

示例:

select * from PivotTemps10min where timeslot >= '2014-02-24 22:00:00';

TimeSlot             T1          T2              T10
-------------------  ----------  ---------- ...  ----------
2014-02-24 22:00:00  19.937      19.687          19.687
2014-02-24 22:10:00  19.937      19.75           19.687
2014-02-24 22:20:00  19.937      19.75           19.687
2014-02-24 22:30:00  20.125      19.937          19.937
2014-02-24 22:40:00  20.187      20.0            19.937
2014-02-24 22:50:00  20.25       20.062          20.062
2014-02-24 23:00:00  20.25       20.062          20.062

这里剩下的唯一问题是 sensorName 'T1' ... 'T10' 现在被硬编码到 VIEW [PivotTemps10min] 中,而不是从查找表中获取。

不过,非常感谢您在此广告中提供的答案!

【讨论】:

这正是我一直在寻找的。非常感谢。 相信很多使用SQL的物联网爱好者都会参考这个。我的申请几乎一模一样。 1 个查询导致 13 小时的折磨,1 个回答让我恢复了理智。谢谢你好先生【参考方案4】:

感谢@pospec4444 的link,这里是@haridsv 的精彩答案的修改版。它使用filter 子句更加简洁

select
    si.studid,
    si.name,
    sum(md.marks) filter(where md.subjectid = 3) subjectid_3,
    sum(md.marks) filter(where md.subjectid = 4) subjectid_4,
    sum(md.marks) filter(where md.subjectid = 5) subjectid_5
from student_info si
join markdetails md on
        md.studid = si.studid
group by si.studid, si.name
;

【讨论】:

【参考方案5】:

如果您有更简单的要求将同一领域的孩子捆绑在一起,group_concat 是您的朋友。

非常感谢来自此线程的 Simon Slaver: http://sqlite.1065341.n5.nabble.com/Howto-pivot-in-SQLite-tp26766p26771.html

【讨论】:

来自Help Center:鼓励链接到外部资源,但请在链接周围添加上下文,以便您的其他用户了解它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。 链接失效

以上是关于如何在 SQLite 中进行透视或以宽格式选择以长格式存储的表?的主要内容,如果未能解决你的问题,请参考以下文章

如何设置Eclipse界面!

Excel PowerQuery:如何将巨大的表格反透视或转置为可读格式以进行分析

Excel:如何像数据透视表一样对一系列带有前导零的数字进行排序?

linux如果想直接使用ls命令时显示的就是文件的周详信息,即以长格式列出当前目录下所有内容,该如何操作?

在 Python Pandas 中以长格式附加列表元素

以长格式保存具有不同级别名称的多索引列 Pandas 为 excel 格式