Skip to content
Advertisement

Write query with HAVING clause in Kotlin with Exposed

I have written an SQL query in which I use the HAVING clause.

However, I have not found practical examples of the use of HAVING in the Exhibited documentation.

My query should return the following:

  • Disputes for an order that has the most recent status of ‘CAPTURED’ or ‘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')

My biggest difficulty is writing the HAVING sub query. I saw that there is a function called wrapAsExpression, but I can’t use it in this scenario. My query so far looks like this:

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])
}​​​​​​​​

I don’t know what else to do to make it work.

Advertisement

Answer

sorry for the delay! You are the man! Saved my development!

I made some adaptations to your suggestion.

My solution was as follows:

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
    }

And the final code for the query:

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
}

Thanks a lot for the help . Hope I can help other people.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement