所以我有这2个文件(获取hive表的hdfs文件位置),具有历史记录的file1和具有当前日期的file2。它们都具有相同的架构。现在,我想对两个文件进行比较后,执行CDC流程以获取更新的/新插入的记录。 多个列中可能会有更改,因此我们想一次提取所有更改的列。 假设这些列是:-Customer_ID,名称,地址,国家/地区。 现在,Customer_ID是主键,而其余3列可能会更改。
文件1
12343| John| Rear exit market| SanFrancisco
45656| Bobs| Knewbound Road PD| Seattle
54345| Fersi| Dallas Road Pnth| Newyork
86575| Persa| Roman Building Path| Kirkland
64565| Camy| Olympus Ground 3rd| NewJersey
文件2
12343| John| World Centre Phase| SanFrancisco
54345| Posi| Dallas Road Pnth| Newyork
我希望最终结果看起来像:-
12343|Rear exit market| World Centre Phase
54345| Fersi| Posi
所以我想要主键,更改前的先前记录,新的更新记录,这些记录在我的最终答案中得到了更新。
答案 0 :(得分:0)
这是一个可能的解决方案。正如我的评论中提到的那样,这几乎是3或4班轮的价格,但我提供了一些替代方案。
// Load the data into 2 dataframes
val df1 = spark.read.option("sep","|").csv("file1a.txt")
val df2 = spark.read.option("sep","|").csv("file2a.txt")
// Next join the two dataframes using an INNER JOIN on the key as follows:
val joined = df1.joinWith(df2, df1.col("_c0") === df2.col("_c0"))
文件中没有标题信息,因此列将获得默认名称。联接的模式基本上是一个Tuple2,每个Tuple2包含联接每一侧的列列表。
以下是架构:
scala> df1.printSchema
root
|-- _c0: string (nullable = true)
|-- _c1: string (nullable = true)
|-- _c2: string (nullable = true)
|-- _c3: string (nullable = true)
scala> joined.printSchema
root
|-- _1: struct (nullable = false)
| |-- _c0: string (nullable = true)
| |-- _c1: string (nullable = true)
| |-- _c2: string (nullable = true)
| |-- _c3: string (nullable = true)
|-- _2: struct (nullable = false)
| |-- _c0: string (nullable = true)
| |-- _c1: string (nullable = true)
| |-- _c2: string (nullable = true)
| |-- _c3: string (nullable = true)
最后一步是(我认为)您想要生产的东西的外推法。我认为您想显示哪些列具有不同的值。您显示的输出格式,恕我直言,有两个潜在的问题。我认为您只是想在两个输出列中显示一个不同的值。恕我直言,这有两个挑战:
以下输出格式通过显示所有列以及一个指示符来显示哪些列具有不同的值,从而解决了上述问题。指标是关键,因为它使差异更容易发现。 这是“强力”方法,其中列出了每一列,并且手动确定了每个差异。
joined.select($"_1._C0".as("id"), $"_1._c1", $"_2._c1", when(col("_1._c1") === col("_2._c1"), "").otherwise("ne").as("c1 Ind"),
$"_1._c2", $"_2._c2", when(col("_1._c2") === col("_2._c2"), "").otherwise("ne").as("c2 Ind"),
$"_1._c3", $"_2._c3", when(col("_1._c3") === col("_2._c3"), "").otherwise("ne").as("c3 Ind")).show(false)
哪个会产生:
+-----+-----+----+------+----------------+------------------+------+------------+------------+------+
|id |_c1 |_c1 |c1 Ind|_c2 |_c2 |c2 Ind|_c3 |_c3 |c3 Ind|
+-----+-----+----+------+----------------+------------------+------+------------+------------+------+
|12343|John |John| |Rear exit market|World Centre Phase|ne |SanFrancisco|SanFrancisco| |
|54345|Fersi|Posi|ne |Dallas Road Pnth|Dallas Road Pnth | |Newyork |Newyork | |
+-----+-----+----+------+----------------+------------------+------+------------+------------+------+
蛮力法很乏味且难以输入-特别是对于较大的结果集。因此,我们可以使用一些Scala魔术使它更加优雅。
// Define a helper function that takes a column name and returns the three parts needed
// to generate the output for that column. i.e. select the column from the two sides of the joined result set
// and generate the case statement to generate the "ne" indicator if the two values
// are unequal.
def genComp(colName:String) = List(s"_1.$colName", s"_2.$colName", s"case when _1.$colName = _2.$colName then '' else 'ne' end as ${colName}_ind")
// Run the query to produce the results:
joined.selectExpr(
(List("_1._C0 as id") ++ genComp("_c1") ++ genComp("_c2") ++ genComp("_c3")) : _*
).show(false)
运行时,其产生的结果与“强力”方法相同。
这是如何工作的?好在第二行是魔术,它是selectExpr方法的功能。
selectExpr方法具有以下签名:def selectExpr(exprs: String*): org.apache.spark.sql.DataFrame
。这意味着它可以接受可变数量的字符串参数。
要生成传递给selectExpr的参数,我使用此构造List (strings) : _*
。这是Scala的“魔术”,它接收字符串列表并将其转换为可变数量的参数参数列表。
其余的非常简单。基本上,genComp函数会返回一个字符串列表,这些字符串用于标识联接的DataFrame的每一侧中的列以及不等式指示符生成逻辑。将它们连接在一起。结果转换为传递给selectExpr的参数列表,该列表最终运行与“蛮力”方法相同的查询。
这是一个有趣的想法,我将作为练习:df1的架构使用genComp生成要输出的列的列表(而不是像我所示的那样简单地手动串联它们)。
这是一个 big 提示:
val cols = df1.schema.filter(c => c.name != "_c0").map(c => List(c.name)).flatten
cols.foreach(println)