如何将 MySQL 语法 WITH `cte` AS (VALUES ROW(1), ROW(2), ROW(n)) 转换为 jOOQ DSL?
Posted
技术标签:
【中文标题】如何将 MySQL 语法 WITH `cte` AS (VALUES ROW(1), ROW(2), ROW(n)) 转换为 jOOQ DSL?【英文标题】:How convert MySQL syntax WITH `cte` AS (VALUES ROW(1), ROW(2), ROW(n)) to jOOQ DSL? 【发布时间】:2021-10-08 22:18:49 【问题描述】:我有一个 mysql 的 SQL:
WITH `cte` AS (
( SELECT 1431655747 AS `n` FROM DUAL ) UNION ALL
( SELECT 1431655733 AS `n` FROM DUAL ) UNION ALL
( SELECT 715827794 AS `n` FROM DUAL ) UNION ALL
( SELECT 715827865 AS `n` FROM DUAL ) UNION ALL
( SELECT 1073741809 AS `n` FROM DUAL ) UNION ALL
( SELECT 1073741759 AS `n` FROM DUAL ) UNION ALL
( SELECT 715827800 AS `n` FROM DUAL ) UNION ALL
( SELECT 1431655693 AS `n` FROM DUAL ) UNION ALL
( SELECT 715827789 AS `n` FROM DUAL ) UNION ALL
( SELECT 715827838 AS `n` FROM DUAL ) UNION ALL
( SELECT 715827823 AS `n` FROM DUAL ) UNION ALL
( SELECT 858993391 AS `n` FROM DUAL )) SELECT
`cte`.`n`,
`maxmind_country`.`country`.`name_en`
FROM
`cte`
JOIN `maxmind_country`.`ipv4` ON `cte`.`n` BETWEEN `maxmind_country`.`ipv4`.`start_int`
AND `maxmind_country`.`ipv4`.`last_int`
JOIN `maxmind_country`.`country` ON `maxmind_country`.`country`.`geoname_id` = `maxmind_country`.`ipv4`.`v_geoname_id`;
转换为 jOOQ DSL 后:
String ipAlias = "n";
SelectSelectStep<Record1<UInteger>> unionIps = ips
.stream()
.distinct()
.map(value -> DSL.select(DSL.val(value).as(ipAlias)))
.reduce((r1, r2) -> (SelectSelectStep<Record1<UInteger>>) r1.unionAll(r2))
.orElse(null);
if(unionIps == null)
return null;
CommonTableExpression<Record1<UInteger>> cte = DSL.name("cte").as(unionIps);
Field<UInteger> ipField = cte.field(ipAlias, UInteger.class);
return dslContext
.with(cte)
.select(ipField, COUNTRY.NAME_EN)
.from(cte)
.join(IPV4).on(ipField.between(IPV4.START_INT, IPV4.LAST_INT))
.join(COUNTRY).on(COUNTRY.GEONAME_ID.eq(IPV4.V_GEONAME_ID))
.fetchMap(ipField, Country.class);
结果将是:
+------------+----------------+
| n | name_en |
+------------+----------------+
| 1431655747 | Spain |
| 1431655733 | Spain |
| 715827794 | China |
| 715827865 | China |
| 1073741809 | United States |
| 1073741759 | United States |
| 715827800 | China |
| 1431655693 | Spain |
| 715827789 | China |
| 715827838 | China |
| 715827823 | China |
| 858993391 | United Kingdom |
+------------+----------------+
12 rows in set (0.16 sec)
但是解释显示了一个多余的UNION:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <derived2> ALL 12 100,00
1 PRIMARY ipv4 ALL ipv4_start_int_UNIQUE,ipv4_last_int_UNIQUE,ipv4_v_geoname_id_fk_idx 325934 11,11 Range checked for each record (index map: 0x7)
1 PRIMARY country eq_ref PRIMARY PRIMARY 4 maxmind_country.ipv4.v_geoname_id 1 100,00
2 DERIVED No tables used
3 UNION No tables used
4 UNION No tables used
5 UNION No tables used
6 UNION No tables used
7 UNION No tables used
8 UNION No tables used
9 UNION No tables used
10 UNION No tables used
11 UNION No tables used
12 UNION No tables used
13 UNION No tables used
我用另一种 MySQL 语法重写了一个 SQL - VALUES ROW(1)、ROW(2)、ROW(n):
WITH `cte` AS (
VALUES ROW(1431655747),
ROW(1431655733),
ROW(715827794),
ROW(715827865),
ROW(1073741809),
ROW(1073741759),
ROW(715827800),
ROW(1431655693),
ROW(715827789),
ROW(715827838),
ROW(715827823),
ROW(858993391)) SELECT
`cte`.`column_0`,
`maxmind_country`.`country`.`name_en`
FROM
`cte`
JOIN `maxmind_country`.`ipv4` ON `cte`.`column_0` BETWEEN `maxmind_country`.`ipv4`.`start_int`
AND `maxmind_country`.`ipv4`.`last_int`
JOIN `maxmind_country`.`country` ON `maxmind_country`.`country`.`geoname_id` = `maxmind_country`.`ipv4`.`v_geoname_id`;
结果相同:
+------------+----------------+
| column_0 | name_en |
+------------+----------------+
| 1431655747 | Spain |
| 1431655733 | Spain |
| 715827794 | China |
| 715827865 | China |
| 1073741809 | United States |
| 1073741759 | United States |
| 715827800 | China |
| 1431655693 | Spain |
| 715827789 | China |
| 715827838 | China |
| 715827823 | China |
| 858993391 | United Kingdom |
+------------+----------------+
12 rows in set (0.16 sec)
但是规划器不使用UNION的:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY <derived2> ALL 12 100,00
1 PRIMARY ipv4 ALL ipv4_start_int_UNIQUE,ipv4_last_int_UNIQUE,ipv4_v_geoname_id_fk_idx 325934 11,11 Range checked for each record (index map: 0x7)
1 PRIMARY country eq_ref PRIMARY PRIMARY 4 maxmind_country.ipv4.v_geoname_id 1 100,00
2 DERIVED No tables used
问题:如何将最后一个脚本转换为 jOOQ DSL?我没有找到任何方法
WITH `cte` AS (VALUES ROW(1), ROW(2), ROW(n)) ... ?
【问题讨论】:
为什么你认为解释中的工会有问题? 【参考方案1】:从 jOOQ 3.15 开始,还没有办法将 standard SQL VALUES
constructor 表示为 org.jooq.Select
。它只能表示为org.jooq.Table
,参见:https://github.com/jOOQ/jOOQ/issues/5871。
因此,您还不能使用 jOOQ DSL 来表示所需的确切语法,但您可以在 FROM
子句中使用 VALUES
:
selectFrom(values(...))
这应该会产生与您正在寻找的计划几乎相同的计划。另一种解决方法是使用plain SQL templating
【讨论】:
以上是关于如何将 MySQL 语法 WITH `cte` AS (VALUES ROW(1), ROW(2), ROW(n)) 转换为 jOOQ DSL?的主要内容,如果未能解决你的问题,请参考以下文章