使用 Exposed 在 Kotlin 中使用 HAVING 子句编写查询

Write query with HAVING clause in Kotlin with Exposed

我编写了一个 SQL 查询,其中我使用了 HAVING 子句。

但是,我没有在展示的文档中找到使用 HAVING 的实际示例。

我的查询应该return如下:

SELECT pc.*
FROM "pedido" p
INNER JOIN pedido_contestacao pc ON t.id = pc.pedido_id
WHERE p.number = '1234'
GROUP BY pc.id
HAVING (
   SELECT status
   FROM contestacao_event ce
   WHERE ce.pedido_contestacao_id = pc.id
   ORDER BY ce.created_at DESC
   limit 1
   ) IN ('CAPTURED', 'EXPIRED')

我最大的困难是编写HAVING子查询。我看到有一个函数叫 wrapAsExpression,但是我不能在这种情况下使用它。到目前为止,我的查询如下所示:

val contestacaoEventTable = ContestacaoEventTable.alias("det")
val pedidoContestacaoTable = PedidoContestacaoTable.alias("tdt")

val subQuery = contestacaoEventTable
    .slice(contestacaoEventTable[ContestacaoEventTable.status])
    .selectAll()
    .andWhere {​​​​​​​​
        ContestacaoEventTable.id eq pedidoContestacaoTable[PedidoContestacaoTable.id]
    }​​​​​​​​.orderBy(ContestacaoEventTable.createdAt to SortOrder.DESC)
    .limit(1)
    .alias("statusQuery")

val status = contestacaoEventTable[ContestacaoEventTable.status]
PedidoTable
    .innerJoin(PedidoContestacaoTable)
    .slice(PedidoContestacaoTable.columns)
    .selectAll()
    .groupBy(PedidoContestacaoTable.id)
    .andWhere {​​​​​​​​ PedidoTable.number eq '1234' }​​​​​​​​
.having {​​​​​​​​
        // wrapAsExpression(subQuery.slice(status).selectAll()).inList(listOf("CAPTURED", "EXPIRED"))
}​​​​​​​​.map {​​​​​​​​
    println(it[PedidoContestacaoTable.id])
}​​​​​​​​

我不知道还需要做些什么才能让它发挥作用。

inList 方法只为 ExpressionWithColumnType class 定义,没有为任何 Expression 定义,所以你需要编写辅助函数将子查询包装到 ExpressionWithColumnType:

fun <T : Any> wrapAsExpressionWithColumnType(query: AbstractQuery<*>, columnType: IColumnType) =
    object : ExpressionWithColumnType<T?>() {
        private val expression = wrapAsExpression<T>(query)

        override fun toQueryBuilder(queryBuilder: QueryBuilder) = expression.toQueryBuilder(queryBuilder)
        override val columnType: IColumnType = columnType
    }

因此您的 having 子条款将是:

.having {
    wrapAsExpressionWithColumnType<String>(subQuery.query, status.columnType) inList listOf("CAPTURED", "EXPIRED")
}

另请注意,当您使用 table 别名时,您应该引用(在 sliceandWhereorderBy 和其他子句中)别名,不是原来的 table.

因此,如果您在子查询中使用别名,则主查询也应使用别名(反之亦然)

抱歉耽误了!你是男人!拯救了我的发展!

我根据你的建议做了一些修改。

我的解决方案如下:

fun <T : Any> wrapAsExpressionWithColumnType(query: Query, columnType: IColumnType) =
    object : ExpressionWithColumnType<T?>() {
        private val expression = wrapAsExpression<T>(query)
        override fun toSQL(queryBuilder: QueryBuilder) = expression.toSQL(queryBuilder)
        override val columnType: IColumnType = columnType
    }

以及查询的最终代码:

transaction {
    val contestacaoEventTable = ContestacaoEventTable.alias("det")
    val pedidoContestacaoTable = PedidoContestacaoTable.alias("tdt")

    val contestacaoEventTransacaoId = contestacaoEventTable[ContestacaoEventTable.contestacaoTransacaoId]
    val contestacaoEventCreatedAt = contestacaoEventTable[ContestacaoEventTable.createdAt]
    val contestacaoEventStatus = contestacaoEventTable[ContestacaoEventTable.status]

    val pedidoContestacaoId = pedidoContestacaoTable[PedidoContestacaoTable.id]
    val pedidoContestacaoTransacaoId = pedidoContestacaoTable[PedidoContestacaoTable.transacaoId]

    val subQuery = contestacaoEventTable
        .slice(contestacaoEventTable[ContestacaoEventTable.status])
        .selectAll()
        .andWhere { contestacaoEventTransacaoId eq pedidoContestacaoId }
        .orderBy(contestacaoEventCreatedAt to SortOrder.DESC)
        .limit(1)
        .alias("subQuery")
    PedidoTable
        .innerJoin(pedidoContestacaoTable, { PedidoTable.id }, { pedidoContestacaoTransacaoId })
        .slice(pedidoContestacaoId)
        .selectAll()
        .groupBy(pedidoContestacaoId)
        .andWhere { PedidoTable.number eq '1234' }
        .having {
            wrapAsExpressionWithColumnType<String>(subQuery.query, contestacaoEventStatus.columnType)
                .inList(listOf(PedidoStatus.CAPTURED.name, PedidoStatus.EXPIRED.name))
        }.toList().size
}

非常感谢您的帮助。希望我能帮助到其他人。