Gerald
03/08/2023, 9:55 AMCREATE TABLE Person (
Id int,
Firstname string,
Lastname string,
DateOfBirth date
) WITH (
'connector' = 'kafka',
'topic' = 'cdc.FlinkResearch.dbo.Person',
...
'format' = 'debezium-avro-confluent',
'debezium-avro-confluent.url' = '...'
);
CREATE TABLE Address (
Id int,
Street string,
ZipCode string,
City string,
Country string,
PersonId int
) WITH (
'connector' = 'kafka',
'topic' = 'cdc.FlinkResearch.dbo.Address',
...
'format' = 'debezium-avro-confluent',
'debezium-avro-confluent.url' = '...'
);
CREATE TABLE PersonUpdated (
Id int,
Firstname string,
Lastname string,
DateOfBirth date,
Addresses ARRAY<ROW(Street string, City string, ZipCode string, Country string)>,
primary key (Id) not enforced
) WITH (
'connector' = 'upsert-kafka',
'topic' = 'cdc.FlinkResearch.dbo.PersonUpdated',
...
'value.format' = 'avro-confluent',
'value.avro-confluent.url' = '...',
'key.format' = 'avro-confluent',
'key.avro-confluent.url' = '...'
);
The idea is to create PersonUpdated entries for every person including the person's addresses as a list. We have tried various ways of casts in our (insert) queries, but still no success:
select p.Id, p.Firstname, p.Lastname, p.DateOfBirth, cast((select collect(row(Street, City, ZipCode, Country)) from Address where PersonId = p.Id) as ARRAY<ROW<Street string, City string, ZipCode string, Country string>>) as Addresses from Person p;
[ERROR] Could not execute SQL statement. Reason:
org.apache.calcite.sql.validate.SqlValidatorException: Cast function cannot convert value of type RecordType(VARCHAR(2147483647) EXPR$0, VARCHAR(2147483647) EXPR$1, VARCHAR(2147483647) EXPR$2, VARCHAR(2147483647) EXPR$3) MULTISET to type RecordType(VARCHAR(2147483647) Street, VARCHAR(2147483647) City, VARCHAR(2147483647) ZipCode, VARCHAR(2147483647) Country) ARRAY
Even more specific casts couldn't solve the issue:
select p.Id, p.Firstname, p.Lastname, p.DateOfBirth, cast((select collect(cast(row(Street, City, ZipCode, Country) as ROW<Street string, City string, ZipCode string, Country string>)) from Address where PersonId = p.Id) as ARRAY<ROW<Street string, City string, ZipCode string, Country string>>) as Addresses from Person p;
[ERROR] Could not execute SQL statement. Reason:
org.apache.calcite.sql.validate.SqlValidatorException: Cast function cannot convert value of type RecordType(VARCHAR(2147483647) Street, VARCHAR(2147483647) City, VARCHAR(2147483647) ZipCode, VARCHAR(2147483647) Country) MULTISET to type RecordType(VARCHAR(2147483647) Street, VARCHAR(2147483647) City, VARCHAR(2147483647) ZipCode, VARCHAR(2147483647) Country) ARRAY
Is there a way to cast nested multisets to an array with plain Flink SQL? Or are there alternatives to the collect function that would avoid casting at all?Martijn Visser
03/08/2023, 9:57 AMGerald
03/08/2023, 10:11 AMHunter Medney
03/08/2023, 3:12 PMpackage myproj.flink.udf;
import java.util.Map;
import org.apache.flink.table.annotation.DataTypeHint;
import org.apache.flink.table.annotation.FunctionHint;
import org.apache.flink.table.functions.ScalarFunction;
public class MultisetToStringArray extends ScalarFunction {
@FunctionHint(input = @DataTypeHint("MULTISET<STRING>"), output = @DataTypeHint("ARRAY<STRING>"))
public String[] eval(Map<String, Integer> multiSet) {
return multiSet.keySet().toArray(String[]::new);
}
}
Test SQL:
CREATE TEMPORARY FUNCTION MultisetToStringArray AS 'myproj.flink.udf.MultisetToStringArray' LANGUAGE JAVA;
CREATE TABLE source (
group_id_gen INT,
group_id AS 'group' || CAST(group_id_gen AS STRING),
col_a_string STRING,
col_b_int INT,
col_c_timestamp TIMESTAMP,
col_d_row ROW(
row_d_1 STRING,
row_d_2 STRING,
row_d_3 INT
),
col_e_array_string ARRAY<STRING>,
col_f_array_bigint ARRAY<BIGINT>,
col_g_array_row ARRAY<ROW(
array_row_g_1 STRING,
array_row_g_2 STRING,
array_row_g_3 INT
)>
) WITH (
'connector' = 'datagen',
'rows-per-second' = '1',
'fields.group_id_gen.min' = '1',
'fields.group_id_gen.max' = '10',
'fields.col_a_string.length' = '3',
'fields.col_d_row.row_d_1.length' = '4',
'fields.col_b_int.min' = '1',
'fields.col_b_int.max' = '10000'
);
SELECT
group_id,
MultisetToStringArray(COLLECT(DISTINCT col_a_string))
FROM source
GROUP BY group_id;Gerald
03/08/2023, 4:41 PMObject instead of String as I can't infer the actual type of the ROW, similar to the col_g_array_row in your example?