优化了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 joinFilter = {
$expr12: {
$eq: [
{
'#attr': fk,
},
{
'#refId': refAlia2,
'#refAttr': 'id',
}
],
}
};
if (!foreignKey) {
Object.assign(joinFilter, {
entity: entity2,
});
// 对in和not in优化不要用exists查询
if (['in', 'not in'].includes(predicate)) {
const { stmt, currentNumber: ct2 } = this.translateSelectInner(subEntity, {
data: {
[fk]: 1,
},
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: (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 {
(0, assert_1.default)(attributes.hasOwnProperty(attr), `非法的属性${attr}`);

View File

@ -1,9 +1,11 @@
import assert from 'assert';
import SqlString from 'sqlstring';
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,
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 { DataType } from "oak-domain/lib/types/schema/DataTypes";
import { judgeRelation } from 'oak-domain/lib/store/relation';
@ -80,7 +82,7 @@ export abstract class SqlTranslator<ED extends EntityDict & BaseEntityDict> {
attributes: [{
name: TriggerUuidAttribute,
}]
},
},
];
// 增加外键等相关属性上的索引
@ -788,44 +790,62 @@ export abstract class SqlTranslator<ED extends EntityDict & BaseEntityDict> {
});
}
const fk = foreignKey || 'entityId';
const joinFilter = {
$expr12: {
$eq: [
{
'#attr': fk,
},
{
'#refId': refAlia2,
'#refAttr': 'id',
}
],
}
};
if (!foreignKey) {
Object.assign(joinFilter, {
entity: entity2,
});
// 对in和not in优化不要用exists查询
if (['in', 'not in'].includes(predicate)) {
const { stmt, currentNumber: ct2 } = this.translateSelectInner(subEntity, {
data: {
[fk]: 1,
},
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})`;
}
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': 'exists',
'not in': 'not exists',
'all': 'not exists',
'not all': 'exists',
};
whereText += ` ${PREDICATE_DICT[predicate]} (${stmt})`;
}
else {
assert(attributes.hasOwnProperty(attr), `非法的属性${attr}`);
@ -841,7 +861,7 @@ export abstract class SqlTranslator<ED extends EntityDict & BaseEntityDict> {
const predicate = Object.keys(filter2[attr])[0];
assert(predicate.startsWith('$'));
// 对属性上的谓词处理
whereText += ` (\`${alias}\`.\`${attr}\` ${this.translatePredicate(predicate, filter2[attr][predicate], type2)})`;
whereText += ` (\`${alias}\`.\`${attr}\` ${this.translatePredicate(predicate, filter2[attr][predicate], type2)})`;
}
}
else {
@ -859,7 +879,7 @@ export abstract class SqlTranslator<ED extends EntityDict & BaseEntityDict> {
};
const where = translateInner(entity, './', filter);
return {
stmt: where,
currentNumber,
@ -1076,7 +1096,7 @@ export abstract class SqlTranslator<ED extends EntityDict & BaseEntityDict> {
}
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 projSubText2 = '';
if (k.startsWith('#max')) {