在SQL Server中结合dbplyr和case_when

时间:2018-08-23 03:51:24

标签: r sql-server dbplyr

我正在使用dbplyr在SQL Server中编写和运行查询,并且想要应用条件化的mutate。可以使用ifelse或使用case_when来完成。使用ifelse时查询有效,但使用case_when时抛出异常。

问题似乎出在这两个命令被翻译成的SQL语法中。 case_when语法似乎不是有效的SQL。您能告诉我原因以及如何解决吗?还是这是一个错误?

# libraries
library(DBI)
library(dplyr)
library(dbplyr)

# establish connection to database table
connection_string = "database.specific.string"
# mine looks something like "DRIVER=...; Trusted_Connection=...; DATABASE=...' SERVER=..."
db_connection = dbConnect(odbc::odbc(), .connection_string = connection_string)
my_table = tbl(db_connection, from = my_table_name)

# attempted query
tmp = my_table %>%
    mutate(new_col = case_when(col1 == col2 ~ "a",
                               col1 != col2 ~ "b"))

# check SQL code for query
show_query(tmp)

生成的SQL查询为:

SELECT 
    col1, col2,
    CASE
       WHEN CONVERT(BIT, IIF(col1 = col2, 1.0, 0.0))) THEN ('a')
       WHEN CONVERT(BIT, IIF(col1 <> col2, 1.0, 0.0))) THEN ('b')
    END AS new_col
FROM my_database.my_table_name

运行此代码会引发错误

  

在需要条件的上下文中在“ THEN”附近指定的非布尔类型的表达式

但是ifelse查询可以按预期工作:

# attempted query
tmp = my_table %>%
    mutate(new_col = ifelse(col1 == col2, "a", "b"))

# check SQL code for query
show_query(tmp)

生成的SQL查询为:

SELECT 
    col1, col2,
    CASE
       WHEN (CONVERT(BIT, IIF(col1 = col2, 1.0, 0.0))) = TRUE) THEN ('a')
       WHEN (CONVERT(BIT, IIF(col1 = col2, 1.0, 0.0))) = FALSE) THEN ('b')
    END AS new_col
FROM my_database.my_table_name

请注意,在两种情况下,SQL语法都是使用show_query生成的。始终使用translate_sql生成SQL代码可以产生更简洁的SQL语法,但这不是在服务器上运行的语法。

其他人还会得到这些SQL查询吗?关于什么是错误以及如何解决此问题的任何建议?

已更新

发布为issue on the tidyverse,并获悉已经开发了一种解决方案,用于将case_when(..., TRUE ~ "b")转换为ELSE 'b'here)。

但是,因为这不能解决导致此异常的语法。对问题进行了编辑,以重点关注引起问题的语法。

更新2

发布为issue on dbplyr。 Christophe Dervieux(cderv)的答复指出,原因似乎是SQL服务器需要像case_when那样对ifelse进行特殊翻译。

同时,用户可以使用多个ifelseif_else语句。

1 个答案:

答案 0 :(得分:0)

仅仅是因为您的dplyr语法有点错误?

尝试一下

# attempted query
tmp = my_table %>%
    mutate(new_col = case_when(col1 == col2 ~ "a",
                               col1 == 'TRUE' ~ "b"
              # alternatively  col1 == 1 ~ "b"
))