Properly quoting SQL query with " and ' in R - r

I need to submit the following query through my R code:
select t."date"
from db.table t
where t."date" > '2016-01-01';
To generate this quoted string properly, I tried:
sqlquery <- dbQuoteString(ANSI()
, paste0("select t.", '"', "date", '"',"
from db.table t
where t.", '"',"date", '"'," > '2016-01-01';")
)
Which output is:
<SQL> 'select t."date"
from db.table t
where t."date" > ''2016-01-01'';'
So I can use:
data <- DBI::dbGetQuery(con, sqlquery)
However, there are double '' instead of a single one ' (around 2016-01-01).
How do I overcome that?

Several layers to this onion.
If you need to quote something, use dQuote or sQuote, they handle both the beginning and end for you. For instance,
dQuote("date")
# [1] "\"date\""
The use of dbQuoteString is quoting your whole query as if it is a string literal. Note the ' before select, indicating that it is a string literal surrounding in single quotes (a common SQL way of delineating string literals). You could also just as easily written
dbQuoteString(ANSI(), "Four score and seven years ago ... said by \"Lincoln\" in '1863'")
# <SQL> 'Four score and seven years ago ... said by "Lincoln" in ''1863'''
The reason you see '' is because it is trying to escape the single quotes that SQL uses to surround string literals. This produces a string, not something that can be executed as a query. In fact, dbQuoteString is something you should be using for your original query, instead of literal quotes and/or my dquote in point 1:
DBI::SQL(paste("select t.", DBI::dbQuoteIdentifier(DBI::ANSI(), "date"), "from db.table t where t.", DBI::dbQuoteIdentifier(DBI::ANSI(), "date"), ">", DBI::dbQuoteString(DBI::ANSI(), "2016-01-01")))
# <SQL> select t. "date" from db.table t where t. "date" > '2016-01-01'
(Admittedly, DBI::SQL is not strictly necessary here, it's really just a character string with a wrapper that makes it print more prettily on the R console.)
Consider not manually encoding data and such into your queries, minor mistakes break queries (or possibly worse, though unlikely in the general case). I strongly urge you to read through https://solutions.rstudio.com/db/best-practices/run-queries-safely/. There, they identify the use of dbQuote* functions as "Manual escaping", which is the last method recommended (least-preferred while still somewhat safe). Other options include:
Parameterized queries, using ? placeholders for data in the query.
ret <- DBI::dbGetQuery(con, 'select t."date" from db.table where "date" > ?', params = list("2016-01-01"))
Use glue::glue_sql:
mydate <- "2016-01-01"
sql <- glue::glue_sql(.con = con, 'select t."date" from db.table where "date" > {mydate}')
sql
# <SQL> select t."date" from db.table where "date" > '2016-01-01'
ret <- DBI::dbGetQuery(con, sql)
Use DBI::sqlInterpolate:
sql <- DBI::sqlInterpolate(con, 'select t."date" from db.table where "date" > ?mydate', mydate = "2016-01-01")
sql
# <SQL> select t."date" from db.table where "date" > '2016-01-01'
ret <- DBI::dbGetQuery(con, sql)

Related

R, ClickHouse: Expected: FixedString(34). Got: UInt64: While processing

I am trying to query data from ClickHouse database from R with subset.
Here is the example
library(data.table)
library(RClickhouse)
library(DBI)
subset <- paste(traffic[,unique(IDs)][1:30], collapse = ',')
conClickHouse <- DBI::dbConnect('here is the connection')
DataX <- dbgetdbGetQuery(conClickHouse, paste0("select * from database
and IDs in (", subset ,") ", sep = "") )
As a result I get error:
DB::Exception: Type mismatch in IN or VALUES section. Expected: FixedString(34).
Got: UInt64: While processing (IDs IN ....
Any help is appreciated
Thanks to the comment of #DennyCrane,
"select * from database where toFixedString(IDs,34) in
(toFixedString(ID1, 34), toFixedString(ID2,34 ))"
This query subset properly
https://clickhouse.tech/docs/en/sql-reference/functions/#strong-typing
Strong Typing
In contrast to standard SQL, ClickHouse has strong typing. In other words, it doesn’t make implicit conversions between types. Each function works for a specific set of types. This means that sometimes you need to use type conversion functions.
https://clickhouse.tech/docs/en/sql-reference/functions/type-conversion-functions/#tofixedstrings-n
select * from (select 'x' B ) where B in (select toFixedString('x',1))
DB::Exception: Types of column 1 in section IN don't match: String on the left, FixedString(1) on the right.
use casting toString or toFixedString
select * from (select 'x' B ) where toFixedString(B,1) in (select toFixedString('x',1))

Problem using WHERE clause in MonetDBLite in R

I am trying to use MonetDBLite in R64bit 3.5.1.
My problem is that I can not filter the data using SQL command like this example:
dbGetQuery(DB,'select * from table1 where "var1" = "1"')
I get this error:
Error in .local(conn, statement, ...) :
Unable to execute statement 'select * from table1 where "var1" = "1"'.
Server says 'ParseException:SQLparser:42000!SELECT: identifier '1' unknown'.
Any ideas?
For constant values, you need to use single quotes (') or none for numeric values. So in your example,
dbGetQuery(DB,'select * from table1 where "var1" = 1') or
dbGetQuery(DB,'select * from table1 where "var1" = \'1\'') or
dbGetQuery(DB,"select * from table1 where \"var1\" = '1'")
should all work.
The general rule is that identifiers (table1 or var1) generally only need to be quoted with " if they contain spaces or uppercase characters and constants (1) only need to be quoted with ' if they are character strings.

Parametrized query for selected columns

I want to have a parameters on columns to select in sql query:
select ? from my_table
I tried it with glue_sql:
glue::glue_sql(con, "select {x} from my_table", x=noquotes("mycolumn"))
but the result is:
select 'mycolumn' from my_table
instead of:
select mycolumn from my_table
any ideas?
You are trying to either not quote the identifier or quote it correctly (with double quotes). The help states:
They automatically quote character results, quote identifiers if the glue expression is surrounded by backticks
Try:
glue::glue_sql(.con=con, "select {`x`} from my_table", x="mycolumn")
# <SQL> select "mycolumn" from my_table
If other readers wonder why single quotes are bad, the single-quotes create it as a string-literal, meaning it will be returned as data, not as a column header. For instance, using some table with an Id field:
DBI::dbGetQuery(con, "select 'Id' from sometable limit 3")
#
# 1 Id
# 2 Id
# 3 Id
(Notice no column header, a proper query might have named the string literal with select 'Id' as somecolumnname ..., but at that point it becomes quite clear why single quotes are not right.)
DBI::dbGetQuery(con, 'select "Id" from sometable limit 3')
# Id
# 1 03E33A23-3F2C-1234-5678-90ABCDEF1234
# 2 04E33A23-3F2C-1234-5678-90ABCDEF1234
# 3 8114F80C-624D-1234-5678-90ABCDEF1234
You need something like this
library(DBI)
library(tidyverse)
x="mycolumn"
con%>%dbGetQuery(glue::glue("select {x} from my_table"))

ROracle bind range of dates

I want to send to oracle via ROracle query with bind parameters which inculde range of dates for a date column.
I try to run :
idsample <- 123
strdate <- "TO_DATE('01/02/2017','DD/MM/YYYY')"
enddate <- "TO_DATE('01/05/2017','DD/MM/YYYY')"
res <- dbGetQuery(myconn,"SELECT * FROM MYTABLE WHERE MYID = :1 AND MYDATE BETWEEN :2 AND :3", data=data.frame(MYID =idsample , MYDATE=c(strdate,enddate )))
but I get error :
"bind data does not match bind specification"
I could find no documentation which covers using more than one positional parameter, but if one parameter corresponds to a single column of a data frame, then by this logic three parameters should correspond to three columns:
idsample <- 123
strdate <- "TO_DATE('01/02/2017', 'DD/MM/YYYY')"
enddate <- "TO_DATE('01/05/2017', 'DD/MM/YYYY')"
res <- dbGetQuery(myconn,
paste0("SELECT * FROM MYTABLE WHERE MYID = :1 AND ",
"MYDATE BETWEEN TO_DATE(:2, 'DD/MM/YYYY') AND TO_DATE(:3, 'DD/MM/YYYY')"),
data=data.frame(idsample, strdate, enddate))
Note that there is nothing special about strdate and enddate from the point of view of the API, such that they should be passed as vector.
Edit:
The problem with making TO_DATE a parameter is that it will probably end up being escaped as a string. In other words, with my first approach you would end up with the following in your WHERE clause:
WHERE MYDATE BETWEEN
'TO_DATE('01/02/2017','DD/MM/YYYY')' AND 'TO_DATE('01/05/2017','DD/MM/YYYY')'
In other words, the TO_DATE function calls ends up being a string. Instead, bind the date strings only.

Using variable in "IN" function of SQL query in R

I am having a variable x which contains 20000 IDs. I want to write a sql query like,
select * from tablename where ID in x;
I am trying to implement this in R where I can get the values only for IDs in x variable. The following is my try,
dbSendQuery(mydb, "select * from tablename where ID in ('$x') ")
I am not getting any error while trying this. But it is returning 0 values.
Next tried using
sprintf("select * from tablename where ID in %s",x)
But this is creating 20000 individual queries which could prove costly in DB.
Can anybody suggest me a way to write a command, which would loop through IDs in x and save to a Dataframe in R in a single query?
You need to have the codes in the actual string. Here is how I would do it with gsub
x <- LETTERS[1:3]
sql <- "select * from tablename where ID in X_ID_CODES "
x_codes <- paste0("('", paste(x, collapse="','"), "')")
sql <- gsub("X_ID_CODES", x_codes, sql)
# see new output
cat(sql)
select * from tablename where ID in ('A','B','C')
# then submit the query
#dbSendQuery(mydb, sql)
How about pasting it:
dbSendQuery(mydb, paste("select * from tablename where ID in (", paste(x, collapse = ","), ")"))

Resources