如何将 MYSQL 查询翻译成 JooQ

How to translate a MYSQL query to JooQ

我最近开始使用 JooQ,我想实现下面的查询,但我无法做到。

insert into user_organization_role(id_user, id_organization, id_role,id_workspace)
with cte_0 as(select id_user
                     , id_organization
                     , id_role 
                     , id_workspace
              from user_organization_role
              where id_user = 0)
select distinct ur.id_user
                , cte_0.id_organization
                , cte_0.id_role
                , cte_0.id_workspace
from user_organization_role ur
     , cte_0
where ur.id_user <> 0
and (ur.id_user, cte_0.id_organization, cte_0.id_role, cte_0.id_workspace) not in (select id_user, id_organization, id_role, id_workspace from user_organization_role where id_user <> 0)

到目前为止我已经达到了下一个选项,但我不知道如何继续以及如何使其发挥作用。

userDSLContext.insertInto(Tables.USER_ORGANIZATION_ROLE.asterisk(), DSL.with("cte_0")
                .as(DSL.select(Tables.USER_ORGANIZATION_ROLE.asterisk()).from(Tables.USER_ORGANIZATION_ROLE).where(Tables.USER_ORGANIZATION_ROLE.ID_USER.eq(0)))
                .selectDistinct(DSL.table(DSL.name("ur")).field("ID_USER"), DSL.table(DSL.name("cte_0")).field("ID_ORGANIZATION"),
                DSL.table(DSL.name("cte_0")).field("ID_ROLE"), DSL.table(DSL.name("cte_0")).field("ID_WORKSPACE"))
                .from(Tables.USER_ORGANIZATION_ROLE.as("ur"), DSL.table(DSL.name("cte_0"))).where(
                                DSL.table(DSL.name("cte_0")).field("ID_WORKSPACE").ne(DSL.inline(0)).and(
                                        DSL.table(DSL.name("ur")).field("ID_USER"), DSL.table(DSL.name("cte_0")).field("ID_ORGANIZATION"),
                                        DSL.table(DSL.name("cte_0")).field("ID_ROLE"), DSL.table(DSL.name("cte_0")).field("ID_WORKSPACE")
                                )
                ));

让我们看看您查询的各个部分

插入

第一个问题是您的 INSERT 语句。您写道:

insertInto(Tables.USER_ORGANIZATION_ROLE.asterisk(), ...)

但是jOOQ中没有这样的方法API。你为什么这样写? DSLContext.insertInto(Table<?>, ...) accepts a table first, not an org.jooq.QualifiedAsterisk.

您可能使用了 asterisk() 方法,因为您认为这会以某种方式映射到插入所有列?但是您也没有在 SQL 查询中使用 USER_ORGANIZATION_ROLE.* 作为表达式,那么为什么要这样做呢?

为什么不直接翻译您的原始查询:

// I'm just going to assume the usual static imports, to reduce "noise"
insertInto(USER_ORGANIZATION_ROLE,
    USER_ORGANIZATION_ROLE.ID_USER,
    USER_ORGANIZATION_ROLE.ID_ORGANIZATION,
    USER_ORGANIZATION_ROLE.ID_ROLE,
    USER_ORGANIZATION_ROLE.ID_WORKSPACE)

插入 .. SELECT

您试图将 Select 表达式直接传递给 insertInto() 方法,但是同样,jOOQ API 没有接受 Select 以及 table。 example from the manual seems clear?

create.insertInto(table)
      .select(select)
      .execute();

所以,翻译成你的use-case:

// The WITH .. SELECT 
Select<Record4<.., .., .., ..>> select = ...

// The code from before
insertInto(USER_ORGANIZATION_ROLE,
    USER_ORGANIZATION_ROLE.ID_USER,
    USER_ORGANIZATION_ROLE.ID_ORGANIZATION,
    USER_ORGANIZATION_ROLE.ID_ROLE,
    USER_ORGANIZATION_ROLE.ID_WORKSPACE)

// Put your WITH .. SELECT in here
.select(select)

在这一点上,还值得注意的是 jOOQ 中的每个查询都是一个 dynamic SQL query,因此如果这有助于更好地构建和理解查询的各个部分,则没有什么可以阻止您将部分分配给局部变量。

与 .. SELECT

那部分还有一些问题,包括:

  • table(name("cte_0")).field("ID_ROLE") 这样不行。您以这种方式创建的 Table<?>ID_ROLE 字段一无所知,因此无法查找。 This is documented in the Javadoc。即

    The returned table does not know its field references, i.e. Fields.fields() returns an empty array.

    您必须改用 field(name("cte_0", "ID_ROLE"))

  • 应将数据类型添加到您的字段投影中,以便在执行查询时获得类型安全和正确的投影,即这样会更好:field(name("cte_0", "ID_ROLE"), ID_ROLE.getDataType())

可能还有其他问题

关于何时使用 jOOQ 的评论

jOOQ 非常适合复杂的查询,尤其是当它们是动态的时候。在某些情况下,即当涉及派生的 tables 或 CTE 时,jOOQ 仍然可以处理复杂性,但内部 DSL 方法使 SQL 语句有点难以读/写,主要是因为它不是可能引用尚未声明的 table。当您编写静态 SQL,即 non-dynamic SQL.

时,整个类型安全参数因派生 tables 或 CTE 而失败

因此,在 jOOQ 世界中,每当您使用派生的 table 或 CTE 时,请考虑:

  • 是否可以使用原生 SQL 来处理复杂的部分,例如通过 plain SQL templating 或视图等。(说真的,仅仅因为你正在使用 jOOQ 并不意味着你必须将它用于 每个查询
  • 您是否可以 re-write 您的查询更简单(无论是否使用 jOOQ 通常都更好)

将查询重写为更简单的内容

您的查询似乎与此相同?

insert into user_organization_role (
  id_user, id_organization, id_role, id_workspace
)
select distinct
  ur.id_user,
  ur0.id_organization,
  ur0.id_role,
  ur0.id_workspace
from 
  user_organization_role ur,
  user_orgainzation_role ur0
where ur.id_user <> 0
and ur0.id_user = 0
and (ur.id_user, ur0.id_organization, ur0.id_role, ur0.id_workspace) not in (
  select id_user, id_organization, id_role, id_workspace
  from user_organization_role 
  where id_user <> 0
)

我不认为你首先需要 CTE。这看起来完全等同。我不太确定 use-case。您似乎在对关系 table 进行操作,并自动为每个还没有此关系的用户添加关系到“默认”organization/role/workspace。那么,也许经典 INSERT IGNORE 会更好? IE。像这样?

insert ignore into user_organization_role (
  id_user, id_organization, id_role, id_workspace
)
select distinct
  ur.id_user,
  ur0.id_organization,
  ur0.id_role,
  ur0.id_workspace
from 
  user_organization_role ur,
  user_orgainzation_role ur0
where ur.id_user <> 0
and ur0.id_user = 0

这是假设 4 列形成一个 UNIQUE 约束条件

我尝试执行最后一个 SQL 查询,但遇到以下问题

'select(org.jooq.Select<? extends org.jooq.Record3<java.lang.Integer,java.lang.Integer,java.lang.Integer>>)' in 'org.jooq.InsertValuesStep3' cannot be applied to '(org.jooq.Select<org.jooq.Record4<java.lang.Integer,java.lang.Integer,java.lang.Integer,java.lang.Integer>>)'

这是我在阅读上面的全部评论后设法实现的代码

 UserOrganizationRole ur = Tables.USER_ORGANIZATION_ROLE;
 UserOrganizationRole ur0 = Tables.USER_ORGANIZATION_ROLE;
 Select<Record4<Integer, Integer, Integer, Integer>> select = DSL.selectDistinct(ur.ID_USER,ur0.ID_ORGANIZATION,ur0.ID_ROLE,ur0.ID_WORKSPACE)
                                                                     .from(ur,ur0).where(ur.ID_USER.notEqual(0).and(ur0.ID_USER.eq(0)));

 userDSLContext.insertInto(Tables.USER_ORGANIZATION_ROLE, Tables.USER_ORGANIZATION_ROLE.ID_ROLE, Tables.USER_ORGANIZATION_ROLE.ID_ORGANIZATION,
                Tables.USER_ORGANIZATION_ROLE.ID_WORKSPACE)
                .select(select).execute();

同时,我解决了这个问题。谢谢 Lukas Eder!

 UserOrganizationRole ur = Tables.USER_ORGANIZATION_ROLE.as("ur");
 UserOrganizationRole ur0 = Tables.USER_ORGANIZATION_ROLE.as("ur0");
 Select<Record4<Integer, Integer, Integer, Integer>> select = DSL.selectDistinct(ur.ID_USER, ur0.ID_ORGANIZATION, ur0.ID_ROLE, ur.ID_WORKSPACE)
                .from(ur, ur0).where(ur.ID_USER.notEqual(DEFAULT_ID_USER).and(ur0.ID_USER.eq(DEFAULT_ID_USER)));

 userDSLContext.insertInto(Tables.USER_ORGANIZATION_ROLE, Tables.USER_ORGANIZATION_ROLE.ID_USER, Tables.USER_ORGANIZATION_ROLE.ID_ORGANIZATION, Tables.USER_ORGANIZATION_ROLE.ID_ROLE,
                        Tables.USER_ORGANIZATION_ROLE.ID_WORKSPACE)
                .select(select)
                .onDuplicateKeyIgnore()
                .execute();