在 Kotlin Exposed 框架中处理子查询
Handling subquery in a Kotlin Exposed framework
Exposed 0.27.1 是否能够翻译以下 SQL 语句?
SELECT FirstName, LastName,
(SELECT COUNT(O.Id)
FROM "Order" O
WHERE O.CustomerId = C.Id) AS OrderCount
FROM Customer C;
这是我尝试过的,但不幸的是子查询独立于查询的其余部分工作。
val query = Customer
.leftJoin(Order, { Customer.id }, { Order.customerId })
.slice(
Customer.firstName,
Customer.lastName,
intLiteral(Order
.innerJoin(Customer, { Order.customerId }, { Customer.id })
.slice(Order.id.count())
.select { Order.customerId eq Customer.id }
.first()[Order.id.count()].toInt())//.alias("OrderCount")
)
.selectAll()
此外,如果这可行,那么我如何使用别名从 ResultRow 获取结果?在 this example 之后,解决方案似乎是将整个子查询与 alias()
方法调用存储在单个变量中,但这看起来很难看。有更好的方法吗?
官方F.A.Q。指出拥有子查询的唯一方法是 via alias for inner query:
val orderCount = Order.customerId.count().alias("OrderCount")
val subQuery = Order.slice(Order.customerId, orderCount)
.selectAll()
.groupBy(Order.customerId)
.alias("subQuery")
val query = Join(Customer)
.join(subQuery, JoinType.LEFT, subQuery[Order.customerId], Customer.id)
.slice(Customer.firstName, Customer.lastName, subQuery[orderCount])
.selectAll()
但也许您在这里不需要子查询?这个生成有点不同 SQL-query 几乎相同的查询结果(与前一个相比,如果没有客户订单,它将 return 0 而不是 null):
val query = Customer
.leftJoin(Order, { Customer.id }, { Order.customerId })
.slice(Customer.firstName, Customer.lastName, Order.id.count().alias("OrderCount"))
.selectAll()
.groupBy(Customer.id)
生成SQL:
SELECT CUSTOMER."firstName",
CUSTOMER."lastName",
COUNT("ORDER".ID) OrderCount
FROM CUSTOMER
LEFT JOIN "ORDER" ON CUSTOMER.ID = "ORDER"."customerId"
GROUP BY CUSTOMER.ID
谢谢,虽然第一个解决方案很难理解,但希望能对所应用的逻辑进行一些文本解释。
我设法从中提取数据:
assertEquals("Manuel", query.andWhere { subQuery[orderCount] eq intLiteral(2) }.first()[Customer.firstName])
但我无法从第二种解决方案中提取数据。这是我所做的:
val orderCount = Order.id.count().alias("OrderCount")
val query = Customer
.leftJoin(Order, { Customer.id }, { Order.customerId })
.slice(Customer.firstName, Customer.lastName, orderCount)
.selectAll()
.groupBy(Customer.id)
assertEquals("Manuel", query.andWhere { orderCount eq intLiteral(2) }.first()[Customer.firstName])
上一个答案不在 select 子句中生成子查询。下面描述的方式允许它。这是在 Exposed 0.36.2.
上测试的
SQL
下一个例子执行 SQL:
SELECT "groups".id, "groups".name,
(SELECT COUNT(group_members.user_id) FROM group_members
WHERE group_members.group_id = "groups".id) members_count
FROM "groups";
如何在Exposed中获取
首先,我们需要一个将 AliasQuery 转换为 Expression 的包装器:
class SubQueryExpression<T>(private val aliasQuery : QueryAlias) : Expression<T>() {
override fun toQueryBuilder(queryBuilder: QueryBuilder) {
aliasQuery.describe(TransactionManager.current(), queryBuilder)
}
}
创建子查询:
val membersCount = GroupMembersTable.userId.count()
val subSelect = GroupMembersTable
.slice(membersCount)
.select { GroupMembersTable.groupId eq GroupsTable.id}
val subQuery: QueryAlias = subSelect.alias("members_count")
将子查询包装到表达式:
val membersCountExp = SubQueryExpression<Long>(subQuery)
进行完整查询:
val q = GroupsTable
.slice(GroupsTable.fields + membersCountExp)
.selectAll()
执行并读取计算值:
q.forEach {
println(it[membersCountExp])
}
Exposed 0.27.1 是否能够翻译以下 SQL 语句?
SELECT FirstName, LastName,
(SELECT COUNT(O.Id)
FROM "Order" O
WHERE O.CustomerId = C.Id) AS OrderCount
FROM Customer C;
这是我尝试过的,但不幸的是子查询独立于查询的其余部分工作。
val query = Customer
.leftJoin(Order, { Customer.id }, { Order.customerId })
.slice(
Customer.firstName,
Customer.lastName,
intLiteral(Order
.innerJoin(Customer, { Order.customerId }, { Customer.id })
.slice(Order.id.count())
.select { Order.customerId eq Customer.id }
.first()[Order.id.count()].toInt())//.alias("OrderCount")
)
.selectAll()
此外,如果这可行,那么我如何使用别名从 ResultRow 获取结果?在 this example 之后,解决方案似乎是将整个子查询与 alias()
方法调用存储在单个变量中,但这看起来很难看。有更好的方法吗?
官方F.A.Q。指出拥有子查询的唯一方法是 via alias for inner query:
val orderCount = Order.customerId.count().alias("OrderCount")
val subQuery = Order.slice(Order.customerId, orderCount)
.selectAll()
.groupBy(Order.customerId)
.alias("subQuery")
val query = Join(Customer)
.join(subQuery, JoinType.LEFT, subQuery[Order.customerId], Customer.id)
.slice(Customer.firstName, Customer.lastName, subQuery[orderCount])
.selectAll()
但也许您在这里不需要子查询?这个生成有点不同 SQL-query 几乎相同的查询结果(与前一个相比,如果没有客户订单,它将 return 0 而不是 null):
val query = Customer
.leftJoin(Order, { Customer.id }, { Order.customerId })
.slice(Customer.firstName, Customer.lastName, Order.id.count().alias("OrderCount"))
.selectAll()
.groupBy(Customer.id)
生成SQL:
SELECT CUSTOMER."firstName",
CUSTOMER."lastName",
COUNT("ORDER".ID) OrderCount
FROM CUSTOMER
LEFT JOIN "ORDER" ON CUSTOMER.ID = "ORDER"."customerId"
GROUP BY CUSTOMER.ID
谢谢,虽然第一个解决方案很难理解,但希望能对所应用的逻辑进行一些文本解释。
我设法从中提取数据:
assertEquals("Manuel", query.andWhere { subQuery[orderCount] eq intLiteral(2) }.first()[Customer.firstName])
但我无法从第二种解决方案中提取数据。这是我所做的:
val orderCount = Order.id.count().alias("OrderCount")
val query = Customer
.leftJoin(Order, { Customer.id }, { Order.customerId })
.slice(Customer.firstName, Customer.lastName, orderCount)
.selectAll()
.groupBy(Customer.id)
assertEquals("Manuel", query.andWhere { orderCount eq intLiteral(2) }.first()[Customer.firstName])
上一个答案不在 select 子句中生成子查询。下面描述的方式允许它。这是在 Exposed 0.36.2.
上测试的SQL
下一个例子执行 SQL:
SELECT "groups".id, "groups".name,
(SELECT COUNT(group_members.user_id) FROM group_members
WHERE group_members.group_id = "groups".id) members_count
FROM "groups";
如何在Exposed中获取
首先,我们需要一个将 AliasQuery 转换为 Expression 的包装器:
class SubQueryExpression<T>(private val aliasQuery : QueryAlias) : Expression<T>() {
override fun toQueryBuilder(queryBuilder: QueryBuilder) {
aliasQuery.describe(TransactionManager.current(), queryBuilder)
}
}
创建子查询:
val membersCount = GroupMembersTable.userId.count()
val subSelect = GroupMembersTable
.slice(membersCount)
.select { GroupMembersTable.groupId eq GroupsTable.id}
val subQuery: QueryAlias = subSelect.alias("members_count")
将子查询包装到表达式:
val membersCountExp = SubQueryExpression<Long>(subQuery)
进行完整查询:
val q = GroupsTable
.slice(GroupsTable.fields + membersCountExp)
.selectAll()
执行并读取计算值:
q.forEach {
println(it[membersCountExp])
}