Как извлечь, где предложение как массив в спарк sql? - PullRequest
1 голос
/ 05 января 2020

Я пытаюсь извлечь предложение where из запроса SQL. Несколько условий, в которых предложение должно быть в массиве формы. Пожалуйста, помогите мне.

Пример строки ввода:

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'")

1 Ответ

3 голосов
/ 05 января 2020

Я бы не использовал Regex для этого. Вот альтернативный способ извлечь ваши условия на основе логического плана 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')

Дополнительные примечания : если вы хотите напечатать имена атрибутов без начального цитата, вы можете справиться с этим, напечатав это вместо toString:

node.asInstanceOf[UnresolvedAttribute].name
...