Amazon Redshift - 表列声明为 varchar(max) 但强制为 varchar(255)

Posted

技术标签:

【中文标题】Amazon Redshift - 表列声明为 varchar(max) 但强制为 varchar(255)【英文标题】:Amazon Redshift - table columns declared as varchar(max) but forced as varchar(255) 【发布时间】:2018-09-14 11:45:31 【问题描述】:

我正在编写一个数据提取工具,以从 Google Search Console(从现在开始为 GSC)加载数据并将其存储在 Amazon Redshift(从现在开始为 AR)数据库中。我编写了一个函数来解析来自 GSC 的数据帧上的元素,以确定在 AR 上创建表时的字段结构。

这是我创建的 R 函数:

get_table_fields <- function (d) 
  r <- FALSE

  if (is.data.frame(d)) 
    r <- vector()
    t <- d[1,]
    c <- colnames(t)

    for (k in c) 
      v <- t[, k]

      if (is.character(v)) 
        r[k] <- "nvarchar(max)"
       else if (!is.na(as.Date(as.character(v), format = c("%Y-%m-%d")))) 
        r[k] <- "date"
       else if (is.numeric(v)) 
        r[k] <- ifelse(grepl(".", v, fixed = TRUE), "real", "integer")
      
    
  

  return(r)

到目前为止,一切都很好。我传递了完整的数据框,函数从第一行提取所有相关信息,为我提供了在 AR 上创建表所需的结构。

这是我用来从 GSC 中提取数据并将其写入 AR 的代码:

# retrieve the table fields schema
s_fields <- get_table_fields(data)

# compose the table creation definition out of the fields schema
d_fields <- paste(toString(sapply(names(s_fields), function (x) 
  return(sprintf('"%s" %s', x, s_fields[x]))
)))

# compose the table creation query
c_query <- sprintf("CREATE TABLE IF NOT EXISTS %s (%s);", t_table_name, d_fields)

if (nrow(data) > 0) 
  # create the table if it doesn't exist
  dbSendUpdate(db, c_query)

  # delete previous saved records for the specified date
  dbSendUpdate(db, sprintf("DELETE FROM %s WHERE date = '%s' AND gsc_domain = '%s';", t_table_name, date_range[d], config.gsc.domain))

  # upload the Google Search Console (GSC) data to Amazon Redshift (AR)
  dbWriteTable(db, t_table_name, data, append = TRUE, row.names = FALSE)

db 是数据库连接对象,声明如下:

# initialize the Amazon Redshift JDBC driver
driver <- JDBC("com.amazon.redshift.jdbc42.Driver", "drivers/RedshiftJDBC42-1.2.16.1027.jar", identifier.quote = "`")

# connect to the Amazon Redshift database instance
db <- dbConnect(driver, sprintf("jdbc:redshift://%s:%s/%s?user=%s&password=%s", config.ar.host, config.ar.port, config.ar.database, config.ar.user, config.ar.password))

t_table_name 是 GSC 提取定义中具有不同维度的连接字符串,以 gsc_by 作为前缀并用下划线连接,因此,如果我们要提取日期、页面和设备,表名将是 @ 987654327@

所以,基本上,这段代码所做的是从 GSC 收集数据帧,确保指定提取的表存在。如果没有,它会创建它。否则,它会删除任何现有数据(以防重新启动提取以不复制任何条目)并将其存储在 AR 中。

问题是似乎 AR 数据库或来自 Amazon Redshift 的 JDBC 驱动程序将我的列定义强制为 varchar(255) 而不是我正在尝试编写的 nvarchar(max) 或 varchar(max)。我尝试了不同的组合,但结果始终相同:

<simpleError in .local(conn, statement, ...): execute JDBC update query failed in dbSendUpdate ([Amazon](500310) Invalid operation: Value too long for character type
Details:
-----------------------------------------------
error:  Value too long for character type
code:      8001
context:   Value too long for type character varying(255)
query:     116225
location:  funcs_string.hpp:395
process:   padbmaster [pid=29705]
-----------------------------------------------;)>

如果我在发送查询之前打印c_query 变量(表创建查询),它会正确打印出来:

CREATE TABLE IF NOT EXISTS gsc_by_date_query_device ("date" date, "query" nvarchar(max), "device" nvarchar(max), "clicks" integer, "impressions" integer, "ctr" real, "position" integer, "gsc_domain" nvarchar(max));
CREATE TABLE IF NOT EXISTS gsc_by_date_query_country_device ("date" date, "query" nvarchar(max), "country" nvarchar(max), "device" nvarchar(max), "countryName" nvarchar(max), "clicks" integer, "impressions" integer, "ctr" real, "position" integer, "gsc_domain" nvarchar(max));
CREATE TABLE IF NOT EXISTS gsc_by_date_page_device ("date" date, "page" nvarchar(max), "device" nvarchar(max), "clicks" integer, "impressions" integer, "ctr" real, "position" real, "gsc_domain" nvarchar(max));

如果我在 SQLWorkbench/J(我用于检查的工具)上执行此操作,它会正确创建表,即使这样,失败的是数据插入。

您能告诉我我做错了什么吗?或者如何将文本列指定为大于 256 个字符?我做噩梦了,我想我已经尽我所能了。

【问题讨论】:

您能否在 SQLWorkbench 中运行时发布此查询的结果。 "select table_schema,table_name,column_name,character_maximum_length from information_schema.columns where table_name='gsc_by_date_query_device';"。另外,在运行此查询之前运行 CREATE 表语句 您使用哪个驱动程序来创建表? @TonyGibbs 我在代码示例中引用了它:RedshiftJDBC42-1.2.16.1027.jar,Amazon 提供的最新版本。 @theDbGuy 表定义看起来不错,但我仍然遇到同样的错误 @TonyGibbs 无论如何,我尝试了所有三个驱动程序,它们的行为都相同:( 【参考方案1】:

我写了一篇详尽的博文,解释了从 Amazon Redshift 读取/写入数据的许多细微差别:https://auth0.com/blog/a-comprehensive-guide-for-connecting-with-r-to-redshift/

特别是,使用 R 读取数据的最佳方式是使用 RPostgres 库,我推荐使用我创建的 R 包写入数据:https://github.com/sicarul/redshiftTools

特别是,它没有您报告的问题,varchars 是使用函数 calculateCharSize 根据字符串的长度创建的:https://github.com/sicarul/redshiftTools/blob/master/R/table_definition.R#L2

不过,作为最佳实践,我会说除非它是临时表或临时表,否则请尝试始终自己创建表,这样您就可以控制排序键、分配键和压缩,这些对 Amazon Redshift 的性能非常重要。

如果您已经创建了表,您可以执行以下操作:

rs_replace_table(data, dbcon=db, table_name=t_table_name, bucket="mybucket", split_files=4)

如果你还没有创建表,你可以用rs_create_table做几乎同样的事情

您需要一个 S3 存储桶和 AWS 密钥才能访问它,因为此包会上传到 S3,然后将 redshift 定向到该存储桶,这是批量上传数据的最快方式。

【讨论】:

如何解决这个问题!? @Achilleus 我在 3 年前写了这篇文章,试图帮助将表格上传到 Amazon Redshift(我现在正在使用 Snowflake),你为什么认为我的回答没有帮助?

以上是关于Amazon Redshift - 表列声明为 varchar(max) 但强制为 varchar(255)的主要内容,如果未能解决你的问题,请参考以下文章

复制命令 Amazon Redshift

在 Redshift 表列中看不到“Null”值

如何根据联接更新 Redshift 中的表列值?

无法在 Amazon Redshift 中将时间戳转换为日期

amazon datapipeline 中 redshift 副本的 sqlactivity 不会为文件名选择通配符

Amazon Redshift 返回日期名称