RODBC - Query vector longer than 1 - r

Using RODBC you can query a database like this:
library(RODBC)
dbHandle <- odbcDriverConnect('driver=SQL Server;server=SOME_SERVER;trusted_connection=true')
returnDf <- sqlQuery(dbHandle, query, stringsAsFactors = FALSE)
odbcClose(dbHandle)
This assumes that the object query is a vector of length 1. What happens if it is not? So, if query contains two elements - is the database queried twice?

Thanks #r2evans for pointing out the solution:
query <- c("select 1 as a", "select 2 as b")
dbhandle <- odbcDriverConnect('driver=SQL Server;server=SOME_SERVER;database=csn_pricing;trusted_connection=true')
df <- sqlQuery(dbhandle, query, stringsAsFactors = FALSE)
odbcClose(dbhandle)
This results in
> df
a
1 1
Hence, only the first element is used.

Related

Safely parametrize WHERE ... IN lists for SQL queries in R

library(DBI)
library(RSQLite)
library(dplyr)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
iris_id <- iris |>
mutate(id = row_number())
dbWriteTable(con, "iris_id", iris_id)
params <- list(id = c(5,6,7))
q <- "SELECT COUNT(*) FROM iris_id WHERE id IN ($id)"
res <- dbSendQuery(con, q)
dbBind(res, params)
dbFetch(res)
As per documentation, this performs the query once per entry in params$id and returns c(1,1,1).
This also doesn't work, because this query is actually WHERE id IN ('5,6,7'):
id <- c(5L,6L,7L)
stopifnot(is.integer(id))
params <- list(id = paste(id, collapse=","))
res <- dbSendQuery(con, q)
dbBind(res, params)
dbFetch(res)
The answer to question [0] suggests using positional ? and pasting a list of ? together. However, this loses the possibility of using named params, which would be beneficial if I have multiple parameters. Is there another way?
[0] Passing DataFrame column into WHERE clause in SQL query embedded in R via parametrized queries
One solution would be to pass all the ids as a comma separated string (no spaces) and use instead of IN the operator LIKE:
params <- list(id = "5,6,7")
q <- "SELECT COUNT(*) FROM iris_id WHERE ',' || $id || ',' LIKE '%,' || id || ',%'"
res <- dbSendQuery(con, q)
dbBind(res, params)
dbFetch(res)
library(dbplyr)
translate_sql(id %in% c(4L,5L,6L))

ROracle 1.3-2 R 4.1.2 - Binding BLOB/CLOB to arguments of ROracle::oracleProc()

I would need to store BLOB/CLOB with larger raw/character data by passing them to the Oracle db procedure from R.
The documentation of ROracle indicates it should be possible. I can pass the data directly by using dbWritetable when setting attr(in.df$msg_att, "ora.type") <- "BLOB" of the input dataframe.
I suppose the same logic should apply in oracleProc. Unfortunately, it does not work. Setting it to BLOB binds the NULL value within the db procedure. When not set, it can pass maximum 32767 Bytes.
Here is the script:
library(ROracle)
ch <- db_connect(ROracle::Oracle(), user, password)
dbGetQuery(ch, "CREATE SEQUENCE ATT_ID_SEQ")
dbGetQuery(
ch,
"CREATE TABLE T_ATT_TEST
(msg_id NUMBER,
att_id NUMBER GENERATED BY DEFAULT AS IDENTITY,
msg_att CLOB)"
)
dbGetQuery(
ch,
"CREATE OR REPLACE PROCEDURE P_ATT_TEST
(msg_id IN INTEGER, att_id OUT integer, msg_att IN OUT BLOB)
IS
BEGIN
insert into T_ATT_TEST(msg_id, att_id, msg_att)
values(msg_id, att_id_seq.nextval, msg_att)
returning att_id into att_id;
END;")
raw.lst <- vector("list", 1)
raw.lst[[1L]] <- charToRaw(paste(rep('x', 32767), collapse = ""))
in.df <- data.frame(msg_id = 1,
att_id = as.integer(NA),
stringsAsFactors = F)
in.df$msg_att <- raw.lst
attr(in.df$msg_id, "ora.parameter_name") <- "msg_id"
attr(in.df$msg_id, "ora.parameter_mode") <- "IN"
attr(in.df$msg_att, "ora.parameter_name") <- "msg_att"
attr(in.df$msg_att, "ora.parameter_mode") <- "IN"
#attr(in.df$msg_att, "ora.type") <- "BLOB"
attr(in.df$att_id, "ora.parameter_name") <- "att_id"
attr(in.df$att_id, "ora.parameter_mode") <- "OUT"
att_id <- oracleProc(ch, 'BEGIN P_ATT_TEST(
:msg_id,
:att_id,
:msg_att
); END;', in.df)
dbCommit(ch)
res <- dbReadTable(ch, "T_ATT_TEST")
str(res)
# remove created objects
dbGetQuery(ch, "DROP SEQUENCE ATT_ID_SEQ")
dbGetQuery(ch, "DROP TABLE T_ATT_TEST")
dbGetQuery(ch, "DROP PROCEDURE P_ATT_TEST")
dbDisconnect(ch)
Can somebody help me with an example of binding BLOB/CLOB to oracleProc?
Thank you and best regards
Ondrej

R table from SQL weird behavior

I connected R to SQL using the following:
library(dplyr)
library(dbplyr)
library(odbc)
library(RODBC)
library(DBI)
con <- dbConnect(odbc(),
Driver = "SQL Server",
Server = "srv name",
Database = "Warehouse")
I pull in the table I want using
data <- tbl(con, in_schema("prc", "PricingLawOfUniv")))
The following things show me what I expect to see (a 38 X 1000 table of data):
head(data)
colnames(data)
The following things behave as I expect:
In the Environment data is a "list of 2"
View(data) shows a list with "src" and "ops" - each of those is also a list of 2.
Ultimately I want to work with the 38 X 1000 table as a dataframe using dplyr. How can I do this? I tried data[1] and data[2] but neither worked. Where is the actual table I want hiding?
You could use DBI::Id to specify the table/schema, and then dbReadTable:
tbl <- DBI::Id(
schema = "prc",
table = "PricingLawOfUniv"
)
data <- DBI::dbReadTable(con, tbl)

Update selected rows in sqlite table in r

I am using the RSQLite package in a shiny app. I need to be able to dynamically update an sqlite db as users progress through the app. I want to use the UPDATE syntax in SQLite to achieve this, but I have come up against a problem when trying to update multiple rows for the same user.
Consider the following code:
# Load libraries
library("RSQLite")
## Path for SQLite db
sqlitePath <- "test.db"
# Create db to store tables
con <- dbConnect(SQLite(),sqlitePath)
## Create toy data
who <- c("jane", "patrick", "samantha", "jane", "patrick", "samantha")
tmp_var_1 <- c(1,2,3, 4, 5, 6)
tmp_var_2 <- c(2,4,6,8,10,12)
# Create original table
users <- data.frame(who = as.character(who), tmp_var_1 = tmp_var_1, tmp_var_2 = tmp_var_2)
users$who <- as.character(users$who)
# Write original table
dbWriteTable(con, "users", users)
# Subset users data
jane <- users[who=="jane",]
patrick <- users[who=="patrick",]
samantha <- users[who=="samantha",]
# Edit Jane's data
jane$tmp_var_1 <- c(99,100)
# Save edits back to SQL (this is where the problem is!)
table <- "users"
db <- dbConnect(SQLite(), sqlitePath)
query <- sprintf(
"UPDATE %s SET %s = ('%s') WHERE who = %s",
table,
paste(names(jane), collapse = ", "),
paste(jane, collapse = "', '"),
"'jane'"
)
dbGetQuery(db, query)
## Load data to check update has worked
loadData <- function(table) {
# Connect to the database
db <- dbConnect(SQLite(), sqlitePath)
# Construct the fetching query
query <- sprintf("SELECT * FROM %s", table)
# Submit the fetch query and disconnect
data <- dbGetQuery(db, query)
dbDisconnect(db)
data
}
loadData("users")
Here I am trying to update the entry for Jane so that the values for tmp_var_1 are changed, but all other columns remain the same. In response to questions from #zx8754 and #Altons posted below, the value for query is as follows:
UPDATE users SET who, tmp_var_1, tmp_var_2 = ('c(\"jane\", \"jane\")', 'c(99, 100)', 'c(2, 8)') WHERE who = 'jane'
The problem is almost certainly coming from the way that I am specifying the query to RSQlite. When I run dbGetQuery(db, query) I get the following error:
Error in sqliteSendQuery(con, statement, bind.data) :
error in statement: near ",": syntax error
Any suggestions for improvement would be most welcome.

Pass R variable to RODBC's sqlQuery with multiple entries?

I'm in the process of learning R, to wave SAS goodbye, I'm still new to this and I somehow have difficulties finding exactly what I'm looking for.
But for this specific case, I read:
Pass R variable to RODBC's sqlQuery?
and made it work for myself, as long as I'm only inserting one variable in the destination table.
Here is my code:
library(RODBC)
channel <- odbcConnect("test")
b <- sqlQuery(channel,
"select top 1 Noinscr
FROM table
where PrixVente > 100
order by datevente desc")
sqlQuery(channel,
paste("insert into TestTable (UniqueID) Values (",b,")", sep = "")
When I replace the top 1 by any other number, let's say top 2, and run the exact same code, I get the following errors:
[1] "42000 195 [Microsoft][SQL Server Native Client 10.0][SQL Server]
'c' is not a recognized built-in function name."
[2] "[RODBC] ERROR: Could not SQLExecDirect
'insert into TestTable (UniqueID) Values (c(8535735, 8449336))'"
I understand that it is because there is an extra c that is generated, I assume for column when I give the command: paste(b).
So how can I get "8535735, 8449336" instead of "c(8535735, 8449336)" when using paste(b)? Or is there another way to do this?
Look into the collapse argument in the paste() documentation. Try replacing b with paste(b, collapse = ", "), as shown below.
Edit As Joshua points out, sqlQuery returns a data.frame, not a vector. So, instead of paste(b, collapse = ", "), you could use paste(b[[1]], collapse = ", ").
library(RODBC)
channel <- odbcConnect("test")
b <- sqlQuery(channel,
"select top 1 Noinscr
FROM table
where PrixVente > 100
order by datevente desc")
sqlQuery(channel,
## note paste(b[[1]], collapse = ", ") in line below
paste("insert into TestTable (UniqueID) Values (", paste(b[[1]], collapse = ", "),")", sep = "")
Assuming b looks like this:
b <- data.frame(Noinscr=c("8535735", "8449336"))
Then you only need a couple steps:
# in case Noinscr is a factor
b$Noinscr <- as.character(b$Noinscr)
# convert the vector into a single string
# NOTE that I subset to get the vector, since b is a data.frame
B <- paste(b$Noinscr, collapse=",")
# create your query
paste("insert into TestTable (UniqueID) Values (",B,")", sep="")
# [1] "insert into TestTable (UniqueID) Values (8535735,8449336)"
You got odd results because sqlQuery returns a data.frame, not a vector. As you learned, using paste on a data.frame (or any list) can provide weird results because paste must return a character vector.

Resources