SQLite:转置GROUP BY的结果并填写名称为

时间:2015-08-17 21:31:42

标签: python sqlite pivot

我的问题很具体,如果你有更好的头衔,请提出建议。此外,格式化很糟糕 - 不知道如何组合列表和代码块。

我有一个SQLite3数据库,其中包含以下内容(相关部分).schema:

CREATE TABLE users (id INTEGER PRIMARY KEY NOT NULL, user TEXT UNIQUE);
CREATE TABLE locations (id INTEGER PRIMARY KEY NOT NULL, name TEXT UNIQUE);
CREATE TABLE purchases (location_id INTEGER, user_id INTEGER);
CREATE TABLE sales (location_id integer, user_id INTEGER);

purchases有大约4.5百万个条目,users大约300k,sales大约100k,locations大约250 - 只是为了衡量数据量。

我希望的用途是生成一个JSON对象,以便传递给另一个应用程序,通过执行以下操作非常精简:

-GROUP将购买和销售分配到一个公共表BY location_id,user_id - IOW,获取每个用户每个位置的“操作”数。我可以做,结果就像是

loc  | usid  | loccount
-----------------------
1    | 1246  | 123
1    | 2345  | 1
13   | 1246  | 46
13   | 8732  | 4
27   | 2345  | 41

(至少看起来很好,总是很难说这些卷;查询:

select location_id,user_id,count(location_id) from
  (select location_id,user_id from purchases
     union all
   select location_id,user_id from sales)
group by location_id,user_id order by user_id`

- 然后,转移那张巨大的桌子让我得到:

usid | loc1 | loc13 | loc27
---------------------------
1246 | 123  | 46    | 0
2345 | 1    | 0     | 41
8732 | 0    | 4     | 0

我做不到,这是我对这个问题绝对关键的一点。我尝试了一些我在网上找到的东西,特别是在这里,但我刚刚开始使用SQLite,并且不了解很多查询。

- 最后,将表转换为纯文本,以便将其写入JSON:

user | AAAA | BBBBB | CCCCC
---------------------------
zeta | 123  | 46    | 0
beta | 1    | 0     | 41
iota | 0    | 4     | 0

可能可以做相当多的实验和内部联接,虽然我总是非常不确定以什么方式处理这样的数据量的最佳方法,因此我不介意指针。

如果重要的话,整个事情都是用Python的sqlite3接口编写的。最后,我希望有一些东西,我可以为每个用户做一个“for”循环,以生成JSON,这当然会非常简单。如果查询需要很长时间(< 10min会很好)并不重要,它每天只运行两次作为一种备份。我只有一个很小的VPS可用,但仅限于一个核心,性能与我相当强大的桌面一样好。 (i5-3570k运行Debian。)

表标题只是示例,因为我不太确定我是否可以为它们使用整数(如果没有发现语法),只要我能以某种方式查找数字部分位置表我很好。将用户ID转换为名称也是如此。列数是事先已知的 - 它们毕竟只是INTEGER PRIMARY KEYs,而我从其他一些操作中得到list()列。行数可以合理快速地确定,如果需要可以~3s。

1 个答案:

答案 0 :(得分:1)

考虑使用子查询来实现所需的转置输出:

SELECT DISTINCT m.usid,

   IFNULL((SELECT t1.loccount FROM tablename t1 
         WHERE t1.usid = m.usid AND t1.loc=1),0) AS Loc1,
   IFNULL((SELECT t2.loccount FROM tablename t2 
         WHERE t2.usid = m.usid AND t2.loc=13),0) AS Loc13,
   IFNULL((SELECT t3.loccount FROM tablename t3 
         WHERE t3.usid = m.usid AND t3.loc=27),0) AS Loc27

FROM tablename As m

或者,您可以使用嵌套的IF语句(或者在使用CASE/WHEN的SQLite的情况下)作为派生表:

SELECT temp.usid, Max(temp.loc1) As Loc1, 
       Max(temp.loc13) As Loc13, Max(temp.loc27) As Loc27
FROM    
   (SELECT tablename.usid, 
      CASE WHEN loc=1 THEN loccount ELSE 0 As Loc1 END,  
      CASE WHEN loc=13 THEN loccount ELSE 0 As Loc13 END, 
      CASE WHEN loc=27 THEN loccount ELSE 0 As Loc27 END
    FROM tablename) AS temp    
GROUP BY temp.usid