Comments (8)
Hi, on the surface, it looks like the strategy that helped us resolve the use of is_null()
as a filter()
and as a mutate()
may also need to be implemented in case_when()
. I'll take a look.
from dbplyr.
@cderv commented on Aug 27, 2018, 6:01 AM UTC:
First, this is a {dbplyr} issue. When you have one, please fill them in dbplyr GH issue.
This is why it is related to some dbplyr issues: tidyverse/dbplyr#112, tidyverse/dbplyr#102 and the PR tidyverse/dbplyr#142 that should solve those. I think it should also solve your issue.
This PR replaces the last part with when(TRUE)
by a ELSE
clause. You can try by intalling the branch (devtools::install_github("tidyverse/dbplyr#142")
) then the translation for case_when will use a ELSE clause.
# to get the PR
# devtools::install_github("tidyverse/dbplyr#142")
library(dbplyr)
# The translation as a ELSE clause
translate_sql(
case_when(
col1 == col2 ~ "a",
TRUE ~ "b")
)
#> <SQL> CASE
#> WHEN ("col1" = "col2") THEN ('a')
#> ELSE ('b')
#> END
# IF ELSE clause is different
translate_sql(
if_else(col1 == col2, "a","b")
)
#> <SQL> CASE WHEN ("col1" = "col2") THEN ('a') WHEN NOT("col1" = "col2") THEN ('b') END
# but you can recreate not using `TRUE~"b`
translate_sql(
case_when(
col1 == col2 ~ "a",
!col1 == col2 ~ "b")
)
#> <SQL> CASE
#> WHEN ("col1" = "col2") THEN ('a')
#> WHEN (NOT("col1" = "col2")) THEN ('b')
#> END
Created on 2018-08-27 by the reprex package (v0.2.0).
You could try if this fix also deals with your exception that you linked to.
from dbplyr.
Sorry, not convinced that the original issue has been correctly understood: Previous discussion was focused on the solution for case_when(..., TRUE ~ "b")
being translated to ELSE 'b'
. This is not the issue I am encountering.
The issue appears to be the SQL syntax that the two commands ifelse
and case_when
get translated into. The case_when
syntax does not appear to be valid 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)
The resulting SQL query is:
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
Running this code throws an error
An expression of non-boolean type specified in a context where a condition is expected, near 'THEN'
However the ifelse
query works as expected:
# attempted query
tmp = my_table %>%
mutate(new_col = ifelse(col1 == col2, "a", "b"))
# check SQL code for query
show_query(tmp)
The resulting SQL query is:
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
The difference between these two queries is that only ifelse
results in an appended = TRUE
or = FALSE
, while case_when
does not. This appears to be the cause.
I propose that = TRUE
be appended to the syntax for case_when
.
Note that in both cases the SQL syntax have been produced using show_query
. Using translate_sql
to produce the SQL code (as per @cdev 's response above) does not produce the SQL syntax that gets run on the server.
from dbplyr.
Thanks for the clarification.
Your example is not a reprex so it was difficult to reproduce. However, looking up, I assume your example is using a Microsoft SQL server, am I right ? The special CONVERT(BIT, IIF
are only added for mssql backend. It is a special translation for ==
or !=
.
Lines 45 to 46 in ce9273e
Lines 194 to 206 in ce9273e
I don't think the standard odbc case_when
translation is incorrect and incoherent with standard ifelse
but instead SQL server needs a special translation for case_when
like it has for if
, ifelse
and if_else
.
Lines 57 to 59 in ce9273e
Lines 225 to 236 in ce9273e
In this code, condition is translated using logical infix translation linked above.
This is a MSSQL translation issue but I don't have a SQL server available to go further and test what should be done.
from dbplyr.
You're welcome. Yes, my example is using a Microsoft SQL server.
If it is helpful, I may be able to test some options on the server - though I'm not familiar with how you usually do this.
from dbplyr.
It would be helpful to have the correct SQL query for case_when
on microsoft SQL server then a translation could be added after some tests.
In fact, I do not know mssql and I wonder why CONVERT(BIT, IIF
is needed. I do not see it, in the doc. So it would make sense to be consistent with if_else
translation but not sure if necessary to keep the same translation.
from dbplyr.
I do not think CONVERT(BIT, IIF
is required either. I have certainly never needed anything like it. IIF
in SQL server is very similar to if_else
in R.
Should they be useful, below are some code examples drawn for SQL server drawn from my projects:
SELECT
/* regular selection */
column_0
/* two simple case when statements */
,CASE WHEN column_1 BETWEEN 0 AND 3 THEN column_2 ELSE NULL END AS column_1
,CASE WHEN column_3 = '1' THEN 1 ELSE 0 END AS column_4
/* a case when with multiple options */
,CASE WHEN column_5 <= 30 THEN 'type1'
WHEN column_6 <= 50 THEN 'type2'
ELSE 'type3'
END AS column_6
/* the else is optional; NULL is used if no else is provided, these two are equivalent */
,CASE WHEN column_7 = 1 column_8 ELSE NULL END AS column_7
,CASE WHEN column_7 = 1 column_8 END AS column_7
/* logical conditions can be combined */
,CASE WHEN column_8 = 1 AND column_9 = 0 THEN 1 ELSE 0 END AS column_10
/* IIF is like a binary case-when, these two are equivalent */
,CASE WHEN column_11 = 1 THEN 'a' ELSE 'b' END AS column_11
,IFF(column_11 = 1, 'a', 'b') AS column_11
/* an alternative syntax that */
,CASE column_12
WHEN 1 THEN 'a'
WHEN 2 THEN 'b'
WHEN 3 THEN 'c'
ELSE 'd'
END column_13
/* which is equivalent to the following */
,CASE
WHEN column_12 = 1 THEN 'a'
WHEN column_12 = 2 THEN 'b'
WHEN column_12 = 3 THEN 'c'
ELSE 'd'
END column_13
FROM database.schema.table
from dbplyr.
Ok, it looks like @cderv 's PR fixes the TRUE ~ x
portion, and PR #103 removes the unnecessary BIT
operations:
library(devtools)
install_github("tidyverse/dbplyr", ref = github_pull(103))
#> Skipping install of 'dbplyr' from a github remote, the SHA1 (3a1a8931) has not changed since last install.
#> Use `force = TRUE` to force installation
library(dplyr, warn.conflicts = FALSE)
library(dbplyr, warn.conflicts = FALSE)
mf <- lazy_frame(x = 1, y = 1, src = simulate_mssql())
mf %>%
mutate(z = case_when(x == y ~ 1, TRUE ~ 2)) %>%
show_query()
#> <SQL> SELECT `x`, `y`, CASE
#> WHEN (`x` = `y`) THEN (1.0)
#> WHEN (TRUE) THEN (2.0)
#> END AS `z`
#> FROM `df`
Created on 2018-09-21 by the reprex package (v0.2.0).
from dbplyr.
Related Issues (20)
- dbplyr 2.4.0 - connecting to a table via a database link no longer works HOT 3
- Using a named vector with `dplyr::select(dplyr::all_of(...))` changes the table column names HOT 1
- The `unknown` column appears after I join the 2 tables HOT 4
- Progress bar for collect() HOT 1
- dbplyr 2.4.0 - date filter doesn't work anymore, possibly due to absence of single quotes in translation HOT 5
- Helpful warning if user forgets `I()`
- Update docs to prefer `I()` over `in_schema()` etc
- Check `table_path()` approach with backends HOT 4
- Fix no visible global function definition NOTE
- Automate global variables defintition HOT 1
- Consider recording quotes in simulate class definition
- Research stricter interpolation revdep failures HOT 1
- Incorrect translation of dates with Oracle ODBC. HOT 1
- Pre-release revdeps HOT 2
- Helper to get `last_sql()`
- str_detect() and str_replace() not treating period (.) detection same in snowflake translation HOT 3
- `paste0()` incorrectly uses a `sep` argument
- Snowflake translation error: dropped filter with `anti_join` HOT 3
- Filter by a column in another table HOT 5
- Release dbplyr 2.5.0
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dbplyr.