使用 Exposed 在 Kotlin 中使用 HAVING 子句编写查询
Write query with HAVING clause in Kotlin with Exposed
我编写了一个 SQL 查询,其中我使用了 HAVING 子句。
但是,我没有在展示的文档中找到使用 HAVING 的实际示例。
我的查询应该return如下:
- 对最近状态为 'CAPTURED' 或 'EXPIRED'
的订单提出异议
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 别名时,您应该引用(在 slice
、andWhere
、orderBy
和其他子句中)别名,不是原来的 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
}
非常感谢您的帮助。希望我能帮助到其他人。
我编写了一个 SQL 查询,其中我使用了 HAVING 子句。
但是,我没有在展示的文档中找到使用 HAVING 的实际示例。
我的查询应该return如下:
- 对最近状态为 'CAPTURED' 或 'EXPIRED' 的订单提出异议
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 别名时,您应该引用(在 slice
、andWhere
、orderBy
和其他子句中)别名,不是原来的 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
}
非常感谢您的帮助。希望我能帮助到其他人。