使用glue_sql() 并避免粘贴在动态SELECT 语句中的方法?

Posted

技术标签:

【中文标题】使用glue_sql() 并避免粘贴在动态SELECT 语句中的方法?
【英文标题】:Way to use glue_sql() and avoid paste in dynamic SELECT statement?使用glue_sql() 并避免粘贴在动态SELECT 语句中的方法?
【发布时间】:2021-10-17 02:05:08
【相关技术】:@tags@
【问题描述】:

我正在学习如何从 R 中查询 SQLite dbs,并使用 glue_sql() 构建这些查询。下面是我的工作流程中子查询的简化示例。有没有一种方法可以在不使用paste0() 的情况下创建s10_wtXs20_wtX,如下面的代码所示?

library(DBI)
library(dplyr)
library(glue)

# example database
set.seed(1)
ps <- data.frame(plot = rep(1:3, each = 4),
                 spp = rep(1:3*10, 2),
                 wtX = rnorm(12, 10, 2) %>% round(1))
con <- dbConnect(RSQLite::SQLite(), "")
dbWriteTable(con, "ps", ps)

# species of interest
our_spp <- c(10, 20)

# for the spp of interest, sum wtX on each plot
sq <- glue_sql(paste0(
  'SELECT ps.plot,\n',
  paste0('SUM(CASE WHEN ps.spp = ', our_spp,
         ' THEN (ps.wtX) END) AS s', our_spp,
         '_wtX',
         collapse = ',\n'), '\n',
  '  FROM ps
    WHERE ps.spp IN (our_spp*) -- spp in our sample
    GROUP BY ps.plot'),
  .con = con)

# the result of the query should look like:
dbGetQuery(con, sq)
  plot s10_wtX s20_wtX
1    1    21.9    10.4
2    2    11.0    22.2
3    3     9.4    13.0

在我的实际工作流程中,我有两种以上的兴趣,所以我宁愿不完整地写出每一行(例如,SUM(CASE WHEN ps.spp = 10 THEN (ps.wtX) END) AS s10_wtX)。

【问题讨论】:

您可以更轻松地在 SQL 中进行简单的聚合并在 R 中进行透视,您是否试图避免这种情况? 使用glue,您可以使用our_spp 并避免使用paste (来自我之前的评论)例如,您可以使用dbGetQuery(con, "select ps.plot, ps.spp, sum(ps.wtX) as wtX from ps where ps.spp in (10,20) group by ps.plot, ps.spp") %&gt;% tidyr::pivot_wider(plot, names_from="spp", values_from="wtX")(如果您可以使用dplyr+tidyr,与reshape2data.table 的结果类似)来获得所需的输出。 我不确定您是否只有sqlite,但其他DBMS 有PIVOT 运算符。例如,在 Oracle 中它将是 SELECT * FROM (SELECT ps.plot, ps.spp, ps.wtX FROM ps WHERE ps IN (10, 20)) PIVOT (SUM(wtX) FOR spp IN (10 as s10_wtx, 20 as s20_wtx)。但是...在 R 中进行处理可能更有意义。 标记了@akrun 的答案,因为它显示了glue_collapse(),并且很容易融入我的工作流程;但将重新审视@r2evans 的想法,尽可能避免glue_sql() ++
【参考方案1】:

OP的原始问题是

有没有一种方法可以在不使用 paste0() 的情况下创建 s10_wtX 和 s20_wtX,如下面的代码所示?

如果我们只想用glue 构造,也可以使用glue_collapse

library(glue)
sq1 <- glue_sql('SELECT ps.plot,', glue_collapse(glue('SUM(CASE WHEN ps.spp = our_spp THEN (ps.wtX) END) AS sour_spp_wtX'), sep = ",\n"), '\nFROM ps\n WHERE ps.spp IN (our_spp*) -- spp in our sample\n    GROUP BY ps.plot', .con = con)
dbGetQuery(con, sq1)
  plot s10_wtX s20_wtX
1    1    21.9    10.4
2    2    11.0    22.2
3    3     9.4    13.0

【讨论】:

很好奇(因为我不经常使用glue),是glue_collapse 实际上只是paste-ing 它与collapse="" 它在内部使用pastecollapse,并在调用as_glue时返回胶水格式【参考方案2】:

为了正式一点(即使它不是你最终使用的),这里是我的 cmets,详细:

out <- DBI::dbGetQuery(con, "
  select ps.plot, ps.spp, sum(ps.wtX) as wtX
  from ps
  where ps.spp in (10,20)
  group by ps.plot, ps.spp")
out
#   plot spp  wtX
# 1    1  10 21.9
# 2    1  20 10.4
# 3    2  10 11.0
# 4    2  20 22.2
# 5    3  10  9.4
# 6    3  20 13.0

这可以很容易地根据您的需要进行调整。例如,使用tidyr::pivot_wider

tidyr::pivot_wider(out, plot, names_from="spp", values_from="wtX")
# # A tibble: 3 x 3
#    plot  `10`  `20`
#   <int> <dbl> <dbl>
# 1     1  21.9  10.4
# 2     2  11    22.2
# 3     3   9.4  13  

(名称需要清理。)

【讨论】:

以上是关于使用glue_sql() 并避免粘贴在动态SELECT 语句中的方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何在下一个空单元格中复制和粘贴动态表值?

如何动态锁定线程并避免竞争条件

如何避免在旋转端口登陆Android4.X后在智能手机中剪切/复制/粘贴?

选择,从 IE 复制并粘贴到 Excel

Hibernate 删除记录惨遭失败

vbscript 在Excel VBA中复制和粘贴的三种方法。请注意,`Range.Copy`和`Range.PasteSpecial`使用剪贴板,因此请避免复制任何内容