Google BigQuery:UI可正常运行的查询在命令行执行失败
看起来你遇到的问题是查询在BigQuery UI里能正常运行,但用bq query命令执行时却报错,核心原因是命令行的shell解析规则和BQ UI不一样,尤其是字符串里的引号处理逻辑差异导致的。
先拆解下你的命令里的关键问题:你用单引号'包裹了整个查询语句,但查询的CASE分支里又用了单引号(比如'#FF0000'),这会让shell误以为第一个内部单引号就是整个查询字符串的结束标记,剩下的内容会被shell当成其他无关命令,导致BQ接收到的查询语句不完整,自然就会报错。
下面给你几个可行的解决方案:
方案1:转义查询内部的单引号
在bash/zsh这类shell里,要在单引号包裹的字符串里使用单引号,需要用'\''来转义(部分shell也支持用''转义)。修改后的命令示例如下:
bq query --destination_table Chicago_Traffic_Sensor_Data.Latest_Traffic_Data_with_Geocoding --replace --use_legacy_sql=false 'SELECT segmentid, _lif_lat, start_lon, _lit_lat, _lit_lon, _traffic, _last_updt, CASE WHEN _traffic < 20 THEN '\''#FF0000'\'' WHEN _traffic >= 20 and _traffic < 40 THEN '\''#FFFF00'\'' WHEN _traffic >= 40 THEN '\''008000'\'' END AS traffic_color FROM your_source_table'
(注:我补全了你CASE里没写完的'008...'为'008000',如果实际值不同你可以自行替换)
方案2:改用双引号包裹整个查询
如果你的查询里没有需要shell解析的变量(比如$var这类),可以用双引号"包裹整个查询,这样内部的单引号就不需要额外转义了:
bq query --destination_table Chicago_Traffic_Sensor_Data.Latest_Traffic_Data_with_Geocoding --replace --use_legacy_sql=false "SELECT segmentid, _lif_lat, start_lon, _lit_lat, _lit_lon, _traffic, _last_updt, CASE WHEN _traffic < 20 THEN '#FF0000' WHEN _traffic >= 20 and _traffic < 40 THEN '#FFFF00' WHEN _traffic >= 40 THEN '008000' END AS traffic_color FROM your_source_table"
⚠️ 注意:如果查询里包含$开头的内容,shell会把它当成环境变量解析,这时候就不能用这个方案,得回到方案1。
方案3:使用Here-Document(更易读)
如果查询语句很长,用上面的方式容易出错,可以用here-doc来传递查询内容,这样完全不需要处理引号转义,可读性也更好:
bq query --destination_table Chicago_Traffic_Sensor_Data.Latest_Traffic_Data_with_Geocoding --replace --use_legacy_sql=false <<EOF SELECT segmentid, _lif_lat, start_lon, _lit_lat, _lit_lon, _traffic, _last_updt, CASE WHEN _traffic < 20 THEN '#FF0000' WHEN _traffic >= 20 and _traffic < 40 THEN '#FFFF00' WHEN _traffic >= 40 THEN '008000' END AS traffic_color FROM your_source_table EOF
这种方式把查询放在<<EOF和EOF之间,shell会原样把内容传递给bq query,完美规避引号解析的问题,特别适合复杂查询。
另外,建议你执行命令前可以先用echo把查询内容打印出来,看看shell解析后的结果是不是和你在BQ UI里写的一致,这样能快速定位引号转义的问题。
内容的提问来源于stack exchange,提问作者P. Patel

