Apache Spark с Java, преобразование в тип даты из Varchar2 в Oracle не удается

У меня есть вариант использования, когда я хочу прочитать данные из одной таблицы Oracle, где все поля имеют тип varchar, и сохранить их в другой таблице Oracle с аналогичными полями, но с идеально правильным типом данных. Это нужно делать только в java. Итак, я хочу прочитать набор данных из таблицы ниже:

create table employeeStr (
 name varchar2(50),
 empid varchar2(50),
 age varchar2(50),
 salary varchar2(50),
 dt_joined varchar2(50));

и напишите в таблице ниже:

create table employeeNorm (
 name varchar2(50),
 empid number,
 age number(3,0),
 salary number(10,2),
 dt_joined date);

Мой код Java выглядит следующим образом:

SparkSession sparkSession =
        SparkSession.builder().master("local[*]").appName("HandlingOracleDataTypes").getOrCreate();

SQLContext sqlContext = sparkSession.sqlContext();

sqlContext.udf().register("toDate", new UDF1<String, java.sql.Date>() {
    @Override
    public java.sql.Date call(String dateStr) throws Exception {
        Date date = new SimpleDateFormat("yyyyMMdd").parse(dateStr);
        return new java.sql.Date(date.getTime());
    }
}, DataTypes.DateType);

sqlContext.udf().register("toDate2", new UDF1<String, Date>() {
    @Override
    public Date call(String dateStr) throws Exception {
        Date date = new SimpleDateFormat("yyyyMMdd").parse(dateStr);
        return date;
    }
}, DataTypes.DateType);

sqlContext.udf().register("toDate3", new UDF1<String, String>() {
    @Override
    public String call(String dateStr) throws Exception {
        Date date = new SimpleDateFormat("yyyyMMdd").parse(dateStr);
        return new SimpleDateFormat("dd-MMM-yyyy").format(date);
    }
}, DataTypes.StringType);

Properties connectionProperties = new Properties();
connectionProperties.put("user", "<username>");
connectionProperties.put("password", "<password>");

String jdbcUrl = "<jdbcurl>";

Dataset<Row> employeeStrDS = sparkSession.read().jdbc(jdbcUrl, "employeeStr", connectionProperties);

employeeStrDS.show();
employeeStrDS.printSchema();

employeeStrDS.withColumn("empid",employeeStrDS.col("empid").cast(DataTypes.IntegerType));
employeeStrDS.withColumn("age",employeeStrDS.col("age").cast(DataTypes.IntegerType));
employeeStrDS.withColumn("salary",employeeStrDS.col("salary").cast(DataTypes.FloatType));
//employeeStrDS.withColumn("dt_joined",employeeStrDS.col("dt_joined").cast(DataTypes.DateType));
//employeeStrDS.registerTempTable("abc");
//sqlContext.sql("select toDate(dt_joined) from abc").show();

employeeStrDS.withColumn("dt_joined", functions.callUDF("toDate3", employeeStrDS.col("dt_joined")));
//employeeStrDS.printSchema();
employeeStrDS.write().mode(SaveMode.Append).jdbc(jdbcUrl, "employeeNorm", connectionProperties);

Если я удаляю столбец «dt_joined» из таблицы и кода, этот код работает, но когда я добавляю столбец «dt_joined» в изображение, ничего не работает. Пробовал все 3 UDF, упомянутые в коде, но каждый раз получал исключение ниже. Пожалуйста, предложите решение этой проблемы.

Caused by: java.sql.BatchUpdateException: ORA-01861: literal does not match format string

    at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:12296)
    at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:246)
    at org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils$.savePartition(JdbcUtils.scala:597)
    at org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils$$anonfun$saveTable$1.apply(JdbcUtils.scala:670)
    at org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils$$anonfun$saveTable$1.apply(JdbcUtils.scala:670)
    at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:926)
    at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:926)
    at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1951)
    at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1951)
    at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:87)
    at org.apache.spark.scheduler.Task.run(Task.scala:99)
    at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:322)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.sql.SQLDataException: ORA-01861: literal does not match format string

    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:450)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:399)
    at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1059)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:522)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:257)
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:587)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:225)

Обновление: фактический сценарий: искровой код считывает данные из Impala, создает кадры данных. Таблица Impala имеет все столбцы как String. Таким образом, в основном проблема заключается в сохранении фрейма данных со схемой в виде всех строк в таблице Oracle с идеальными типами данных.


person abhihello123    schedule 22.10.2017    source источник
comment
Я бы предложил использовать spark-shell и написать код для сохранения только одной строки в таблице Oracle. При этом вы знаете, правильный формат или нет (по крайней мере).   -  person Jacek Laskowski    schedule 23.10.2017


Ответы (1)


Я не думаю, что вы можете извлечь выгоду из использования Spark в этом случае, поскольку вам нужно будет сначала извлечь все данные из базы данных Oracle в ваш кластер Spark, а затем обратно в базу данных Oracle. Используя SQL, вы можете делать все на месте (внутри Oracle DB). Все, что вам нужно сделать, это выполнить следующие операторы SQL (на стороне БД Oracle):

insert into employeeNorm
select name, empid, age, salary, to_date(dt_joined, 'yyyy-mm-dd')
from employeeStr;

commit;

вы должны заменить 'yyyy-mm-dd' соответствующим форматом даты и времени - подробности см. ниже...

ПРИМЕЧАНИЕ. формат даты/времени в функции Oracle to_date() не совместимый со стандартным форматом UNIX.

Вот минимальное отображение:

Oracle     UNIX
------     ----
YYYY       %Y
YY         %y
MM         %m
DD         %d
HH24       %H
MI         %M
SS         %S

Вам решать, как и где выполнять эти операторы - в Java, с использованием sqlplus и т. д.

PS, если employeeStr слишком велико, чтобы сделать все за одну транзакцию, вам следует рассмотреть возможность использования BULK INSERT в фрагменты.

person MaxU    schedule 22.10.2017
comment
см. обновление, фактический сценарий компилируется из базы данных Impala, поэтому мы должны использовать только Spark. - person abhihello123; 22.10.2017
comment
@abhihello123, пожалуйста, предоставьте небольшой воспроизводимый набор данных - person MaxU; 22.10.2017