【问题标题】:Update SQLite database without downloading data locally更新 SQLite 数据库而不在本地下载数据
【发布时间】:2019-09-05 17:57:05
【问题描述】:

我有以下 2 个数据框:

library(tidyverse)
library(RSQLite)

df1 <- data.frame(user_id=c("A","B","C"), 
                  transaction_date=c("2019-01-01","2019-01-01","2019-01-01"))

df2 <- data.frame(user_id=c("C","D","E"), 
                  transaction_date=c("2019-01-03","2019-01-03","2019-01-03"))

df1
df2

#    user_id    transaction_date
#    <fct>      <fct>
#    A          2019-01-01
#    B          2019-01-01
#    C          2019-01-01

#    user_id    transaction_date
#    <fct>      <fct>
#    C          2019-01-03
#    D          2019-01-03
#    E          2019-01-03

我想找出每个 user_id 的最短交易日期。我可以这样做:

rbind(df1, df2) %>%
group_by(user_id) %>%
summarise(min_dt=min(transaction_date %>% as.Date()))

#    user_id    min_dt
#    <fct>      <date>
#    A          2019-01-01
#    B          2019-01-01
#    C          2019-01-01
#    D          2019-01-03
#    E          2019-01-03

问题是我有 100 个数据帧(每天 1 个)和每个数据帧数百万行。每次我引入新的 user_id 并计算 min_dt 时,user_id 的列表都会增长。因此,随着时间的推移,整个过程变得非常缓慢。 问题:1) 在 SQLite 中运行计算会更快吗? 2)如果是这样,我怎样才能在不每次都在本地下载数据的情况下做到这一点?

这是我尝试过的。

第 1 步:从 df1 创建数据库:

db <- dbConnect(SQLite(), dbname = "user_db.sqlite")

dbWriteTable(conn = db, name = "first_appeared", value = df1, append=TRUE)
tbl(db, "first_appeared")

##  Source:   table<first_appeared> [?? x 2]
##  Database: sqlite 3.29.0 [user_db.sqlite]
#   user_id transaction_date
#   <chr>   <chr>           
# 1 A       2019-01-01      
# 2 B       2019-01-01      
# 3 C       2019-01-01 

第 2 步:附加 df2:

dbWriteTable(conn = db, name = "first_appeared", value = df2, append=TRUE)
tbl(db, "first_appeared")

##  Source:   table<first_appeared> [?? x 2]
##  Database: sqlite 3.29.0 [/Volumes/GoogleDrive/My Drive/Ad hoc/201908 v2
#   mapper/user_db.sqlite]
#   user_id transaction_date
#   <chr>   <chr>           
# 1 A       2019-01-01      
# 2 B       2019-01-01      
# 3 C       2019-01-01      
# 4 C       2019-01-03      
# 5 D       2019-01-03      
# 6 E       2019-01-03 

第三步:在 SQLite 中计算 min_dt

tbl(db, "first_appeared") %>%
group_by(user_id) %>%
summarise(first_appeared=min(transaction_date))

dbDisconnect(db)            # Close connection

##  Source:   lazy query [?? x 2]
##  Database: sqlite 3.29.0 [/Volumes/GoogleDrive/My Drive/Ad hoc/201908 v2
##  mapper/user_db.sqlite]
#   user_id first_appeared
#   <chr>   <chr>         
# 1 A       2019-01-01    
# 2 B       2019-01-01    
# 3 C       2019-01-01    
# 4 D       2019-01-03    
# 5 E       2019-01-03  

第 4 步:如何在不先将数据下载到本地的情况下将这些结果直接传输到数据库(覆盖数据库)?

【问题讨论】:

标签: r rsqlite dbplyr


【解决方案1】:

一般方法

让我从我将使用的一般方法开始:每天更新一个“最新”表。

update = function(existing_table, new_day_table){
  new_existing_table = existing_table %>%
    full_join(new_day_table, by = "user_id", suffix = c("_exist","_new") %>%
    mutate(transaction_date = ifelse(test = !is.na(transaction_date_exist)
                                            & (is.na(transaction_date_new)
                                               | transaction_date_exist < transaction_date_new ), 
                                     yes = transaction_date_exist,
                                     no = transaction_date_new)) %>%
    select(user_id, transaction_date)
}

在 R 中,你会每天运行这个函数:

existing_table = update(existing_table, next_day_table)

我推荐这种方法,因为在每次计算时您只需要两个表:存储所有详细信息的表和用于更新它的表。这比每次更新的所有每日数据文件要处理的数据要少得多。

假设您想在 SQLite 中执行此操作?

上面我的更新函数中的代码应该很容易通过dbplyr 从 R 转换为 SQLite。假设 existing_tablenext_day_table 都已经在 SQLite 中了。

但是dbplyr 不会将结果表保存为对象。因此,如果您调用new_table = update(existing_table, next_day_table),那么new_table 将由dbplyr 用来构造它的SQL 命令定义。

要将其保存为表格,您可能需要以下内容:

sql_query = paste("CREATE TABLE new_first_appeared AS\n"
                  ,as.character(sql_render(new_table))
)
dbExecute(db_connection, sql_query)

请注意,您必须写入数据库中的new_first_appeared。您不能直接覆盖first_appeared,因为new_first_appeared 的定义取决于first_appeared。然后你必须删除现有的表first_appeared,并重命名new_first_appeared

您还能考虑什么?

根据您的上下文,first_appeared 表中的现有记录在创建后可能不会更改。如果是这种情况,那么与其重写整个表,不如只考虑识别新记录并将它们附加到现有表中。

为此,您可能需要 SQLite 中的 INSERT INTO first_appeared_table SELECT * FROM table 模式。您还需要更改查询以仅返回新记录。

【讨论】:

    猜你喜欢
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多