mirror of
https://github.com/dbgate/dbgate
synced 2024-11-22 07:48:06 +00:00
query designer - grop filter (having clause)
This commit is contained in:
parent
5a42e8c9ae
commit
351ac5e6a7
@ -36,6 +36,11 @@ export function dumpSqlSelect(dmp: SqlDumper, cmd: Select) {
|
||||
dmp.putCollection(', ', cmd.groupBy, (expr) => dumpSqlExpression(dmp, expr));
|
||||
dmp.put('&n');
|
||||
}
|
||||
if (cmd.having) {
|
||||
dmp.put('&n^having ');
|
||||
dumpSqlCondition(dmp, cmd.having);
|
||||
dmp.put('&n');
|
||||
}
|
||||
if (cmd.orderBy) {
|
||||
dmp.put('&n^order ^by ');
|
||||
dmp.putCollection(', ', cmd.orderBy, (expr) => {
|
||||
|
@ -16,6 +16,7 @@ export interface Select {
|
||||
orderBy?: OrderByExpression[];
|
||||
groupBy?: Expression[];
|
||||
where?: Condition;
|
||||
having?: Condition;
|
||||
}
|
||||
|
||||
export type UpdateField = Expression & { targetColumn: string };
|
||||
|
@ -1,5 +1,14 @@
|
||||
import _ from 'lodash';
|
||||
import { dumpSqlSelect, Select, JoinType, Condition, Relation, mergeConditions, Source } from 'dbgate-sqltree';
|
||||
import {
|
||||
dumpSqlSelect,
|
||||
Select,
|
||||
JoinType,
|
||||
Condition,
|
||||
Relation,
|
||||
mergeConditions,
|
||||
Source,
|
||||
ResultField,
|
||||
} from 'dbgate-sqltree';
|
||||
import { EngineDriver } from 'dbgate-types';
|
||||
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
|
||||
import { DesignerComponent } from './DesignerComponentCreator';
|
||||
@ -93,6 +102,59 @@ export class DesignerQueryDumper {
|
||||
}
|
||||
}
|
||||
|
||||
addGroupConditions(select: Select, tables: DesignerTableInfo[], selectIsGrouped: boolean) {
|
||||
for (const column of this.designer.columns || []) {
|
||||
if (!column.groupFilter) continue;
|
||||
const table = (this.designer.tables || []).find((x) => x.designerId == column.designerId);
|
||||
if (!table) continue;
|
||||
if (!tables.find((x) => x.designerId == table.designerId)) continue;
|
||||
|
||||
const condition = parseFilter(column.groupFilter, findDesignerFilterType(column, this.designer));
|
||||
if (condition) {
|
||||
select.having = mergeConditions(
|
||||
select.having,
|
||||
_.cloneDeepWith(condition, (expr) => {
|
||||
if (expr.exprType == 'placeholder') {
|
||||
return this.getColumnOutputExpression(column, selectIsGrouped);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getColumnOutputExpression(col, selectIsGrouped): ResultField {
|
||||
const source = findQuerySource(this.designer, col.designerId);
|
||||
const { columnName } = col;
|
||||
let { alias } = col;
|
||||
if (selectIsGrouped && !col.isGrouped) {
|
||||
// use aggregate
|
||||
const aggregate = col.aggregate == null || col.aggregate == '---' ? 'MAX' : col.aggregate;
|
||||
if (!alias) alias = `${aggregate}(${columnName})`;
|
||||
|
||||
return {
|
||||
exprType: 'call',
|
||||
func: aggregate == 'COUNT DISTINCT' ? 'COUNT' : aggregate,
|
||||
argsPrefix: aggregate == 'COUNT DISTINCT' ? 'DISTINCT' : null,
|
||||
alias,
|
||||
args: [
|
||||
{
|
||||
exprType: 'column',
|
||||
columnName,
|
||||
source,
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
exprType: 'column',
|
||||
columnName,
|
||||
alias,
|
||||
source,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
run() {
|
||||
let res: Select = null;
|
||||
for (const component of this.components) {
|
||||
@ -120,37 +182,7 @@ export class DesignerQueryDumper {
|
||||
if (outputColumns.length == 0) {
|
||||
res.selectAll = true;
|
||||
} else {
|
||||
res.columns = outputColumns.map((col) => {
|
||||
const source = findQuerySource(this.designer, col.designerId);
|
||||
const { columnName } = col;
|
||||
let { alias } = col;
|
||||
if (selectIsGrouped && !col.isGrouped) {
|
||||
// use aggregate
|
||||
const aggregate = col.aggregate == null || col.aggregate == '---' ? 'MAX' : col.aggregate;
|
||||
if (!alias) alias = `${aggregate}(${columnName})`;
|
||||
|
||||
return {
|
||||
exprType: 'call',
|
||||
func: aggregate == 'COUNT DISTINCT' ? 'COUNT' : aggregate,
|
||||
argsPrefix: aggregate == 'COUNT DISTINCT' ? 'DISTINCT' : null,
|
||||
alias,
|
||||
args: [
|
||||
{
|
||||
exprType: 'column',
|
||||
columnName,
|
||||
source,
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
exprType: 'column',
|
||||
columnName,
|
||||
alias,
|
||||
source,
|
||||
};
|
||||
}
|
||||
});
|
||||
res.columns = outputColumns.map((col) => this.getColumnOutputExpression(col, selectIsGrouped));
|
||||
}
|
||||
|
||||
const groupedColumns = topLevelColumns.filter((x) => x.isGrouped);
|
||||
@ -176,6 +208,7 @@ export class DesignerQueryDumper {
|
||||
}
|
||||
|
||||
this.addConditions(res, topLevelTables);
|
||||
this.addGroupConditions(res, topLevelTables, selectIsGrouped);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ export default function QueryDesignColumns({ value, onChange }) {
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const hasGroupedColumn = !!(columns || []).find((x) => x.isGrouped);
|
||||
|
||||
return (
|
||||
<TableControl rows={columns || []}>
|
||||
<TableColumn fieldName="columnName" header="Column/Expression" />
|
||||
@ -136,6 +138,21 @@ export default function QueryDesignColumns({ value, onChange }) {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{hasGroupedColumn && (
|
||||
<TableColumn
|
||||
fieldName="groupFilter"
|
||||
header="Group filter"
|
||||
formatter={(row) => (
|
||||
<DataFilterControl
|
||||
filterType={findDesignerFilterType(row, value)}
|
||||
filter={row.groupFilter}
|
||||
setFilter={(groupFilter) => {
|
||||
changeColumn({ ...row, groupFilter });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<TableColumn
|
||||
fieldName="actions"
|
||||
header=""
|
||||
|
@ -29,7 +29,8 @@ export type DesignerColumnInfo = {
|
||||
aggregate?: string;
|
||||
isOutput?: boolean;
|
||||
sortOrder?: number;
|
||||
filter: string;
|
||||
filter?: string;
|
||||
groupFilter?: string;
|
||||
};
|
||||
|
||||
export type DesignerInfo = {
|
||||
|
Loading…
Reference in New Issue
Block a user