【问题标题】:Handle dates with dbplyr using pure R使用纯 R 使用 dbplyr 处理日期
【发布时间】:2021-04-04 07:01:05
【问题描述】:

dbplyr 将 dplyr 和 base R 命令转换为 SQL,以便开发人员可以编写 R 代码并在数据库中执行它 (tidyverse reference)。在 R 中处理日期时,通常使用 lubridate 包。但是,目前不存在 lubridate 函数的 dbplyr 翻译。因此,使用 dbplyr 的开发人员需要找到处理日期的替代方法。

我之前的方法是在我的 dplyr 命令中使用 SQL 语法片段(例如,请参阅答案:herehere)。但是,这需要开发人员知道(或找出)相应的 SQL 命令,而 dbplyr 的部分意义在于它会为您转换为 SQL。

这让我问:仅在连接到远程数据库时使用 dbplyr 翻译来操作日期的最佳方法是什么?

理想的解决方案是:

  • 仅使用 dbplyr 翻译,因此无法使用没有 dbplyr 翻译的函数。
  • 使用纯 R,没有 SQL 片段。
  • 在数据库上运行,所以使用远程表而不是本地表。

我认为至少我们应该能够:

  • 提取年、月、日
  • 将年、月、日组合成一个新日期

您可以通过这些手动执行其他操作,例如:

  • 增加一个日期
  • 找出两个日期之间的差异
  • 查找月末日期

但是更快/优雅的方式来执行这些更高级的操作会更好。

【问题讨论】:

  • 在 PostgreSQL 上,至少使用RPostgres,您可以很容易地使用lubridate 函数,如图here。我认为通过这种方式可以完成您要求的所有事情。如果这不适用于其他 SQL 实现(例如 MySQL),则可能需要对相关包进行处理(例如,在 RMySQL 中)。

标签: r dplyr dbplyr


【解决方案1】:

一个答案是,这在很大程度上已经成为可能。 (见答案here。)

如果dbplyr 中缺少所需的函数,一种想法是编写拉取请求,将lubridate 函数的更多翻译添加到dbplyr 的后端。

似乎翻译不可避免地是特定于后端的。如果您查看 PostgreSQL 后端 here,您会看到一些 lubridate 函数(例如,monthquarter)在那里提供了翻译,但其他(例如,ymd)没有。

【讨论】:

  • 谢谢。看起来这其中的一些取决于所使用的 SQL 的特定风格。要么通过特定的包(例如,RPostgres,当根据您的第一个链接使用 PostgreSQL 数据库时),或者因为dbplyr 有一些 SQL 风格的翻译,但没有其他风格(根据你的第二个链接)。因此,开发人员应首先尝试lubridate 方法,只有在失败时才采用变通方法。
【解决方案2】:

想到的第一个方法是将日期转换为文本,因为已经有针对不同形式的文本操作的 dbplyr 翻译。这种方法依赖于as.character 将日期转换为字符,并依赖substr 将年、月或日提取为文本。然后可以将其转换为数字并进一步操作。

(1) 设置模拟数据库连接以测试翻译(选择您喜欢的 SQL 风格):

library(dplyr)
library(dbplyr)

df = data.frame(start_dates = c('2020-01-31', '2020-02-28', '2020-03-31'))

# simulate a connection to test translation (pick your preferred flavor)
df = tbl_lazy(df, con = simulate_mssql())
# df = tbl_lazy(df, con = simulate_hive())
# df = tbl_lazy(df, con = simulate_impala())
# df = tbl_lazy(df, con = simulate_oracle())
# df = tbl_lazy(df, con = simulate_postgres())
# df = tbl_lazy(df, con = simulate_mysql())
# df = tbl_lazy(df, con = simulate_sqlite())

(2) 示例——提取日期分量,递增年份,重新组合:

output = df %>%
  mutate(text_date = as.character(start_dates)) %>%
  mutate(text_year = substr(text_date, 1, 4),
         text_month = substr(text_date, 6, 7),
         text_day = substr(text_date, 9, 10)) %>%
  mutate(num_year = as.numeric(text_year),
         num_month = as.numeric(text_month),
         num_day = as.numeric(text_day)) %>%
  select(start_dates, num_year, num_month, num_day) %>%
  mutate(next_year = num_year + 1) %>%
  mutate(next_year_text_date = paste0(next_year, '-', num_month, '-', num_day)) %>%
  mutate(next_year_date = as.Date(next_year_text_date)) %>%
  select(start_dates, next_year_date)

调用show_query(output) 然后给出以下翻译,但格式不那么好。我知道嵌套查询不被认为是良好的 SQL 做法,但这就是 dbplyr 翻译的工作原理。

SELECT `start_dates`
    , TRY_CAST(`next_year_text_date` AS DATE) AS `next_year_date`
FROM (
    SELECT `start_dates`
        , `num_year`
        , `num_month`
        , `num_day`
        , `next_year`
        , `next_year` + '-' + `num_month` + '-' + `num_day` AS `next_year_text_date`
    FROM (
        SELECT `start_dates`
            , `num_year`
            , `num_month`
            , `num_day`
            , `num_year` + 1.0 AS `next_year`
        FROM (
            SELECT `start_dates`
                , TRY_CAST(`text_year` AS FLOAT) AS `num_year`
                , TRY_CAST(`text_month` AS FLOAT) AS `num_month`
                , TRY_CAST(`text_day` AS FLOAT) AS `num_day`
            FROM (
                SELECT `start_dates`
                    , `text_date`
                    , SUBSTRING(`text_date`, 1, 4) AS `text_year`
                    , SUBSTRING(`text_date`, 6, 2) AS `text_month`
                    , SUBSTRING(`text_date`, 9, 2) AS `text_day`
                FROM (
                    SELECT `start_dates`
                        , TRY_CAST(`start_dates` AS VARCHAR(MAX)) AS `text_date`
                    FROM `df`
                ) `q01`
            ) `q02`
        ) `q03`
    ) `q04`
) `q05`

(3) 提取组件,压缩:

output = df %>%
  mutate(num_year = as.numeric(substr(as.character(start_dates), 1, 4)),
         num_month = as.numeric(substr(as.character(start_dates), 6, 7)),
         num_day = as.numeric(substr(as.character(start_dates), 9, 10)))

使用show_query(output) 的 SQL 翻译要短得多:

SELECT `start_dates`
    , TRY_CAST(SUBSTRING(TRY_CAST(`start_dates` AS VARCHAR(MAX)), 1, 4) AS FLOAT) AS `num_year`
    , TRY_CAST(SUBSTRING(TRY_CAST(`start_dates` AS VARCHAR(MAX)), 6, 2) AS FLOAT) AS `num_month`
    , TRY_CAST(SUBSTRING(TRY_CAST(`start_dates` AS VARCHAR(MAX)), 9, 2) AS FLOAT) AS `num_day`
FROM `df`

希望这适用于 dbplyr 可以转换的所有 SQL 风格。由于我无法访问每种 SQL 风格来对其进行测试,因此来自在特定 SQL 风格上测试过它的人的 cmets 会很有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 1970-01-01
    • 2012-08-23
    相关资源
    最近更新 更多