在SQLite中转动

ara*_*ams 30 sqlite

我想在一个查询中找到一张表格,其中显示了学生以及他们为所有科目收到的分数.

这是我的表结构:

表: markdetails

## studid ## ## subjectid ##  ## marks ##
     A1            3                50
     A1            4                60
     A1            5                70
     B1            3                60
     B1            4                80
     C1            5                95
Run Code Online (Sandbox Code Playgroud)

表: student info

实际结构:

## studid ##  ## name ##
      A1          Raam
      B1          Vivek
      c1          Alex
Run Code Online (Sandbox Code Playgroud)

我希望结果集看起来像这样:

表: 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
Run Code Online (Sandbox Code Playgroud)

我怎样才能在SQLite中实现这一目标?

har*_*dsv 27

由于作者不太友好地给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');
Run Code Online (Sandbox Code Playgroud)

下面是使用一种替代的解决方案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
;
Run Code Online (Sandbox Code Playgroud)

为了比较,这里是@ Eric的解决方案中的相同select语句:

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
;
Run Code Online (Sandbox Code Playgroud)

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

  • 我有机会在一张约150,000行的桌子上进行测试.一个复杂因素是我事先不知道列数,所以我必须做一些预处理以确定所需列数.此外,并非所有行都具有相同数量的数据.使用外连接方法,我的PC花了50秒.在方法的情况下,花了15秒.使用reshape2和plyr的组合(我使用R来运行sqlite),花了大约1,040秒.但是,里程可能会有所不同. (8认同)
  • @haridsv 很好,更好的答案。另外,请阅读:https://modern-sql.com/use-case/pivot (2认同)

Eri*_*ric 24

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

alter table student_info rename to student_name
Run Code Online (Sandbox Code Playgroud)

然后,您将要重新创建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
)
Run Code Online (Sandbox Code Playgroud)

然后,填充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
Run Code Online (Sandbox Code Playgroud)

现在,只需删除临时表:

drop table student_temp
Run Code Online (Sandbox Code Playgroud)

这就是你如何快速更新你的桌子.

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

  • 这可能是第一行的拼写错误.也许应该将`alter table student_info重命名为student_temp`而不是? (5认同)
  • _“现在,只需删除你的临时表:`drop table Student_temp`”_,这个`student_temp`临时表来自哪里,Eric? (2认同)

McP*_*ppr 8

很棒的附录!帮助我以低成本和系统负载解决类似的问题.我使用Raspberry Pi获取1wire接口DS18B20温度传感器数据如下:

CREATE TABLE temps (Timestamp DATETIME, sensorID TEXT, temperature NUMERIC);
Run Code Online (Sandbox Code Playgroud)

例:

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
Run Code Online (Sandbox Code Playgroud)

使用SUBSTR()命令我将时间戳"规范化"为10分钟.使用JOIN,使用查找表'传感器'将sensorID更改为SensorName

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

例:

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
Run Code Online (Sandbox Code Playgroud)

现在,神奇的情况发生在上面提到的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;
Run Code Online (Sandbox Code Playgroud)

例:

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
Run Code Online (Sandbox Code Playgroud)

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

尽管如此,非常感谢你对这个问题的答案!


Rad*_*luk 5

感谢@pospec4444 的链接,这里是@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
;
Run Code Online (Sandbox Code Playgroud)