0

When I attempt to import a data frame onto a SQL server in R via a JDBC connection, I get the following error:

dbWriteTable(dbhandle, "dbo.Testing", My_DF, row.names=FALSE, append = TRUE, temporary = FALSE)
Error in .sql.qescape(names(value), TRUE, conn@identifier.quote) : 
The JDBC connection doesn't support quoted identifiers, but table/column name contains characters that 
must be quoted

I searched online and found a suggested solution by specifying quotes, i.e.

dbWriteTable(dbhandle, "dbo.Testing", My_DF, row.names=FALSE, append = TRUE, temporary = FALSE, identifier.quote = "\"")

However, I get exactly the same error message. Is there some argument in dbWriteTable that I'm still missing? As far as I know, there is nothing strange about my data frame's data type or structure.

Max
  • 487
  • 5
  • 19
  • (1) *"doesn't support quoted identifiers"*? Wow, that's profound and ... brain-numbing. (2) Related: https://stackoverflow.com/q/2038169/3358272 (not R). (3) https://spring.io/blog/2020/05/20/migrating-to-spring-data-jdbc-2-0 suggests JDBC (on the java side, not R) can or does support quoted identifiers, you might look at your version and see if you can update it. (I don't use jdbc, sorry, just guesses.) (4) In case it's related to the DBMS to which you are connecting, it might help to mention the server type and version. – r2evans Aug 13 '20 at 14:54
  • I believe you need to change it to "dbo.testing". The capital T needs to be quoted. i.e. if you were writing this as a SQL query, you would need to say `SELECT * FROM dbo."Testing"` – FlexYourData Aug 13 '20 at 14:54
  • As far as I know, I have the most recent java update on my machine, so I don't think that it's a java issue. – Max Aug 13 '20 at 14:56

1 Answers1

1

Essentially, your main issue is two related things: 1) using a mixed case identifier, Testing in dbWriteTable and 2) not setting the double quotes feature in connection object of RJDBC.

According to RJDBC docs, the connection driver call, JDBC() defaults to NA for identifier quotes with further explanation of the argument:

JDBC (driverClass = "", classPath = "", identifier.quote = NA)

identifier.quote
      character to use for quoting identifiers in automatically generated SQL statements or NA if
      the back-end doesn’t support quoted identifiers. See details section below.

... Therefore the RJDBC implementation tries to adhere to the SQL92 standard, but not all databases are compliant. This affects mainly functions such as dbWriteTable that have to automatically generate SQL code. One major ability is the support for quoted identifiers. The SQL92 standard uses double-quotes, but many database engines either don't support it or use other character. The identifier.quote parameter allows you to set the proper quote character for the database used.


Specifically, the reason the following fails is mixing cases in your new table name, T (upper) esting (lower).

dbWriteTable(dbhandle, "Testing", My_DF, row.names=FALSE, append=TRUE, temporary=FALSE)

This forces dbWriteTable to run underlying SQL commands with double quotes such as the create-table command with double quotes in order to preserve cases in the name:

CREATE TABLE "Testing" (
   ...
)

RJDBC source code shows that it checks if double quotes were set in connection sesssion, and if not it throws an error. To fix consider either solution:

  1. Set identifier.quote at JDBC() call:

    drv <- JDBC("com.microsoft.sqlserver.jdbc.SQLServerDriver",
                "/path/to/sql/server.jar",
                identifier.quote = "\"")
    
    dbHandle <- dbConnect(drv, "jdbc:sqlserver://...", "user", "***")
    
  2. Keep all identifiers in all upper case (Oracle) that adhere to ANSI SQL-1992 standard or all lower case (Postgres).

    dbWriteTable(dbhandle, "TESTING", My_DF, row.names=FALSE, 
                  append=TRUE, temporary=FALSE)
    
    dbWriteTable(dbhandle, "testing", My_DF, row.names=FALSE, 
                  append=TRUE, temporary=FALSE)
    

    This will then allow any queries to be run without double quotes: select * from TeStInG, select * from testing, etc.

However, by default, identifiers in SQL Server are case insensitive unless you adjusted collation at server, database, or column levels. But to bypass RDBJC check and to effectively run the create-table command, consider simply setting the idenitier.quote at driver step.

Parfait
  • 104,375
  • 17
  • 94
  • 125
  • I think you're correct about excluding dbo. However, neither "Testing" nor '"Testing"' nor "\"Testing\"" work, they all return the same error about containing characters that must be quoted as before. – Max Aug 13 '20 at 23:13
  • I did not attempt the DBI method, I will try it later today though I'm not familiar with the package. I'm not sure that I follow your question about querying the table outside of the SQL Server console or R, as those are what I use to run SQL queries. – Max Aug 14 '20 at 13:27
  • Could you please clarify what is meant by non-default schema in this context? – Max Aug 14 '20 at 13:43
  • To answer your question, none of the select options are applicable to this table, because the table in question is not on a sql server. The issue that I'm trying to resolve is how to import this dataframe onto a SQL server to begin with. – Max Aug 14 '20 at 16:40
  • We seem to be misunderstanding one another. I am trying to import the data frame onto an MS sql server. However, I (by definition) cannot run SQL queries on this table because it is not yet on a SQL server - I am trying to use dbWriteTable to import it into the SQL server. – Max Aug 14 '20 at 18:17
  • See my updated answer, advising to use `identifier.quote = "\""` in `JDBC()` and not `dbWriteTable`. – Parfait Aug 14 '20 at 20:18
  • Using identifier.quote in JDBC() did work. Could you please explain why using the identifier.quote argument in dbWriteTable did not solve the problem? – Max Aug 14 '20 at 21:01
  • I don't believe there is such an argument for `dbWriteTable`. Where did you see its use? Curious did all caps or lower caps *without* `identifier.quote` also work? Try now to query your new table running various selects with/out quotes and varying letter cases: `dbGetQuery(dbhandle, "select * from XXX")`. – Parfait Aug 14 '20 at 21:44
  • I can query it without any problem, thanks. I found the use of indentifier.quote as an input argument for dbWriteTable on another forum when I Googled my error message. The input argument itself didn't return an error (though it didn't resolve the error). However, I just looked up the documentation for the function and it doesn't list identifier.quote or quote as one of the input arguments. – Max Aug 14 '20 at 22:08
  • Did querying require double quotes around *Testing*? Once again, can you create a table **without** `identifier.quote` if you do not MIX cases. Re the old [R list forum](https://r.789695.n4.nabble.com/rjdbc-identifier-quote-td3298519.html), OP did just like you by using that argument in wrong function which was not advised by fellow StackOverflow user, [G. Grothendieck](https://stackoverflow.com/users/516548/g-grothendieck) who recommended `JDBC()`. – Parfait Aug 14 '20 at 22:19
  • Yes, I did need the double quotes. – Max Aug 17 '20 at 12:46