我想实现可以连接到任何sql server并从中加载任何表的java应用程序。对于每个表,我想基于一些任意列创建直方图。
例如,如果我有这张表
name profit
------------
name1 12
name2 14
name3 18
name4 13
我可以根据利润列的最小值和最大值创建bin大小为4的直方图,并计算每个bin的记录数。
结果是:
profit count
---------------
12-16 3
16-20 1
我遇到此问题的解决方案是根据所需的列检索所有数据,之后使用java stream Collectors.groupingBy
按记录构建bin和group。
我不确定我的解决方案是否已经过优化,为此我需要一些帮助才能找到更好的算法,特别是当我有大量记录时。(例如使用sql server或其他框架的一些好处,可以使用。)
我可以针对此问题使用更好的算法吗?
编辑1: 假设我的sql结果在列表数据
中private String mySimpleHash(Object[] row, int index) {
StringBuilder hash = new StringBuilder();
for (int i = 0; i < row.length; i++)
if (i != index)
hash.append(row[i]).append(":");
return hash.toString();
}
//index is index of column for histogram
List<Object[]> histogramData = new ArrayList<>();
final Map<String, List<Object[]>> map = data.stream().collect(
Collectors.groupingBy(row -> mySimpleHash(Arrays.copyOfRange(row, index))));
for (final Map.Entry<String, List<Object[]>> entry : map.entrySet()) {
Object[] newRow = newData.get(rowNum);
double result = entry.getValue().stream()
.mapToDouble(row ->
Double.valueOf(row[index].toString())).count();
newRow[index] = result;
histogramData.add(newRow);
}
答案 0 :(得分:1)
正如您所考虑的那样,如果表中的行数增加,则在从SQL服务器获取所有数据后执行聚合将非常昂贵。您可以在SQL中简单地进行聚合。
根据您表达柱状图箱的方式,这可能是微不足道的,也可能需要一些工作。在您的情况下,最低bin以min值开始的要求需要一些设置,而不是从0开始的binning。请参阅下面的示例。内部查询是将值映射到bin编号,外部查询是聚合和计算bin边界。
CREATE TABLE Test (
Name varchar(max) NOT NULL,
Profit int NOT NULL
)
INSERT Test(Name, Profit)
VALUES
('name1', 12),
('name2', 14),
('name3', 18),
('name4', 13)
DECLARE @minValue int = (SELECT MIN(Profit) FROM Test)
DECLARE @binSize int = 4
SELECT
(@minValue + @binSize * Bin) AS BinLow,
(@minValue + @binSize * Bin) + @binSize - 1 AS BinHigh,
COUNT(*) AS Count
FROM (
SELECT
((Profit - @minValue) / @binSize) AS Bin
FROM
Test
) AS t
GROUP BY Bin
| BinLow | BinHigh | Count |
|--------|---------|-------|
| 12 | 15 | 3 |
| 16 | 19 | 1 |