关于SQL和R之间转换的一般问题

我从这个样例数据集开始:

start = structure(list(id = c(111L, 111L, 111L, 111L, 222L, 222L, 222L
), year = c(2010L, 2011L, 2012L, 2013L, 2018L, 2019L, 2020L),
col2 = c("A", "BB", "A", "C", "D", "EE", "F"), col3 = c(242L,
213L, 233L, 455L, 11L, 444L, 123L), col4 = c(1213L, 5959L,
9988L, 4242L, 333L, 1232L, 98L)), class = "data.frame", row.names = c(NA,
-7L))

这就是我想在R中做的事情:

 library(dplyr)

end <- start %>%
mutate(year_end = lead(year),
     col2_end = lead(col2),
     col3_end = lead(col3),
     col4_end = lead(col4)) %>%
mutate_at(vars(ends_with("_end")), ~ifelse(is.na(.), "END", .)) %>%
rename(year_start = year,
     col2_start = col2,
     col3_start = col3,
     col4_start = col4)

现在我try 使用SQL:

# my attempt to solve the problem
CREATE TABLE end AS
SELECT
id,
year AS year_start,
LEAD(year) OVER (PARTITION BY id ORDER BY year) AS year_end,
col2 AS col2_start,
LEAD(col2) OVER (PARTITION BY id ORDER BY year) AS col2_end,
col3 AS col3_start,
LEAD(col3) OVER (PARTITION BY id ORDER BY year) AS col3_end,
col4 AS col4_start,
LEAD(col4) OVER (PARTITION BY id ORDER BY year) AS col4_end
FROM start;

这段代码是否与R做同样的事情?

在过go ,我通常在R中执行这类操作,但我的数据在SQL Server上,并且正在try 为更大的数据集优化此代码.

我认为代码是正确的,但我想让另一双眼睛判断这段代码是否存在任何可能的问题.有人看到什么问题了吗?

推荐答案

他们在做同样的事情吗?No.几个原因:

  1. 在您的SQL代码中,您有partition by id,而数据链路中的类似功能是group_by(id),您已经省略了它.

  2. 您的数据中包含整数,但是您正在通过使用base::ifelse(., ., "END")悄悄地将它们转换( destruct )为character.允许R静默地强制转换为不同的类是不好的做法,特别是当下游需要的是整数时.如果您希望所有字段都是字符串,那么我建议您显式地将它们转换为字符串first.

    为此,我下面的代码将保留integer类直到最后,您可以 Select 将它们更改为具有最后mutate(..)的字符串.

    除非您知道严格地将"END"字符串的值设为need,否则我建议您最好使用NA或其他类似数字的前哨值,例如Inf.这在很大程度上取决于你如何使用它.

  3. 在R代码中,您将空值(NA)替换为"END",并且您不会在SQL中try 这样做.将数据CAST转换为varchar(或char或...)后,正确的SQL动词是COALESCE.

仅供参考,mutate_at已经被取代了一段时间,我建议你换到across.

以下是更新后的代码块.

数据链路

start %>%
  group_by(id) %>%
  mutate(across(everything(), ~ lead(.), .names = "{.col}_end")) %>%
  ungroup() %>%
  rename_with(.fn = ~ paste0(., "_start"), .cols = -c(id, ends_with("_end")))
# # A tibble: 7 × 9
#      id year_start col2_start col3_start col4_start year_end col2_end col3_end col4_end
#   <int>      <int> <chr>           <int>      <int>    <int> <chr>       <int>    <int>
# 1   111       2010 A                 242       1213     2011 BB            213     5959
# 2   111       2011 BB                213       5959     2012 A             233     9988
# 3   111       2012 A                 233       9988     2013 C             455     4242
# 4   111       2013 C                 455       4242       NA <NA>           NA       NA
# 5   222       2018 D                  11        333     2019 EE            444     1232
# 6   222       2019 EE                444       1232     2020 F             123       98
# 7   222       2020 F                 123         98       NA <NA>           NA       NA

如果你真的需要"END"来表示组结束(而不是这里看起来明确的NA),那么再加上:

... %>%
  mutate(across(matches("_(start|end)$"), ~ coalesce(as.character(.), "END")))
# # A tibble: 7 × 9
#      id year_start col2_start col3_start col4_start year_end col2_end col3_end col4_end
#   <int> <chr>      <chr>      <chr>      <chr>      <chr>    <chr>    <chr>    <chr>   
# 1   111 2010       A          242        1213       2011     BB       213      5959    
# 2   111 2011       BB         213        5959       2012     A        233      9988    
# 3   111 2012       A          233        9988       2013     C        455      4242    
# 4   111 2013       C          455        4242       END      END      END      END     
# 5   222 2018       D          11         333        2019     EE       444      1232    
# 6   222 2019       EE         444        1232       2020     F        123      98      
# 7   222 2020       F          123        98         END      END      END      END     

SQL

您的初始代码生成的代码与上面的第一个代码块相同(保留integer个类).我将在结尾处添加tibble,只是为了获得它的专栏类提示,而不是为了给这个讨论增加任何其他东西:

sqldf::sqldf("
SELECT
  id,
  year AS year_start,
  LEAD(year) OVER (PARTITION BY id ORDER BY year) AS year_end,
  col2 AS col2_start,
  LEAD(col2) OVER (PARTITION BY id ORDER BY year) AS col2_end,
  col3 AS col3_start,
  LEAD(col3) OVER (PARTITION BY id ORDER BY year) AS col3_end,
  col4 AS col4_start,
  LEAD(col4) OVER (PARTITION BY id ORDER BY year) AS col4_end
FROM start;") %>%
  tibble()
# # A tibble: 7 × 9
#      id year_start year_end col2_start col2_end col3_start col3_end col4_start col4_end
#   <int>      <int>    <int> <chr>      <chr>         <int>    <int>      <int>    <int>
# 1   111       2010     2011 A          BB              242      213       1213     5959
# 2   111       2011     2012 BB         A               213      233       5959     9988
# 3   111       2012     2013 A          C               233      455       9988     4242
# 4   111       2013       NA C          <NA>            455       NA       4242       NA
# 5   222       2018     2019 D          EE               11      444        333     1232
# 6   222       2019     2020 EE         F               444      123       1232       98
# 7   222       2020       NA F          <NA>            123       NA         98       NA

如果您需要将SQL转换为also,将数字更改为字符串并使用"END",那么

sqldf::sqldf("SELECT
id,
CAST(year as varchar(8)) AS year_start,
COALESCE(CAST(LEAD(year) OVER (PARTITION BY id ORDER BY year) as VARCHAR(9)), 'END') AS year_end,
CAST(col2 as varchar(8)) AS col2_start,
COALESCE(CAST(LEAD(col2) OVER (PARTITION BY id ORDER BY year) as VARCHAR(9)), 'END') AS col2_end,
CAST(col3 as varchar(8)) AS col3_start,
COALESCE(CAST(LEAD(col3) OVER (PARTITION BY id ORDER BY year) as VARCHAR(9)), 'END') AS col3_end,
CAST(col4 as varchar(8)) AS col4_start,
COALESCE(CAST(LEAD(col4) OVER (PARTITION BY id ORDER BY year) as VARCHAR(9)), 'END') AS col4_end
FROM start;") %>%
  tibble()
# # A tibble: 7 × 9
#      id year_start year_end col2_start col2_end col3_start col3_end col4_start col4_end
#   <int> <chr>      <chr>    <chr>      <chr>    <chr>      <chr>    <chr>      <chr>   
# 1   111       2010 2011     A          BB              242 213            1213 5959    
# 2   111       2011 2012     BB         A               213 233            5959 9988    
# 3   111       2012 2013     A          C               233 455            9988 4242    
# 4   111       2013 END      C          END             455 END            4242 END     
# 5   222       2018 2019     D          EE               11 444             333 1232    
# 6   222       2019 2020     EE         F               444 123            1232 98      
# 7   222       2020 END      F          END             123 END              98 END     

如果您到目前为止还没有推断出来,那么我是一个坚持变量class一致性的人:我对对象的类做了太多的假设,当R静默地将变量强制转换为字符串或类似的类型时,我就受阻了.ifelse是这一问题的常见罪魁祸首,对比ifelse(c(T,T), 1, "A")ifelse(c(T,F), 1, "A");它在other ways中也不是类安全的.

Sql相关问答推荐

如何将多个 Select 查询从一个表中组合出来

我可以将INSERT语句与SELECT一起使用来创建条件吗?

连接三个表的正确方式是什么?在这三个表中,可以显示在一个表上的行将在其他表中显示结果

我可以在SQLite3中使用BLOB作为主键吗?

属于(日期)范围类型及其交集的总权重​

仅在日期相隔时递增(Oracle SQL)

将两列相加为同一表中的行的查询

使用拆分器将已分组的不同值连接在一起

SQL将三个表中的三列组合为一列

我可以在 T-SQL (SQL Server) 的函数内使用 OPTION 子句吗?

每次计数器增加时通过运行总重置进行分组

SQL Server: 将JSON对象数组转换为表格格式

SQL Server 查找存在于所有不同时期(或序列)中的条目

忽略与给定列匹配的行的 LAG 函数

使用 R 遍历 SQL 查询,每次替换一个变量

具有日期时间条件的存储过程

如何在 PL/SQL 中区分返回的 XML 值?

如何通过子 Select 在一次更新(并行数组)中多次更新相同的行

如何在一个存储过程中创建全局临时表,并在另一个存储过程中使用它

Snowflake SQL group-by 的行为不同,具体取决于列是按位置引用还是按别名引用