【问题标题】:Loading data with RSQLite which has quoted values使用具有引用值的 RSQLite 加载数据
【发布时间】:2017-12-21 18:23:16
【问题描述】:

我正在尝试使用 RSQLite 包(我也尝试过 sqldf 包)将大型 csv 文件加载到 SQL lite 数据库中。该文件包含所有英国邮政编码及其各种查找值。

我想避免将其加载到 R 中,而直接将其加载到数据库中。虽然这对于这项任务来说并不是绝对必要的,但我想这样做是为了让该技术为更大的文件做好准备,如果我将来必须处理它们,这些文件将不适合内存。

不幸的是,csv 提供了双引号中的值,而 dbWriteTable 函数似乎无法以任何形式剥离或忽略它们。这里是文件的下载位置:http://ons.maps.arcgis.com/home/item.html?id=3548d835cff740de83b527429fe23ee0

这是我的代码:

# Load library
library("RSQLite")

# Create a temporary directory
tmpdir <- tempdir()

# Set the file name
file <- "data\\ONSPD_MAY_2017_UK.zip"

# Unzip the ONS Postcode Data file
unzip(file, exdir = tmpdir )

# Create a path pointing at the unzipped csv file
ONSPD_path <- paste0(tmpdir,"\\ONSPD_MAY_2017_UK.csv")

# Create a SQL Lite database connection
db_connection <- dbConnect(SQLite(), dbname="ons_lkp_db")

# Now load the data into our SQL lite database
dbWriteTable(conn = db_connection,
              name = "ONS_PD",
              value = ONSPD_path,
              row.names = FALSE,
              header = TRUE,
              overwrite = TRUE
              )

# Check the data upload
dbListTables(db_connection)
dbGetQuery(db_connection,"SELECT pcd, pcd2, pcds from ONS_PD  LIMIT 20")

遇到这个问题后,我找到了推荐使用 sqldf 包的参考教程 (https://www.r-bloggers.com/r-and-sqlite-part-1/),但不幸的是,当我尝试在 sqldf (read.csv.sql) 中使用相关函数时,我遇到了同样的问题双引号。

当将 csv 文件导入 sql 系统时,这感觉是一个相当普遍的问题,大多数导入工具都能够处理双引号,所以我很惊讶遇到这个问题(除非我错过了一个明显的帮助文件途中某个地方的问题)。

编辑 1

以下是我的 csv 文件中的一些示例数据,以 SQL 表的 dput 输出形式:

structure(list(pcd = c("\"AB1 0AA\"", "\"AB1 0AB\"", "\"AB1 0AD\"", 
"\"AB1 0AE\"", "\"AB1 0AF\""), pcd2 = c("\"AB1  0AA\"", "\"AB1  0AB\"", 
"\"AB1  0AD\"", "\"AB1  0AE\"", "\"AB1  0AF\""), pcds = c("\"AB1 0AA\"", 
"\"AB1 0AB\"", "\"AB1 0AD\"", "\"AB1 0AE\"", "\"AB1 0AF\"")), .Names = c("pcd", 
"pcd2", "pcds"), class = "data.frame", row.names = c(NA, -5L))

编辑 2

这是我在 sqldf 的 read.csv.sql 函数中使用 filter 参数的尝试(请注意,Windows 用户需要为此安装 rtools)。不幸的是,这似乎仍然没有从我的数据中删除引号,尽管它确实神秘地删除了所有空格。

library("sqldf")
sqldf("attach 'ons_lkp_db' as new")
db_connection <- dbConnect(SQLite(), dbname="ons_lkp_db")
read.csv.sql(ONSPD_path,
              sql = "CREATE TABLE ONS_PD AS SELECT * FROM file",
              dbname = "ons_lkp_db",
              filter = 'tr.exe -d ^"'
              )

dbGetQuery(db_connection,"SELECT pcd, pcd2, pcds from ONS_PD  LIMIT 5")

另外,感谢那些认为这不是 Stack Overflow 范围内的编程问题的人的密切投票(?!)。

【问题讨论】:

  • 请参阅 sqldf github 页面上的常见问题解答 #13。还有read.csv.sql 的帮助文件请查看minimal reproducible example
  • 谢谢,我会在安装 rtools 后尝试常见问题解答建议。你能指出我的例子需要改进的地方吗?
  • 其他人关注:github.com/ggrothendieck/…
  • 在问题中显示一小部分输入行以使其独立,或者如果它们很长,则将它们缩短到足以保持主要思想而不会因大量数据而使问题超载。
  • 等我有空的时候再补充。我刚刚尝试了常见问题解答中的建议,但仍然没有运气:utils::read.table 中的错误(value,sep = sep,header = header,skip = skip,:rsqlite_send_query 中的错误输入中没有可用的行(conn@ ptr,语句):没有这样的表:文件

标签: r sqlite csv sqldf rsqlite


【解决方案1】:

老实说,我找不到任何东西来解决这个问题。 sqldf 文档告诉 “所以,.csv 文件的一个限制是引号 在文件中不被视为特殊,因此数据字段中的逗号,例如 “史密斯,詹姆斯” 将被视为字段分隔符,引号将作为数据的一部分输入 可能不是我们想要的”

所以,据我所知,似乎没有解决方案。

一种可能的次优方法(其他在文本编辑器中明显的查找和替换) 就是使用这样的SQL命令

dbSendQuery(db_connection,"UPDATE ONS_PD SET pcd = REPLACE(pcd, '\"', '')")

【讨论】:

  • 我现在只是在尝试 rtools 建议,但可能有一种方法可以在加载之前通过在 csv 上调用 windows powershell 脚本来剥离它们。不过有点繁琐。
  • 文档中的引用仅适用于不使用 filter 命令的情况。如果您使用filter 命令,您应该能够以任意方式转换输入,所以如果可能的话,您应该能够做到。如果您需要更多帮助,您将需要提供一个自包含的可重现示例。我们不知道您拥有什么或做了什么。
【解决方案2】:

RSQLite 包中的 CSV 导入器源自 sqlite3 shell,它在导入 CSV 文件时似乎不支持引用值(How to import load a .sql or .csv file into SQLite?doc)。你可以使用readr::read_delim_chunked():

callback <- function(data) {
  name <- "ONS_PD"
  exists <- dbExistsTable(con, name)
  dbWriteTable(con, name, data, append = exists)
}

readr::read_delim_chunked(ONSPD_path, callback, ...)

用 CSV 文件所需的任何额外参数替换 ...

【讨论】:

  • 谢谢你,你知道大文件的速度吗?我希望能解决我在 read.csv.sql 函数中过滤时遇到的问题,但下次有机会时会尝试你的建议。
  • @Tumbledown:阅读器速度很快;虽然据报道来自 data.table 的fread() 速度更快,但解析“复杂”CSV 文件的选项似乎更少,而且我还没有找到添加回调的选项。 dbWriteTable() 使用准备好的语句,而且速度也很快。我不知道这与 sqldf 相比如何。
【解决方案3】:

使用带有 filter 参数的 sqldf 包中的 read.csv.sql,并提供任何实用程序来去除双引号或将它们转换为空格。

该问题没有提供完全可重现的最小示例,但我在下面提供了一个。如果您使用read.csv.sql 来选择行或列的子集,那么只需添加适当的sql 参数即可。

首先设置测试输入数据,然后尝试如下所示的任一单行解决方案。假设 Windows,请确保 tr 实用程序(在 R 的 Rtools 发行版中找到)或第三方 csvfix 实用程序(找到 here,对于 Linux 还请参见 this)或 trquote2space.vbs vbscript 实用程序(参见末尾的注释)已打开你的路径:

library(sqldf)
cat('a,b\n"1","2"\n', file = "tmp.csv")

# 1 - corrected from FAQ
read.csv.sql("tmp.csv", filter = "tr.exe -d '^\"'")

# 2 - similar but does not require Windows cmd quoting
read.csv.sql("tmp.csv", filter = "tr -d \\42")

# 3 - using csvfix utility (which must be installed first)
read.csv.sql("tmp.csv", filter = "csvfix echo -smq")

# 4 - using trquote2space.vbs utility as per Note at end
read.csv.sql("tmp.csv", filter = "cscript /nologo trquote2space.vbs")

其中任何一个给出:

  a b
1 1 2

您还可以使用任何其他合适的语言或实用程序。例如,可以使用您的 Powershell 建议,尽管我怀疑 tr 和 csvfix 等专用实用程序会运行得更快。

上面的第一个解决方案是从常见问题解答中更正的。 (它在多年前编写常见问题解答时确实有效,但现在在 Windows 10 中对其进行测试似乎需要指定的更改,或者降价可能从最初所在的 Google 代码迁移到github,它使用了稍微不同的 markdown 风格。)

对于 Linux,tr 本身是可用的,尽管引用与 Windows 不同,甚至可以依赖于 shell。 csvfix 在 Linux 上也可用,但必须安装。上面显示的 csvfix 示例在 Windows 和 Linux 上的工作方式相同。 vbscript 显然是特定于 Windows 的。

注意: sqldf 带有一个用 vbscript 编写的 mini-tr 实用程序。如果您将相关行更改为:

Dim sSearch  : sSearch  = chr(34)
Dim sReplace : sReplace = " "

并将名称更改为 trquote2space.vbs 然后您将拥有一个特定于 Windows 的实用程序来将双引号更改为空格。

【讨论】:

  • 已添加更多解决方案并重新排列。
  • 太好了,您对过滤器参数的更正效果很好。很抱歉没有完全确定可重复的示例,并感谢您为回答所付出的努力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-10
  • 2016-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多