【问题标题】:Select field only if it exists (SQL or Scala)仅当字段存在时才选择字段(SQL 或 Scala)
【发布时间】:2019-08-09 03:20:07
【问题描述】:

输入数据框可能并不总是包含所有列。在 SQL 或 SCALA 中,我想创建一个 select 语句,即使数据框没有列,它也不会出错,只会输出确实存在的列。

例如,此语句将起作用。

Select store, prod, distance from table

+-----+------+--------+
|store|prod  |distance|
+-----+------+--------+
|51   |42    |2     |
|51   |42    |5     |
|89   |44    |9     |

如果数据框如下所示,我希望使用相同的语句,忽略不存在的内容,并输出现有列(在本例中为“存储”和“产品”)

+-----+------+
|store|prod  |
+-----+------+
|51   |42    |
|51   |42    |
|89   |44    |

【问题讨论】:

    标签: sql scala apache-spark dataframe apache-spark-sql


    【解决方案1】:

    您可以在Dataframe 上使用columns 方法。这看起来像这样:

    val result = if(df.columns.contains("distance")) df.select("store", "prod", "distance") 
                 else df.select("store", "prod")
    

    编辑:

    有很多这样的列,您可以将它们保存在数组中,例如colsfilter it:

    val selectedCols = cols.filter(col -> df.columns.contains("distance")).map(col)
    val result = df.select(selectedCols:_*)
    

    【讨论】:

    • 我正在尝试在大约 100 列的数据框中执行此操作。我并不总是知道数据框会丢失哪一列。有没有更动态的方法呢?类似于全选,只是忽略不存在的列。
    • 没问题。如果有帮助,请考虑支持/标记为正确;)
    【解决方案2】:

    假设您使用扩展的 SQL 模板,例如 select a,b,c from tab,您可以执行以下操作来获得所需的结果。

    1. 获取sql字符串并将其转换为小写。
    2. 用空格或逗号分割 sql 以获得数组中的单个单词
    3. 从上述数组中删除“select”和“from”,因为它们是 SQL 关键字。
    4. 现在您的最后一个索引是表名
    5. 第一个到最后一个索引,但一个包含选择列的列表。
    6. 要获得所需的列,只需根据 df2.columns 对其进行过滤。 SQL 中有但表中没有的列将被过滤掉
    7. 现在使用各个部分构建 sql。
    8. 使用 spark.sql(reqd_sel_string) 运行它以获得结果。

    看看这个

    scala> val df2 = Seq((51,42),(51,42),(89,44)).toDF("store","prod")
    df2: org.apache.spark.sql.DataFrame = [store: int, prod: int]
    
    scala> df2.createOrReplaceTempView("tab2")
    
    scala> val sel_query="Select store, prod, distance from tab2".toLowerCase
    sel_query: String = select store, prod, distance from tab2
    
    scala> val tabl_parse = sel_query.split("[ ,]+").filter(_!="select").filter(_!="from")
    tabl_parse: Array[String] = Array(store, prod, distance, tab2)
    
    scala> val tab_name=tabl_parse(tabl_parse.size-1)
    tab_name: String = tab2
    
    scala> val tab_cols = (0 until tabl_parse.size-1).map(tabl_parse(_))
    tab_cols: scala.collection.immutable.IndexedSeq[String] = Vector(store, prod, distance)
    
    scala> val reqd_cols = tab_cols.filter( x=>df2.columns.contains(x))
    reqd_cols: scala.collection.immutable.IndexedSeq[String] = Vector(store, prod)
    
    scala> val reqd_sel_string = "select " + reqd_cols.mkString(",") + " from " + tab_name
    reqd_sel_string: String = select store,prod from tab2
    
    scala> spark.sql(reqd_sel_string).show(false)
    +-----+----+
    |store|prod|
    +-----+----+
    |51   |42  |
    |51   |42  |
    |89   |44  |
    +-----+----+
    
    
    scala>
    

    【讨论】:

    • @touelv.. 这样的解析有帮助吗?
    • 确实如此,我很欣赏这个想法。这篇文章正上方和正下方的答案也可以以简化的方式工作
    • DownVoters .. 解决方案有什么问题?.. 请先在 cmets 中提及它.. 它适用于 OP..
    【解决方案3】:

    您可以在列表中列出所有列,无论是硬编码还是从其他元数据准备并使用相交

    val columnNames = Seq("c1","c2","c3","c4")
    
    df.select( df.columns.intersect(columnNames).map(x=>col(x)): _* ).show()
    

    【讨论】:

    • 在我的真实数据集中尝试时确实遇到了这个错误::153: error: type mismatch;发现:需要字符串:Int
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多