【问题标题】:How to extract where clause as array in spark sql?如何在spark sql中提取where子句作为数组?
【发布时间】:2020-01-15 04:45:36
【问题描述】:

我正在尝试从 SQL 查询中提取 where 子句。 where 子句中的多个条件应在表单数组中。请帮我。

示例输入字符串:

select * from table where col1=1 and (col2 between 1 and 10 or col2 between 190 and 200) and col2 is not null 

预期输出:

Array("col1=1", "(col2 between 1 and 10 or col2 between 190 and 200)", "col2 is not null")

提前致谢。

编辑:

我的问题是......我想将所有条件拆分为单独的项目......假设我的查询就像

select * from table where col1=1 and (col2 between 1 and 10 or col2 between 190 and 200) and col2 is not null

我期待的输出是这样的

List("col1=1", "col2 between 1 and 10", "col2 between 190 and 200", "col2 is not null")

问题是查询可能有多个级别的条件,例如

select * from table where col1=1 and (col2 =2 or(col3 between 1 and 10 or col3 is between 190 and 200)) and col4='xyz'

在输出中每个条件都应该是一个单独的项目

List("col1=1","col2=2", "col3 between 1 and 10", "col3 between 190 and 200", "col4='xyz'")

【问题讨论】:

  • 你能解析你的sql查询字符串吗?
  • 嗨@sarvan,你不区分ANDOR 这是故意的吗?
  • 嗨@Baitmbarek,基本上我试图从树中提取所有孩子,而不考虑AND或OR。我正在尝试一些递归 fn 来提取但没有运气

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


【解决方案1】:

我不会为此使用正则表达式。这是根据 Catalyst 的逻辑计划提取条件的另一种方法:

val plan = df.queryExecution.logical
val predicates: Seq[Expression] = plan.children.collect{case f: Filter =>
    f.condition.productIterator.flatMap{
      case And(l,r) => Seq(l,r)
      case o:Predicate => Seq(o)
    }
}.toList.flatten

println(predicates)

输出:

List(('col1 = 1), ((('col2 >= 1) && ('col2 <= 10)) || (('col2 >= 190) && ('col2 <= 200))), isnotnull('col2))

这里的谓词仍然是Expressions 并保存信息(树表示)。

编辑: 正如评论中所问的,这是谓词的字符串(我希望用户友好)表示:)

val plan = df.queryExecution.logical
val predicates: Seq[Expression] = plan.children.collect{case f: Filter =>
    f.condition.productIterator.flatMap{
      case o:Predicate => Seq(o)
    }
}.toList.flatten

def stringifyExpressions(expression: Expression): Seq[String] = {
  expression match{
    case And(l,r) => (l,r) match {
      case (gte: GreaterThanOrEqual,lte: LessThanOrEqual) => Seq(s"""${gte.left.toString} between ${gte.right.toString} and ${lte.right.toString}""")
      case (_,_) => Seq(l,r).flatMap(stringifyExpressions)
    }
    case Or(l,r) => Seq(Seq(l,r).flatMap(stringifyExpressions).mkString("(",") OR (", ")"))
    case eq: EqualTo => Seq(s"${eq.left.toString} = ${eq.right.toString}")
    case inn: IsNotNull => Seq(s"${inn.child.toString} is not null")
    case p: Predicate => Seq(p.toString)
  }
}

val stringRepresentation = predicates.flatMap{stringifyExpressions}

println(stringRepresentation)

新输出:

List('col1 = 1, ('col2 between 1 and 10) OR ('col2 between 190 and 200), 'col2 is not null)

如果您想自定义输出,可以继续使用递归 stringifyExpressions 方法。

编辑 2: 回应您自己的编辑:

您可以将Or / EqualTo 的情况更改为以下

def stringifyExpressions(expression: Expression): Seq[String] = {
  expression match{
    case And(l,r) => (l,r) match {
      case (gte: GreaterThanOrEqual,lte: LessThanOrEqual) => Seq(s"""${gte.left.toString} between ${gte.right.toString} and ${lte.right.toString}""")
      case (_,_) => Seq(l,r).flatMap(stringifyExpressions)
    }
    case Or(l,r) => Seq(l,r).flatMap(stringifyExpressions)
    case EqualTo(l,r) =>
      val prettyLeft = if(l.resolved && l.dataType == StringType) s"'${l.toString}'" else l.toString
      val prettyRight = if(r.resolved && r.dataType == StringType) s"'${r.toString}'" else r.toString
      Seq(s"$prettyLeft=$prettyRight")
    case inn: IsNotNull => Seq(s"${inn.child.toString} is not null")
    case p: Predicate => Seq(p.toString)
  }
}

这给出了 4 个元素列表:

List('col1=1, 'col2 between 1 and 10, 'col2 between 190 and 200, 'col2 is not null)

第二个例子:

select * from table where col1=1 and (col2 =2 or (col3 between 1 and 10 or col3 between 190 and 200)) and col4='xyz'

你会得到这个输出(List[String] 有 5 个元素):

List('col1=1, 'col2=2, 'col3 between 1 and 10, 'col3 between 190 and 200, 'col4='xyz')

补充说明:如果要打印不带引号的属性名称,可以通过打印 this 而不是 toString 来处理:

node.asInstanceOf[UnresolvedAttribute].name

【讨论】:

  • 我想得到如下输出List("col1=1","col2 between 1 and 10", "col2 between 190 and 200", "col2 is not null")你能帮我吗
  • between 关键字是And(l,r) 的特例。我会在接下来的几天内尝试一下。
  • @sarvan 嗨!刚刚编辑了答案,我希望输出更具人类可读性;)
  • 非常感谢。我的问题有点不同。我在原始 qn 中更新我的 qn。
  • @sarvan 您可以查看 EDIT 2 它应该符合您的需求。如果需要,您可以根据需要更改字符串表示形式,具体取决于您的使用情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-07
  • 1970-01-01
  • 2016-08-09
  • 1970-01-01
  • 2013-07-13
  • 2020-09-22
相关资源
最近更新 更多