Python 使用SQLAlchemy查询填写缺少的月度数据

Python 使用SQLAlchemy查询填写缺少的月度数据,python,sqlite,sqlalchemy,Python,Sqlite,Sqlalchemy,我有一个数据库表,它列出了玩游戏的时间。我现在查询这个表来收集数据,以显示在一个图表中,这样就可以很好地了解过去一年每月玩的游戏数量 以下方法效果很好,直到新冠病毒-19锁定我们,我们开始看到数月零游戏:-/ data_db = list( games.group_by("year")

我有一个数据库表,它列出了玩游戏的时间。我现在查询这个表来收集数据,以显示在一个图表中,这样就可以很好地了解过去一年每月玩的游戏数量

以下方法效果很好,直到新冠病毒-19锁定我们,我们开始看到数月零游戏:-/

data_db = list(                                                             
      games.group_by("year")                                                  
      .group_by("month")                                                      
      .order_by(desc("year"))                                                 
      .order_by(desc("month"))                                                
      .limit(12)                                                              
      .values(                                                                
          func.extract("year", Game.created).label("year"),                   
          func.extract("month", Game.created).label("month"),                 
          func.count().label("games"),                                        
      )                                                                          
)
在哪里

游戏是一个炼金术模型

这导致数据的形式为
[(2020,2,20),(2020,3,65),(2020,5,3),…]
,本例中没有2020年4月的数据。然后,生成的条形图将显示所有非零月份的数据,当然不能真实显示现实情况

我开始研究通过加入动态生成的日历表来扩展SQLAlchemy查询以包括零个月的方法,但实际上并没有走多远。顺便说一下,底层数据库是SQLite

我现在已经用Python“解决”了如下情况:

  data_raw = []                                                               
  for date in rrule.rrule(                                                    
          rrule.MONTHLY,                                                      
          dtstart=datetime.now() - relativedelta.relativedelta(months=11),    
          until=datetime.now(),                                               
  ):                                                                          
      for dbdata in data_db:                                                  
          if dbdata[0:2] == (date.year, date.month):                          
              data_raw.append(dbdata)                                         
              break                                                           
      else:                                                                   
          data_raw.append((date.year, date.month, 0))
这当然有效,但让我嘴里有点酸味

由于数据集非常小,这与其说是一个真正的性能问题,不如说是一个镀金的问题,但我还是想看看是否有一个SQLAlchemy唯一的解决方案


(我想图形库(在本例中是Chart.js)也可以用来填补空白,但我没有深入研究。)

您可以创建一个包含所有年/月对(包括缺少的年/月对)的临时表,然后将其与聚合查询(作为
.subquery()
)连接起来:

导入日期时间
从sqlalchemy导入(
创建引擎,
桌子
元数据,
专栏,
整数,
日期时间,
描述,
func,
而且,,
)
从sqlalchemy.orm导入声明性_基,会话
引擎=创建引擎(
“sqlite://:内存:”,
未来=真实,
回声=真,
)
Base=声明性_Base()
班级游戏(基本):
__tablename_=“游戏”
id=列(整数,主键=True)
已创建=列(日期时间)
房间=列(整数)
无玩家=列(整数)
started=列(日期时间)
Base.metadata.create_all(引擎)
#创建要查询的测试数据
将会话(引擎,future=True)作为会话:
session.add_all(
[
游戏(
created=datetime.datetime.now(),
房间=1,
无玩家=2,
started=datetime.datetime.now(),
),
游戏(
created=datetime.datetime(2021,3,4,3,2,1),
房间=1,
无玩家=2,
started=datetime.datetime(2021,3,4,5,6,7),
),
游戏(
created=datetime.datetime(2021,1,1,1,1,1),
房间=1,
没有玩家=3,
started=datetime.datetime(2021,1,1,1,2,3),
),
]
),
session.commit()
#定义要插入的临时表结构和数据
tmp\u tbl\u月数=表(
“tmp tbl月”,
元数据(),
列(“年”,整数,主键=True,自动递增=False),
列(“月”,整数,主键=True,自动递增=False),
前缀=[“临时”],
)
当前日期=datetime.date.today()
循环年=当前日期年
循环月份=当前日期月份
tmp_数据=[]
报告的月数=4
对于范围内的i(num\u months\u到报告):
tmp_data.append({“year”:loop_year,“month”:loop_month})
如果循环_月==1:
循环_月=12
循环年-=1
其他:
循环_月-=1
打印(tmp_数据)
# [
#{'year':2021,'month':3},
#{'year':2021,'month':2},
#{'year':2021,'month':1},
#{‘年’:2020,‘月’:12}
# ]
使用engine.begin()作为conn:
tmp_tbl_月。创建(康涅狄格州)
conn.execute(tmp\u tbl\u months.insert(),tmp\u数据)
试验室=1#
games=会话.query(游戏).filter(
Game.started.isnot(无),Game.no_玩家。isnot(-1),Game.room==room
)
聚合=(
奥运会。分组依据(“年度”)
.分组依据(“月份”)
.订购人(描述(“年份”))
.订购人(描述(“月份”))
.限额(12)
.与联合国实体(
函数摘录(“年”,游戏创建)。标签(“年”),
函数提取(“月”,游戏创建)。标签(“月”),
func.count()标签(“游戏”),
)
.subquery()
)
#临时任务剩余月加入聚合(子查询)
数据=列表(
session.query()
.从中选择(tmp\U tbl\U月)
.外套(
聚合,
及_(
聚合c.year==tmp\u tbl\u月c.year,
聚合月=tmp\u tbl\u月,
),
)
.与联合国实体(
tmp_tbl_月c.年,
tmp_tbl_月c月,
函数合并(聚合c.games,0),
)
)
打印(数据_db)
# [(2021, 3, 2), (2021, 2, 0), (2021, 1, 1), (2020, 12, 0)]

Waw,非常感谢Gord!最后的
合并
很好。虽然我可以看到这肯定是可行的,但我对它所需要的额外代码数量感到惊讶。我想我有点希望在查询中动态生成这个临时表。我还查看了中的修饰符,并正在考虑使用SQLAlchemy方法访问这些修饰符。无论如何,非常感谢您为熟悉我介绍的案例所付出的努力!
  data_raw = []                                                               
  for date in rrule.rrule(                                                    
          rrule.MONTHLY,                                                      
          dtstart=datetime.now() - relativedelta.relativedelta(months=11),    
          until=datetime.now(),                                               
  ):                                                                          
      for dbdata in data_db:                                                  
          if dbdata[0:2] == (date.year, date.month):                          
              data_raw.append(dbdata)                                         
              break                                                           
      else:                                                                   
          data_raw.append((date.year, date.month, 0))