优化了in/not in的SQL语句

This commit is contained in:
Xu Chang 2025-07-17 13:59:13 +08:00
parent c789457f96
commit 09715010e4
2 changed files with 114 additions and 79 deletions

View File

@ -617,43 +617,58 @@ class SqlTranslator {
}); });
} }
const fk = foreignKey || 'entityId'; const fk = foreignKey || 'entityId';
const joinFilter = { // 对in和not in优化不要用exists查询
$expr12: { if (['in', 'not in'].includes(predicate)) {
$eq: [ const { stmt, currentNumber: ct2 } = this.translateSelectInner(subEntity, {
{ data: {
'#attr': fk, [fk]: 1,
}, },
{ filter: filter2[attr]
'#refId': refAlia2, }, currentNumber, filterRefAlias, option);
'#refAttr': 'id', currentNumber = ct2;
} whereText += `(${refAlia2}.id ${predicate} (${stmt}))`;
], }
} else {
}; /**
if (!foreignKey) { * 只剩下all/not all
Object.assign(joinFilter, { * 翻译成键值不等条件下的not exist/exist
entity: entity2, */
}); const joinFilter = {
$expr83: {
[['all', 'not all'].includes(predicate) ? '$ne' : '$eq']: [
{
'#attr': fk,
},
{
'#refId': refAlia2,
'#refAttr': 'id',
}
],
}
};
if (!foreignKey) {
Object.assign(joinFilter, {
entity: entity2,
});
}
const { stmt, currentNumber: ct2 } = this.translateSelectInner(subEntity, {
data: {
id: 1,
},
filter: (0, filter_1.combineFilters)(subEntity, this.schema, [joinFilter, filter2[attr]]),
indexFrom: 0,
count: 1,
}, currentNumber, filterRefAlias, option);
currentNumber = ct2;
const PREDICATE_DICT = {
// in 和 not in已经不会命中了优化到上面的路径中
'in': 'exists',
'not in': 'not exists',
'all': 'not exists',
'not all': 'exists',
};
whereText += ` ${PREDICATE_DICT[predicate]} (${stmt})`;
} }
const conditionalPredicate = ['all', 'not all'].includes(predicate) ? {
$not: filter2[attr],
} : filter2[attr];
const { stmt, currentNumber: ct2 } = this.translateSelectInner(subEntity, {
data: {
id: 1,
},
filter: (0, filter_1.combineFilters)(subEntity, this.schema, [joinFilter, conditionalPredicate]),
indexFrom: 0,
count: 1,
}, currentNumber, filterRefAlias, option);
currentNumber = ct2;
const PREDICATE_DICT = {
'in': 'exists',
'not in': 'not exists',
'all': 'not exists',
'not all': 'exists',
};
whereText += ` ${PREDICATE_DICT[predicate]} (${stmt})`;
} }
else { else {
(0, assert_1.default)(attributes.hasOwnProperty(attr), `非法的属性${attr}`); (0, assert_1.default)(attributes.hasOwnProperty(attr), `非法的属性${attr}`);

View File

@ -1,9 +1,11 @@
import assert from 'assert'; import assert from 'assert';
import SqlString from 'sqlstring'; import SqlString from 'sqlstring';
import { assign, cloneDeep, difference, identity, intersection, keys, set } from 'lodash'; import { assign, cloneDeep, difference, identity, intersection, keys, set } from 'lodash';
import { Attribute, EntityDict, EXPRESSION_PREFIX, Index, OperateOption, import {
Attribute, EntityDict, EXPRESSION_PREFIX, Index, OperateOption,
Q_FullTextValue, Ref, RefOrExpression, SelectOption, StorageSchema, SubQueryPredicateMetadata, Q_FullTextValue, Ref, RefOrExpression, SelectOption, StorageSchema, SubQueryPredicateMetadata,
TriggerDataAttribute, CreateAtAttribute, UpdateAtAttribute, DeleteAtAttribute, SeqAttribute, TriggerUuidAttribute } from "oak-domain/lib/types"; TriggerDataAttribute, CreateAtAttribute, UpdateAtAttribute, DeleteAtAttribute, SeqAttribute, TriggerUuidAttribute
} from "oak-domain/lib/types";
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { DataType } from "oak-domain/lib/types/schema/DataTypes"; import { DataType } from "oak-domain/lib/types/schema/DataTypes";
import { judgeRelation } from 'oak-domain/lib/store/relation'; import { judgeRelation } from 'oak-domain/lib/store/relation';
@ -788,44 +790,62 @@ export abstract class SqlTranslator<ED extends EntityDict & BaseEntityDict> {
}); });
} }
const fk = foreignKey || 'entityId'; const fk = foreignKey || 'entityId';
const joinFilter = {
$expr12: {
$eq: [
{
'#attr': fk,
},
{
'#refId': refAlia2,
'#refAttr': 'id',
}
],
}
};
if (!foreignKey) {
Object.assign(joinFilter, {
entity: entity2,
});
}
const conditionalPredicate = ['all', 'not all'].includes(predicate) ? {
$not: filter2[attr],
} : filter2[attr];
const { stmt, currentNumber: ct2 } = this.translateSelectInner(subEntity, {
data: {
id: 1,
},
filter: combineFilters(subEntity, this.schema, [joinFilter, conditionalPredicate]),
indexFrom: 0,
count: 1,
}, currentNumber, filterRefAlias, option);
currentNumber = ct2;
const PREDICATE_DICT = { // 对in和not in优化不要用exists查询
'in': 'exists', if (['in', 'not in'].includes(predicate)) {
'not in': 'not exists', const { stmt, currentNumber: ct2 } = this.translateSelectInner(subEntity, {
'all': 'not exists', data: {
'not all': 'exists', [fk]: 1,
}; },
whereText += ` ${PREDICATE_DICT[predicate]} (${stmt})`; filter: filter2[attr]
}, currentNumber, filterRefAlias, option);
currentNumber = ct2;
whereText += `(${refAlia2}.id ${predicate} (${stmt}))`;
}
else {
/**
* all/not all
* not exist/exist
*/
const joinFilter = {
$expr83: {
[['all', 'not all'].includes(predicate) ? '$ne' : '$eq']: [
{
'#attr': fk,
},
{
'#refId': refAlia2,
'#refAttr': 'id',
}
],
}
};
if (!foreignKey) {
Object.assign(joinFilter, {
entity: entity2,
});
}
const { stmt, currentNumber: ct2 } = this.translateSelectInner(subEntity, {
data: {
id: 1,
},
filter: combineFilters(subEntity, this.schema, [joinFilter, filter2[attr]]),
indexFrom: 0,
count: 1,
}, currentNumber, filterRefAlias, option);
currentNumber = ct2;
const PREDICATE_DICT = {
// in 和 not in已经不会命中了优化到上面的路径中
'in': 'exists',
'not in': 'not exists',
'all': 'not exists',
'not all': 'exists',
};
whereText += ` ${PREDICATE_DICT[predicate]} (${stmt})`;
}
} }
else { else {
assert(attributes.hasOwnProperty(attr), `非法的属性${attr}`); assert(attributes.hasOwnProperty(attr), `非法的属性${attr}`);
@ -1076,7 +1096,7 @@ export abstract class SqlTranslator<ED extends EntityDict & BaseEntityDict> {
} }
groupByText = as; groupByText = as;
} }
else if (k.startsWith('#')){ else if (k.startsWith('#')) {
let { projText: projSubText } = this.translateProjection(entity, (data as any)[k]!, aliasDict, projectionRefAlias, undefined, true); let { projText: projSubText } = this.translateProjection(entity, (data as any)[k]!, aliasDict, projectionRefAlias, undefined, true);
let projSubText2 = ''; let projSubText2 = '';
if (k.startsWith('#max')) { if (k.startsWith('#max')) {