使用计算在模式中存储新的永久 table

Store new permanent table in schema using compute

我想使用 dbplyr 语法对某些 table 执行一些 JOIN / FILTER 操作并将结果存储回数据库 没有先收集它。

根据我的阅读,compute(..., temporary = FALSE, ...) 应该这样做,但是我很难为我想要的 table 提供完全限定名称(即 database.schema.table_name)商店

我知道 DBI::Iddbplyr::in_schema 但我不知道如何正确使用它们。尝试使用 sql 至少做了我想要的(创建了 table)但导致了(spurios?)错误。

我需要做什么?

一些NoReprex

library(DBI)
library(dbplyr)

con <- dbConnect(odbc::odbc(), "myserver")

## do __not__ collect the data
my_frame <- con %>%
   tbl(Id(catalog = "mydb", schema = "dbo", table = "mytable")) %>%
   inner_join(con %>% tbl(Id(catalog = "mydb", schema = "dbo", 
                             table = "yetanothertable")),
              "id")

compute(my_frame,
        # Id(catalog = "mydb", schema = "dbo", table = "mynewtable"), # (1)
        # in_schema("dbo", "mynewtable"),                             # (2),
        sql("mydb.dbo.mynewtable"),                                   # (3)
        FALSE)

根据我使用的变体,我得到不同的错误

# (1)
## Error in h(simpleError(msg, call)) : 
##   error in evaluating the argument 'conn' in selecting a method for function 
## 'dbQuoteIdentifier': argument "con" is missing, with no default

# (2)
## Error in escape(x$schema, con = con) : 
##   argument "con" is missing, with no default

# (3)
## Error: nanodbc/nanodbc.cpp:1655: 42000: [Microsoft][SQL Server Native Client 11.0][SQL Server]Incorrect syntax near ')'.  
##            [Microsoft][SQL Server Native Client 11.0][SQL Server]Statement(s) could not be prepared. 
## <SQL> 'SELECT *
## FROM (my.fully_qualified.name) "q02"
## WHERE (0 = 1)'

P.S.: 我真的希望能够用 fully 限定名称保存 table,包括数据库名称(虽然在这个简化的例子中是一样的)。所以 dbConnect(..., database = <somedb>) 不会解决我的问题 运行.

P.P.S:我正在寻找 compute 解决方案。我知道我可以自己构建 SQL,但我真的很想知道我是否可以为此使用 dbplyr 抽象层。

我以前更喜欢涉及编写一些 SQL 的解决方案(根据 回答)。但是当你在你的问题中排除这种方法时,我测试并找到了一种不用写 SQL.

的方法

我们将使用 db_compute 而不是 compute

  • computedocumentation 声明“compute() 将结果存储在远程临时 table”。所以我认为这意味着我们不能使用 compute.
  • 编写永久 tables
  • db_computedocumentation 说得很少。但它出现在 db_copy_to 旁边,其目的与我们正在寻找的相似。所以值得一试(而且有效)。

常规设置

library(DBI)
library(dplyr)
library(dbplyr)

# connect to database
connection_string = "..."
db_connection = dbConnect(odbc::odbc(), .connection_string = connection_string)

# remote table
remote_table = tbl(db_connection, from = in_schema("schema","table"))
top_rows = remote_table %>%
  head()

测试计算

top_rows %>% show_query()
# <SQL>
# SELECT TOP (6) *
# FROM [database_name].[schema_name].[table_name]

top_rows = top_rows %>%
  compute()
# Created a temporary table named: #dbplyr_002

top_rows %>% show_query()
# <SQL>
# SELECT *
# FROM #dbplyr_.002

所以我们可以看到 compute 写了一个临时的 table。因此,如果我们做一些复杂的处理(而不是只取前几行),compute 将是存储处理后的 table 的有效方法,这样我们就可以避免每次查询时重复复杂的处理。

但是因为它是临时的,当我们与数据库断开连接时 table 应该消失:DBI::dbDisconnect(db_connection).

测试中db_compute

out = db_compute(
  con = db_connection,
  table = in_schema("schema","new_table"),
  sql = sql_render(top_rows),
  temporary = FALSE
)

out
# <IDENT> database_name.schema_name.new_table

# reconnect
new_remote_table = tbl(db_connection, from = in_schema("schema","new_table"))

所以我们现在可以从 R 中访问新的(永久的)table。我还通过 SQL 查询检查并确认 table 存在于数据库中。

注意由于db_compute的文档很少,不清楚是否打算以这种方式使用。我已经测试了上面的内容并且它有效。但如果没有额外的文档,使用风险自负。

仅作记录,感谢 Simon 的回答,我终于找到了提供完全限定 table 名称(即包含数据库、架构和 table 名称)的正确方法。有了这条信息,一个人可以依靠 compute,一切都像一个魅力。您只需提供数据库名称作为模式的一部分,并确保通过 sql:

进行转义
compute(my_frame,
        # in_schema("mydb.dbo", "mynewtable") would __not__ work
        in_schema(sql("mydb.dbo"), "mynewtable"), 
        FALSE)