处理分页SQL查询结果

时间:2014-06-17 13:13:41

标签: sql r postgresql pagination rcurl

对于我的论文数据收集,其中一个来源是外部管理系统,它基于Web表单提交 SQL查询。使用RRCurl,我实现了一个自动数据收集框架,我在其中模拟上述形式。当我限制结果数据集的大小时,一切都运行良好。但是,当我试图超过100000条记录(下面的代码中为RQ_SIZE)时,串联的“我的代码 - 他们的系统”开始没有响应(“挂起”)。

所以,我决定使用 SQL分页功能LIMIT ... OFFSET ...)来提交一系列请求,希望然后将分页结果组合成一个目标数据框架。但是,在相应地更改我的代码之后,我看到的输出只是一个分页进度字符(*),然后没有更多输出。我很感激,如果你能帮助我找出意外行为的可能原因。我无法提供可重现的示例,因为提取功能非常困难,更不用说数据了,但我希望以下代码片段足以揭示问题(或者至少是问题的方向)。 / p>

# First, retrieve total number of rows for the request
srdaRequestData(queryURL, "COUNT(*)", rq$from, rq$where,
                DATA_SEP, ADD_SQL)
assign(dataName, srdaGetData()) # retrieve result
data <- get(dataName)
numRequests <- as.numeric(data) %/% RQ_SIZE + 1

# Now, we can request & retrieve data via SQL pagination
for (i in 1:numRequests) {

  # setup SQL pagination
  if (rq$where == '') rq$where <- '1=1'
  rq$where <- paste(rq$where, 'LIMIT', RQ_SIZE, 'OFFSET', RQ_SIZE*(i-1))

  # Submit data request
  srdaRequestData(queryURL, rq$select, rq$from, rq$where,
                  DATA_SEP, ADD_SQL)
  assign(dataName, srdaGetData()) # retrieve result
  data <- get(dataName)

  # some code

  # add current data frame to the list
  dfList <- c(dfList, data)
  if (DEBUG) message("*", appendLF = FALSE)
}

# merge all the result pages' data frames
data <- do.call("rbind", dfList)

# save current data frame to RDS file
saveRDS(data, rdataFile)

3 个答案:

答案 0 :(得分:1)

当推测MySQL阻碍LIMIT OFFSET时,它可能属于该类别: Why does MYSQL higher LIMIT offset slow the query down?

总的来说,重复通过HTTP获取大型数据集并不是非常可靠。

答案 1 :(得分:1)

因为这是为了你的论文,这是一手:

## Folder were to save the results to disk.
##  Ideally, use a new, empty folder. Easier then to load from disk
folder.out <- "~/mydissertation/sql_data_scrape/"
## Create the folder if not exist. 
dir.create(folder.out, showWarnings=FALSE, recursive=TRUE)


## The larger this number, the more memory you will require. 
## If you are renting a large box on, say, EC2, then you can make this 100, or so
NumberOfOffsetsBetweenSaves <- 10

## The limit size per request
RQ_SIZE <- 1000

# First, retrieve total number of rows for the request
srdaRequestData(queryURL, "COUNT(*)", rq$from, rq$where,
                DATA_SEP, ADD_SQL)


## Get the total number of rows
TotalRows <- as.numeric(srdaGetData())

TotalNumberOfRequests <- TotalRows %/% RQ_SIZE

TotalNumberOfGroups <- TotalNumberOfRequests %/% NumberOfOffsetsBetweenSaves + 1

## FYI: Total number of rows being requested is
##  (NumberOfOffsetsBetweenSaves * RQ_SIZE * TotalNumberOfGroups) 


for (g in seq(TotalNumberOfGroups)) {

  ret <- 
    lapply(seq(NumberOfOffsetsBetweenSaves), function(i) {

      ## function(i) is the same code you have
      ##    inside your for loop, but cleaned up.

      # setup SQL pagination
      if (rq$where == '') 
          rq$where <- '1=1'

      rq$where <- paste(rq$where, 'LIMIT', RQ_SIZE, 'OFFSET', RQ_SIZE*g*(i-1))

      # Submit data request
      srdaRequestData(queryURL, rq$select, rq$from, rq$where,
                      DATA_SEP, ADD_SQL)

       # retrieve result
      data <- srdaGetData()

      # some code

      if (DEBUG) message("*", appendLF = FALSE)    


      ### DONT ASSIGN TO dfList, JUST RETURN `data`
      # xxxxxx DONT DO: xxxxx dfList <- c(dfList, data)
      ### INSTEAD:

      ## return
      data
  })

  ## save each iteration
  file.out <- sprintf("%s/data_scrape_%04i.RDS", folder.out, g)
  saveRDS(do.call(rbind, ret), file=file.out)

  ## OPTIONAL (this will be slower, but will keep your rams and goats in line)
  #    rm(ret)
  #    gc()
}

然后,一旦你完成了刮擦:

library(data.table)

folder.out <- "~/mydissertation/sql_data_scrape/"

files <- dir(folder.out, full=TRUE, pattern="\\.RDS$") 

## Create an empty list
myData <- vector("list", length=length(files))


## Option 1, using data.frame
    for (i in seq(myData))
      myData[[i]] <- readRDS(files[[i]])

    DT <- do.call(rbind, myData)

## Option 2, using data.table
    for (i in seq(myData))
      myData[[i]] <- as.data.table(readRDS(files[[i]]))

    DT <- rbindlist(myData)

答案 2 :(得分:0)

我回答了自己的问题,最后,我已经弄明白问题的真正来源。我的调查显示该程序的意外等待状态是由于PostgreSQL因格式错误的SQL查询而感到困惑,其中包含多个LIMITOFFSET个关键字。

原因非常简单:我在rq$where循环外部和内部使用了for,这使paste()连接了上一次迭代&#39; s WHERE子句与当前的一个。我通过处理WHERE子句的内容并在循环之前保存它然后在循环的每次迭代中安全地使用保存的值来修复代码,因为它独立于原始{{1}的值条款。

此调查还帮助我修复了代码中的一些其他缺陷并进行了改进(例如使用子选择来正确处理SQL查询,返回具有聚合函数的查询的记录数)。故事的道德:你在软件开发中永远不会太谨慎。 非常感谢那些帮助解决这个问题的好人。

相关问题