在 WRDS MSRB 数据集上使用 SQL 查询从许多 CUSIP 检索数据

Retrieving data from many CUSIPs using a SQL query on the WRDS MSRB dataset

我是 SQL 的新手,所以很抱歉,如果这是一个简单的问题,我在搜索时没有找到任何东西,但我可能错过了明显的搜索词。

我正在尝试下载一组市政债券的所有交易数据,我有一个 CUSIP 列表,目前存储为一个 .txt 文件,每行一个 CUSIP。 WRDS 的在线版本允许用户上传这样一个 .txt 文件来检索他们的数据。

我想在 R 中自动执行此过程,并按照 WRDS 指南在 R 中设置 SQL 查询。最终我将使用类似

的内容
res <- dbSendQuery(wrds, "select *
               from msrb.msrb
               where cusip IN ???")
data <- dbFetch(res, n=-1)
dbClearResult(res)
data

如何将我的 CUSIP 列表实际放入查询中?直接列出每个 CUSIP 太长了,不可行。我能否以某种方式引用 .txt 文件,或者至少引用 R 中的字符向量或其他内容?有没有更好的方法?

我认为有两种有效的方法可以在 SQL 中以编程方式执行 IN (...),一种方法很流行但有风险(我通常不鼓励这样做)。

  1. 使用参数绑定。这在某些主观限制下是实用的;可能有一个 real 限制参数 DBI 允许绑定的数量,但我不知道;我不知道 SQL 实现是否经常限制您可以在文字 IN (...) 语句中放入的值的数量(我刚刚用 5000 测试了 PG11,没问题)。在某些时候,使用下面的选项 2 可能更有效或更可取。但是,如果我们谈论的是几十个,那么试试这个。

    cusips <- c(...) # vector of CUSIPs
    params <- paste(paste0("$", seq_along(cusips)), collapse = ",")
    ret <- DBI::dbGetQuery(con,
      paste("select * from msrb.msrb where cusip in (", params, ")"),
      params = as.list(cusips))
    

    (, , )的使用是postgres特有的;其他 DBMS 可能使用不同的命名法,包括 (?,?,?)(sql 服务器和其他)。

  2. 将 ID 上传到临时 table 并对其进行查询。 (如果您从另一个查询获取要使用的 ID,也可以部分使用它,只需更新内部 SQL 以反映您的其他查询。)

    cusips <- c(...) # vector of CUSIPs
    tmptbl <- paste0("tmptable_", paste(sample(9), collapse = ""))
    DBI::dbWriteTable(con, tmptbl, data.frame(cusip = cusips))
    DBI::dbGetQuery(con,
      paste("select * from msrb.msrb where cusip in",
            "(select cusip from", tmptbl, ")"))
    

    或加入临时 table,与

    DBI::dbGetQuery(con,
      paste("select msrb.* from ", tmptbl, "t",
            "left join msrb on t.cusip = msrb.cusip"))
    
  3. 总的来说,我是使用参数绑定的坚定拥护者,因为它会 side-step 任何形式的 SQL 注入,无论是恶意的还是意外的。但是,如果您赶时间,可以自己组成 IN (...)。您可以使用 glue::glue_sql 来确保始终使用正确的引号(针对您的特定 DBMS);如果不是,使用单引号通常是安全的。

    cusips <- c(...) # vector of CUSIPs
    params <- paste("(", paste(sQuote(cusips), collapse = ","), ")")
    # or
    params <- glue::glue_sql("({cusips*})", .con = con)
    DBI::dbGetQuery(con, paste("select * from msrb.msrb where cusip in", params))
    

    请注意,glue::glue_sql 提供了 * 表示法。来自 ?glue::glue_sql:

    If you place a '*' at the end of a glue expression the values will be collapsed with commas. This is useful for the SQL IN Operator for instance.

对于所有三种方法,我都使用了 more-direct DBI::dbGetQuery,但如果您愿意,您仍然可以使用 DBI::dbSendQuery/DBI::dbFetch two-step。

根据您的 msrb table 及其索引的大小,这些查询可能不会达到所有优化。如果是这种情况,请考虑根据 DBA 的建议添加到查询中。