更新 SQLite 数据库而不在本地下载数据
Update SQLite database without downloading data locally
我有以下 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
第 3 步:在 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 步:如何在不先将数据下载到本地的情况下将这些结果直接传输到数据库(覆盖数据库)?
一般方法
让我从我会使用的一般方法开始:在每个新的一天更新 'latest' table。
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)
我推荐这种方法,因为在每次计算时你只需要两个 tables:你存储所有细节的 table 和你用来更新的 table它。这比每次更新的所有每日数据文件要处理的数据要少得多。
假设您想在 SQLite 中执行此操作?
我上面的更新函数中的代码应该可以通过 dbplyr
从 R 轻松转换为 SQLite。假设 existing_table
和 next_day_table
都已经在 SQLite.
但是 dbplyr
不会将生成的 table 保存为对象。因此,如果您调用 new_table = update(existing_table, next_day_table)
,那么 new_table
将由 dbplyr
用于构造它的 SQL 命令定义。
要将其另存为 table,您可能需要如下内容:
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
。然后,您必须删除现有的 table first_appeared
,并重命名 new_first_appeared
.
您还能考虑什么?
根据您的上下文,您 first_appeared
table 中的现有记录一旦创建可能不会更改。如果是这种情况,那么与其重写整个 table,不如考虑只识别新记录并将它们附加到现有的 table.
为此,您可能需要 SQLite 中的 INSERT INTO first_appeared_table SELECT * FROM table
模式。您还需要将查询更改为 return 仅新记录。
我有以下 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
第 3 步:在 SQLite
中计算 min_dttbl(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 步:如何在不先将数据下载到本地的情况下将这些结果直接传输到数据库(覆盖数据库)?
一般方法
让我从我会使用的一般方法开始:在每个新的一天更新 'latest' table。
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)
我推荐这种方法,因为在每次计算时你只需要两个 tables:你存储所有细节的 table 和你用来更新的 table它。这比每次更新的所有每日数据文件要处理的数据要少得多。
假设您想在 SQLite 中执行此操作?
我上面的更新函数中的代码应该可以通过 dbplyr
从 R 轻松转换为 SQLite。假设 existing_table
和 next_day_table
都已经在 SQLite.
但是 dbplyr
不会将生成的 table 保存为对象。因此,如果您调用 new_table = update(existing_table, next_day_table)
,那么 new_table
将由 dbplyr
用于构造它的 SQL 命令定义。
要将其另存为 table,您可能需要如下内容:
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
。然后,您必须删除现有的 table first_appeared
,并重命名 new_first_appeared
.
您还能考虑什么?
根据您的上下文,您 first_appeared
table 中的现有记录一旦创建可能不会更改。如果是这种情况,那么与其重写整个 table,不如考虑只识别新记录并将它们附加到现有的 table.
为此,您可能需要 SQLite 中的 INSERT INTO first_appeared_table SELECT * FROM table
模式。您还需要将查询更改为 return 仅新记录。