我可以在R中使用以下代码在任何通用SQL数据库中选择不同的行。我会使用dplyr::distinct()
,但SQL语法不支持它。无论如何,这确实有效:
dbGetQuery(database_name,
"SELECT t.*
FROM (SELECT t.*, ROW_NUMBER() OVER (PARTITION BY column_name ORDER BY column_name) AS SEQNUM
FROM table_name t
) t
WHERE SEQNUM = 1;")
我一直在成功使用它,但是想知道如何在其他dplyr步骤之后传递相同的SQL查询,而不是像上面所示那样仅将其用作第一步。最好用一个例子来说明:
distinct.df <-
left_join(sql_table_1, sql_table_2, by = "col5") %>%
sql("SELECT t.*
FROM (SELECT t.*, ROW_NUMBER() OVER (PARTITION BY column_name ORDER BY column_name) AS SEQNUM
FROM table_name t
) t
WHERE SEQNUM = 1;")
因此,我dplyr::left_join()
有两个SQL表,然后我要查看不同的行,并保留所有列。是否如上所述将SQL代码通过管道传递到R中(仅使用sql()
函数)?如果是的话,table_name
行中的FROM table_name t
应该使用什么?
在第一个示例中,我使用从中提取的实际表名。太明显了!但是在这种情况下,我正在使用管道,并且习惯了使用rlang的magrittr代词.
或有时使用rlang的.data
代词,如果我当时在内存中在没有数据库的R中工作。
虽然我在SQL数据库中...所以如何处理这种情况?我如何正确地将已知有效的SQL管道传送到我的R代码中(使用适当的表名代词)? dbplyr's reference page是一个很好的起点,但是并不能真正回答这个特定问题。
答案 0 :(得分:2)
您似乎希望将自定义SQL代码与dbplyr
中自动生成的SQL代码结合使用。为此,重要的是要区分:
DBI::db*
命令-在数据库上执行提供的SQL并返回结果。dbplyr
转换-您可以在其中远程连接到表您只能以某些方式组合这些。下面根据您的特定用例给出了几个示例。所有人都假设DISTINCT
是您的特定SQL环境中可接受的命令。
如果您会自我推广,我建议您看一下我的dbplyr_helpers
GitHub存储库(here)。这包括:
union_all
函数接收通过dbplyr
访问的两个表,并使用一些自定义SQL代码输出一个表。write_to_datebase
函数,该函数接受通过dbplyr
访问的表并将其转换为可以通过DBI::dbExecute
执行的代码 dbplyr
在使用定义了SQL转换的标准dplyr
动词时会自动将代码传递给下一个查询。只要定义了sql转换,您就可以将许多管道(我一次使用10个或更多)链接在一起,(唯一)缺点是sql转换的查询很难被人阅读。
例如,考虑以下内容:
library(dbplyr)
library(dplyr)
tmp_df = data.frame(col1 = c(1,2,3), col2 = c("a","b","c"))
df1 = tbl_lazy(tmp_df, con = simulate_postgres())
df2 = tbl_lazy(tmp_df, con = simulate_postgres())
df = left_join(df1, df2, by = "col1") %>%
distinct()
当您随后调用show_query(df)
时,R返回以下自动生成的SQL代码:
SELECT DISTINCT *
FROM (
SELECT `LHS`.`col1` AS `col1`, `LHS`.`col2` AS `col2.x`, `RHS`.`col2` AS `col2.y`
FROM `df` AS `LHS`
LEFT JOIN `df` AS `RHS`
ON (`LHS`.`col1` = `RHS`.`col1`)
) `dbplyr_002`
但格式不正确。请注意,初始命令(左联接)以嵌套查询的形式出现,外部查询中的字母与众不同。因此,df
是到上述sql查询定义的远程数据库表的R链接。
您可以将dbplyr
传递到自定义SQL函数中。管道意味着被管道传输的东西成为接收函数的第一个参数。
custom_distinct <- function(df){
db_connection <- df$src$con
sql_query <- build_sql(con = db_connection,
"SELECT DISTINCT * FROM (\n",
sql_render(df),
") AS nested_tbl"
)
return(tbl(db_connection, sql(sql_query)))
}
df = left_join(df1, df2, by = "col1") %>%
custom_distinct()
当您随后调用show_query(df)
时,R应该返回以下SQL代码(我说“应该”,因为我无法在模拟的SQL连接中使用此代码),但格式却不正确:
SELECT DISTINCT * FROM (
SELECT `LHS`.`col1` AS `col1`, `LHS`.`col2` AS `col2.x`, `RHS`.`col2` AS `col2.y`
FROM `df` AS `LHS`
LEFT JOIN `df` AS `RHS`
ON (`LHS`.`col1` = `RHS`.`col1`)
) nested_tbl
与前面的示例一样,df
是指向由上述sql查询定义的远程数据库表的R链接。
您可以从现有的dbplyr
远程表中获取代码,并将其转换为可以使用DBI::db*
执行的字符串。
作为编写独特查询的另一种方式:
df1 = tbl_lazy(tmp_df, con = simulate_postgres())
df2 = tbl_lazy(tmp_df, con = simulate_postgres())
df = left_join(df1, df2, by = "col1")
custom_distinct2 = paste0("SELECT DISTINCT * FROM (",
as.character(sql_render(df)),
") AS nested_table")
local_table = dbGetQuery(db_connection, custom_distinct2)
根据先前的示例,这将使用等效的sql命令返回本地R数据帧。
答案 1 :(得分:1)
如果要对dbplyr操作的结果进行自定义SQL处理,首先使用compute()
可能会很有用,它会使用数据库中的结果集创建一个新表(临时表或永久表)。下面的reprex显示了如果依赖自动生成,如何访问新生成的表的名称。 (请注意,这依赖于dbplyr内部,如有更改,恕不另行通知-也许最好对表进行命名。)然后,照常使用dbGetQuery()
。
library(tidyverse)
library(dbplyr)
#>
#> Attaching package: 'dbplyr'
#> The following objects are masked from 'package:dplyr':
#>
#> ident, sql
lazy_query <-
memdb_frame(a = 1:3) %>%
mutate(b = a + 1) %>%
summarize(c = sum(a * b, na.rm = TRUE))
lazy_query
#> # Source: lazy query [?? x 1]
#> # Database: sqlite 3.30.1 [:memory:]
#> c
#> <dbl>
#> 1 20
lazy_query_computed <-
lazy_query %>%
compute()
lazy_query_computed
#> # Source: table<dbplyr_002> [?? x 1]
#> # Database: sqlite 3.30.1 [:memory:]
#> c
#> <dbl>
#> 1 20
lazy_query_computed$ops$x
#> <IDENT> dbplyr_002
由reprex package(v0.3.0)于2020-01-01创建
如果您的SQL方言支持CTE,那么您也可以提取查询字符串并将其用作自定义SQL的一部分,也许类似于Simon的建议。
library(tidyverse)
library(dbplyr)
#>
#> Attaching package: 'dbplyr'
#> The following objects are masked from 'package:dplyr':
#>
#> ident, sql
lazy_query <-
memdb_frame(a = 1:3) %>%
mutate(b = a + 1) %>%
summarize(c = sum(a * b, na.rm = TRUE))
sql <-
lazy_query %>%
sql_render()
cte_sql <-
paste0(
"WITH my_result AS (", sql, ") ",
"SELECT c + 1 AS d FROM my_result"
)
cte_sql
#> [1] "WITH my_result AS (SELECT SUM(`a` * `b`) AS `c`\nFROM (SELECT `a`, `a` + 1.0 AS `b`\nFROM `dbplyr_001`)) SELECT c + 1 AS d FROM my_result"
DBI::dbGetQuery(
lazy_query$src$con,
cte_sql
)
#> d
#> 1 21
由reprex package(v0.3.0)于2020-01-01创建