我有一个查询从表的组合返回各种数字。我正在使用jooq来运行此查询。
final SiteSalesFigures siteSalesFigures =
dsl.select(
countDistinct(LINE.TRANSACTION_ID).as("transactionCount"),
sum(LINE.PROFIT).as("totalProfit"),
sum(LINE.TOTAL).as("totalSalesAmount"),
sum(LINE.QUANTITY).as("totalItemsSold"),
sum(LINE.PROFIT).divide(sum(LINE.TOTAL)).multiply(100).as("profitMarginPercentage"),
avg(TRANSACTIONS.NO_OF_ITEMS).as("averageItemsPerTransaction"),
sum(LINE.TOTAL).divide(countDistinct(LINE.TRANSACTION_ID)).as("averageSalesTotalPerTransaction"),
sum(LINE.PROFIT).divide(countDistinct(LINE.TRANSACTION_ID)).as("averageProfitTotalPerTransaction"))
.from(TRANSACTIONS)
.join(LINE).on(TRANSACTIONS.TRANSACTION_ID.equal(LINE.TRANSACTION_ID))
.leftJoin(ITEM).on(LINE.ITEM_ID.equal(ITEM.ITEM_CODE))
.where(TRANSACTIONS.SITE_ID.equal(siteId))
.and(TRANSACTIONS.NO_OF_LINES.greaterThan(0))
.and(TRANSACTIONS.START_TIME
.between(new Timestamp(reportStartDate.toInstant().toEpochMilli()))
.and(new Timestamp(reportEndDate.toInstant().toEpochMilli())))
.and(TRANSACTIONS.TRANSACTION_TYPE_ID.notEqual(cancelledSaleID))
.fetchOneInto(SiteSalesFigures.class);
averageItemsPerTransaction被证明是个问题。我完全理解为什么它不起作用,但我不确定如何才能使它工作。不幸的是,由于使用Line表的排除,因此必须进行连接。
如果事务有3行,则事务详细信息(包括no_of_items)将被复制三次,这会导致值不正确。
我知道正确的值,因为我只在事务表上运行了平均(no_of_items)查询。
以下是仅两个事务的表格(隐藏此示例不需要的列):
**transaction_id** **no_of_lines no_of_items**
8abf1720-51f6-a1bf-4714-004b644cb99f --- 2 --- 2
8abf1720-51f6-a1bf-4714-004b644cb99f --- 2 --- 2
d239feab-38ea-7c8a-4814-7d5a38f74949 --- 3 --- 4
d239feab-38ea-7c8a-4814-7d5a38f74949 --- 3 --- 4
d239feab-38ea-7c8a-4814-7d5a38f74949 --- 3 --- 4
您会注意到行数并不总是等于项目数(例如,一行可以扫描两个项目)
有人有解决方案吗?
一个明显的解决方案是运行两个查询来获得这些结果。第一个查询将是您已经拥有的(但没有平均值),第二个查询只计算平均值:
final SiteSalesFigures siteSalesFigures =
dsl.select(
avg(TRANSACTIONS.NO_OF_ITEMS).as("averageSalesTotalPerTransaction"),
avg(TRANSACTIONS.PRICE).as("averageSalesTotalPerTransaction"),
avg(TRANSACTIONS.PROFIT).as("averageProfitTotalPerTransaction"))
.from(TRANSACTIONS)
.where(TRANSACTIONS.SITE_ID.equal(siteId))
.and(TRANSACTIONS.NO_OF_LINES.greaterThan(0))
.and(TRANSACTIONS.START_TIME
.between(new Timestamp(reportStartDate.toInstant().toEpochMilli()))
.and(new Timestamp(reportEndDate.toInstant().toEpochMilli())))
.and(TRANSACTIONS.TRANSACTION_TYPE_ID.notEqual(cancelledSaleID))
.fetchOneInto(SiteSalesFigures.class);
这可能比一次性完成所有速度要慢,具体取决于TRANSACTIONS
表的大小。
因为您的连接会生成重复的TRANSACTIONS
行,所以您必须计算加权平均值,而不是普通平均值。举个例子,如果你的TRANSACTIONS
行被重复3次,那么你必须将该特定事务的贡献除以3.这通常会非常复杂,但考虑到你已经通过预先计算每个事务的NO_OF_ITEMS
来对你的模式进行非规范化你很幸运如果您没有此列,则必须在派生表中预先计算它。
在SQL / jOOQ中:
final SiteSalesFigures siteSalesFigures =
dsl.select(
...
count()
.divide(countDistinct(TRANSACTIONS.TRANSACTION_ID)).as("averageSalesTotalPerTransaction"),
sum(TRANSACTIONS.PRICE.divide(TRANSACTIONS.NO_OF_ITEMS))
.divide(countDistinct(TRANSACTIONS.TRANSACTION_ID)).as("averageSalesTotalPerTransaction"),
sum(TRANSACTIONS.PROFIT.divide(TRANSACTIONS.NO_OF_ITEMS))
.divide(countDistinct(TRANSACTIONS.TRANSACTION_ID)).as("averageProfitTotalPerTransaction"))
.from(TRANSACTIONS)
.join(...)
...
.fetchOneInto(SiteSalesFigures.class);
根据您的数据类型,您可能需要转换为DOUBLE
或NUMBER
。
I've blogged about calculating weighted averages in SQL more in detail here。
解决方案一直在我面前,我可以使用正在工作的值来获取我需要的值:
final SiteSalesFigures siteSalesFigures =
dsl.select(
countDistinct(LINE.TRANSACTION_ID).as("transactionCount"),
sum(LINE.PROFIT).as("totalProfit"),
sum(LINE.TOTAL).as("totalSalesAmount"),
sum(LINE.QUANTITY).as("totalItemsSold"),
sum(LINE.PROFIT).divide(sum(LINE.TOTAL)).multiply(100).as("profitMarginPercentage"),
sum(LINE.QUANTITY).divide(countDistinct(LINE.TRANSACTION_ID)).as("averageItemsPerTransaction"),
sum(LINE.TOTAL).divide(countDistinct(LINE.TRANSACTION_ID)).as("averageSalesTotalPerTransaction"),
sum(LINE.PROFIT).divide(countDistinct(LINE.TRANSACTION_ID)).as("averageProfitTotalPerTransaction"))
.from(TRANSACTIONS)
.join(LINE).on(TRANSACTIONS.TRANSACTION_ID.equal(LINE.TRANSACTION_ID))
.leftJoin(ITEM).on(LINE.ITEM_ID.equal(ITEM.ITEM_CODE))
.where(TRANSACTIONS.SITE_ID.equal(siteId))
.and(TRANSACTIONS.NO_OF_LINES.greaterThan(0))
.and(TRANSACTIONS.START_TIME
.between(new Timestamp(reportStartDate.toInstant().toEpochMilli()))
.and(new Timestamp(reportEndDate.toInstant().toEpochMilli())))
.and(TRANSACTIONS.TRANSACTION_TYPE_ID.notEqual(cancelledSaleID))
.fetchOneInto(SiteSalesFigures.class);