Spark 2.1 +
Вы можете использовать функцию from_json
:
import org.apache.spark.sql.functions.from_json
import org.apache.spark.sql.types._
val schema = StructType(Seq(
StructField("k", StringType, true), StructField("v", DoubleType, true)
))
df.withColumn("jsonData", from_json($"jsonData", schema))
Spark 1.6 +
Вы можете используйте get_json_object
, который берет столбец и путь:
import org.apache.spark.sql.functions.get_json_object
val exprs = Seq("k", "v").map(
c => get_json_object($"jsonData", s"$$.$c").alias(c))
df.select($"*" +: exprs: _*)
и извлекает поля для отдельных строк, которые могут быть добавлены к ожидаемым типам.
Аргумент path
выраженный с использованием точечного синтаксиса, с ведущим $.
, обозначающим корень документа (поскольку используемый выше код использует интерполяцию строк $
, поэтому $$.
).
Spark & lt; = 1.5:
Возможно ли это?
Насколько я знаю, это невозможно. Вы можете попробовать что-то похожее на это:
val df = sc.parallelize(Seq( ("1", """{"k": "foo", "v": 1.0}""", "some_other_field_1"), ("2", """{"k": "bar", "v": 3.0}""", "some_other_field_2") )).toDF("key", "jsonData", "blobData")
Я предполагаю, что поле
blob
не может быть представлено в JSON. В противном случае вы откажитесь от разделения и соединения:import org.apache.spark.sql.Row val blobs = df.drop("jsonData").withColumnRenamed("key", "bkey") val jsons = sqlContext.read.json(df.drop("blobData").map{ case Row(key: String, json: String) => s"""{"key": "$key", "jsonData": $json}""" }) val parsed = jsons.join(blobs, $"key" === $"bkey").drop("bkey") parsed.printSchema // root // |-- jsonData: struct (nullable = true) // | |-- k: string (nullable = true) // | |-- v: double (nullable = true) // |-- key: long (nullable = true) // |-- blobData: string (nullable = true)
Альтернативный (более дешевый, хотя и более сложный) подход - использовать UDF для разбора JSON и вывода столбца
struct
илиmap
. Например, что-то вроде этого:import net.liftweb.json.parse case class KV(k: String, v: Int) val parseJson = udf((s: String) => { implicit val formats = net.liftweb.json.DefaultFormats parse(s).extract[KV] }) val parsed = df.withColumn("parsedJSON", parseJson($"jsonData")) parsed.show // +---+--------------------+------------------+----------+ // |key| jsonData| blobData|parsedJSON| // +---+--------------------+------------------+----------+ // | 1|{"k": "foo", "v":...|some_other_field_1| [foo,1]| // | 2|{"k": "bar", "v":...|some_other_field_2| [bar,3]| // +---+--------------------+------------------+----------+ parsed.printSchema // root // |-- key: string (nullable = true) // |-- jsonData: string (nullable = true) // |-- blobData: string (nullable = true) // |-- parsedJSON: struct (nullable = true) // | |-- k: string (nullable = true) // | |-- v: integer (nullable = false)
Вы можете использовать только псевдонимы столбцов в предложениях GROUP BY, ORDER BY или HAVING.
Стандартный SQL не позволяет ссылаться на псевдоним столбца в предложении WHERE. Это ограничение наложено, потому что, когда выполняется код WHERE, значение столбца еще не может быть определено.
blockquote>Скопировано из Документация MySQL
Как указано в комментариях, использование HAVING вместо этого может выполнить эту работу. Обязательно прочитайте это WHERE vs HAVING .
Возможно, мой ответ слишком поздний, но это может помочь другим.
Вы можете заключить его в другой оператор select и использовать в нем предложение where.
SELECT * FROM (Select col1, col2,...) as t WHERE t.calcAlias > 0
calcAlias - это столбец псевдонимов, который был рассчитан.
Вы можете использовать предложение HAVING для фильтра, рассчитанного в полях SELECT и псевдонимах
WHERE
и HAVING
. Они не идентичны. stackoverflow.com/search?q=where+vs+having
– rinogo
22 June 2017 в 21:05
Стандартный SQL (или MySQL) не позволяет использовать псевдонимы столбцов в предложении WHERE, потому что
при вычислении предложения WHERE значение столбца может еще не определено.
blockquote>(из документации MySQL ). Вы можете вычислить значение столбца в предложении WHERE , сохранить значение в переменной и использовать его в списке полей. Например, вы можете сделать это:
SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`, @postcode AS `guaranteed_postcode` FROM `users` LEFT OUTER JOIN `locations` ON `users`.`id` = `locations`.`user_id` WHERE (@postcode := SUBSTRING(`locations`.`raw`,-6,4)) NOT IN ( SELECT `postcode` FROM `postcodes` WHERE `region` IN ( 'australia' ) )
Это позволяет избежать повторения выражения, когда оно усложняется, что упрощает его работу.
select @code:=sum(2), 2*@code
работает в MySQL 5.5, но для меня в 5.6 второй столбец дает NULL при первом вызове и возвращает 2 раза предыдущий результат i> при повторном запуске. Интересно, что оба варианта @code:=2, 2*@code
и select @code:=rand(), 2*@code
действительно работают в моем 5.6 (сегодня). Но они действительно записывают и читают в предложении SELECT; в вашем случае вы устанавливаете его в ГДЕ.
– Arjan
21 November 2014 в 08:59
Я использую mysql 5.5.24, и работает следующий код:
select * from (
SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
) as a
WHERE guaranteed_postcode NOT IN --this is where the fake col is being used
(
SELECT `postcode` FROM `postcodes` WHERE `region` IN
(
'australia'
)
)
Стандартный SQL запрещает ссылки на псевдонимы столбцов в предложении WHERE. Это ограничение наложено, потому что, когда вычисляется предложение WHERE, значение столбца может еще не определено. Например, следующий запрос является незаконным:
SELECT id, COUNT (*) AS cnt FROM tbl_name WHERE cnt> 0 GROUP BY id;
Как отметил Виктор, проблема связана с псевдонимом. Этого можно избежать, поставив выражение непосредственно в предложение WHERE x IN y:
SELECT `users`.`first_name`,`users`.`last_name`,`users`.`email`,SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE SUBSTRING(`locations`.`raw`,-6,4) NOT IN #this is where the fake col is being used
(
SELECT `postcode` FROM `postcodes` WHERE `region` IN
(
'australia'
)
)
Однако, я думаю, это очень неэффективно, так как подзапрос должен выполняться для каждой строки внешний запрос.
Вы можете использовать SUBSTRING (locations
. raw
, - 6,4), где условие
SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`,
SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode`
FROM `users` LEFT OUTER JOIN `locations`
ON `users`.`id` = `locations`.`user_id`
WHERE SUBSTRING(`locations`.`raw`,-6,4) NOT IN #this is where the fake col is being used
(
SELECT `postcode` FROM `postcodes` WHERE `region` IN
(
'australia'
)
)
HAVING
выполняется медленнее, чемWHERE
– DTs 25 April 2014 в 09:38