refactor(db): use SqlDialect in DiscreteMaterialPlanDAO
Replace all manual isSqlServer checks and inline SQL dialect logic with the SqlDialect abstraction. Removes buildPlaceholders(), TABLE_NAME_SQLSERVER, and TABLE_NAME_MYSQL in favor of dialect.params(), dialect.param(), and dialect.quoteTableName(). Batch size logic now uses dialect.maxBatchRows(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { create, type IDatabaseService } from './index'
|
import { create, type IDatabaseService } from './index'
|
||||||
|
import { createDialect, type SqlDialect } from './dialects'
|
||||||
import { createLogger, run, getRequestId, trackDuration } from '../logger'
|
import { createLogger, run, getRequestId, trackDuration } from '../logger'
|
||||||
|
|
||||||
const log = createLogger('DiscreteMaterialPlanDAO')
|
const log = createLogger('DiscreteMaterialPlanDAO')
|
||||||
@@ -53,8 +54,6 @@ export interface MaterialPlanRecord {
|
|||||||
* Configuration for DiscreteMaterialPlanData table
|
* Configuration for DiscreteMaterialPlanData table
|
||||||
*/
|
*/
|
||||||
export const DISCRETE_MATERIAL_PLAN_CONFIG = {
|
export const DISCRETE_MATERIAL_PLAN_CONFIG = {
|
||||||
TABLE_NAME_SQLSERVER: '[dbo].[DiscreteMaterialPlanData]',
|
|
||||||
TABLE_NAME_MYSQL: 'dbo_DiscreteMaterialPlanData',
|
|
||||||
COLUMNS: {
|
COLUMNS: {
|
||||||
ID: 'ID',
|
ID: 'ID',
|
||||||
FACTORY: 'Factory',
|
FACTORY: 'Factory',
|
||||||
@@ -94,15 +93,20 @@ export const DISCRETE_MATERIAL_PLAN_CONFIG = {
|
|||||||
*/
|
*/
|
||||||
export class DiscreteMaterialPlanDAO {
|
export class DiscreteMaterialPlanDAO {
|
||||||
private dbService: IDatabaseService | null = null
|
private dbService: IDatabaseService | null = null
|
||||||
|
private dialect: SqlDialect | null = null
|
||||||
|
|
||||||
|
private getDialect(): SqlDialect {
|
||||||
|
if (!this.dialect) {
|
||||||
|
this.dialect = createDialect(this.dbService!.type)
|
||||||
|
}
|
||||||
|
return this.dialect
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the appropriate table name based on database type
|
* Get the appropriate table name based on database type
|
||||||
*/
|
*/
|
||||||
private getTableName(): string {
|
private getTableName(): string {
|
||||||
const isSqlServer = this.dbService?.type === 'sqlserver'
|
return this.getDialect().quoteTableName('dbo', 'DiscreteMaterialPlanData')
|
||||||
return isSqlServer
|
|
||||||
? DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_SQLSERVER
|
|
||||||
: DISCRETE_MATERIAL_PLAN_CONFIG.TABLE_NAME_MYSQL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,15 +121,6 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
return this.dbService
|
return this.dbService
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build placeholders for IN clause based on database type
|
|
||||||
*/
|
|
||||||
private buildPlaceholders(count: number, isSqlServer: boolean): string {
|
|
||||||
return isSqlServer
|
|
||||||
? Array.from({ length: count }, (_, idx) => `@p${idx}`).join(',')
|
|
||||||
: Array.from({ length: count }, () => '?').join(',')
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== QUERY ALL ====================
|
// ==================== QUERY ALL ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -219,13 +214,13 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
try {
|
try {
|
||||||
const dbService = await this.getDatabaseService()
|
const dbService = await this.getDatabaseService()
|
||||||
const tableName = this.getTableName()
|
const tableName = this.getTableName()
|
||||||
const isSqlServer = dbService.type === 'sqlserver'
|
const dialect = this.getDialect()
|
||||||
const batchSize = 1500
|
const batchSize = 1500
|
||||||
const allResults: any[] = []
|
const allResults: any[] = []
|
||||||
|
|
||||||
for (let i = 0; i < sourceNumbers.length; i += batchSize) {
|
for (let i = 0; i < sourceNumbers.length; i += batchSize) {
|
||||||
const batch = sourceNumbers.slice(i, i + batchSize)
|
const batch = sourceNumbers.slice(i, i + batchSize)
|
||||||
const placeholders = this.buildPlaceholders(batch.length, isSqlServer)
|
const placeholders = dialect.params(batch.length)
|
||||||
|
|
||||||
const sqlString = `
|
const sqlString = `
|
||||||
SELECT *
|
SELECT *
|
||||||
@@ -273,13 +268,13 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
try {
|
try {
|
||||||
const dbService = await this.getDatabaseService()
|
const dbService = await this.getDatabaseService()
|
||||||
const tableName = this.getTableName()
|
const tableName = this.getTableName()
|
||||||
const isSqlServer = dbService.type === 'sqlserver'
|
const dialect = this.getDialect()
|
||||||
const batchSize = 1500
|
const batchSize = 1500
|
||||||
const allResults: any[] = []
|
const allResults: any[] = []
|
||||||
|
|
||||||
for (let i = 0; i < sourceNumbers.length; i += batchSize) {
|
for (let i = 0; i < sourceNumbers.length; i += batchSize) {
|
||||||
const batch = sourceNumbers.slice(i, i + batchSize)
|
const batch = sourceNumbers.slice(i, i + batchSize)
|
||||||
const placeholders = this.buildPlaceholders(batch.length, isSqlServer)
|
const placeholders = dialect.params(batch.length)
|
||||||
|
|
||||||
const sqlString = `
|
const sqlString = `
|
||||||
WITH RankedRecords AS (
|
WITH RankedRecords AS (
|
||||||
@@ -338,9 +333,9 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
try {
|
try {
|
||||||
const dbService = await this.getDatabaseService()
|
const dbService = await this.getDatabaseService()
|
||||||
const tableName = this.getTableName()
|
const tableName = this.getTableName()
|
||||||
const isSqlServer = dbService.type === 'sqlserver'
|
const dialect = this.getDialect()
|
||||||
|
|
||||||
const placeholder = isSqlServer ? '@p0' : '?'
|
const placeholder = dialect.param(0)
|
||||||
const sqlString = `
|
const sqlString = `
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM ${tableName}
|
FROM ${tableName}
|
||||||
@@ -377,9 +372,9 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
try {
|
try {
|
||||||
const dbService = await this.getDatabaseService()
|
const dbService = await this.getDatabaseService()
|
||||||
const tableName = this.getTableName()
|
const tableName = this.getTableName()
|
||||||
const isSqlServer = dbService.type === 'sqlserver'
|
const dialect = this.getDialect()
|
||||||
|
|
||||||
const placeholder = isSqlServer ? '@p0' : '?'
|
const placeholder = dialect.param(0)
|
||||||
const sqlString = `
|
const sqlString = `
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM ${tableName}
|
FROM ${tableName}
|
||||||
@@ -418,13 +413,13 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
try {
|
try {
|
||||||
const dbService = await this.getDatabaseService()
|
const dbService = await this.getDatabaseService()
|
||||||
const tableName = this.getTableName()
|
const tableName = this.getTableName()
|
||||||
const isSqlServer = dbService.type === 'sqlserver'
|
const dialect = this.getDialect()
|
||||||
const batchSize = 1500
|
const batchSize = 1500
|
||||||
const allResults: any[] = []
|
const allResults: any[] = []
|
||||||
|
|
||||||
for (let i = 0; i < planNumbers.length; i += batchSize) {
|
for (let i = 0; i < planNumbers.length; i += batchSize) {
|
||||||
const batch = planNumbers.slice(i, i + batchSize)
|
const batch = planNumbers.slice(i, i + batchSize)
|
||||||
const placeholders = this.buildPlaceholders(batch.length, isSqlServer)
|
const placeholders = dialect.params(batch.length)
|
||||||
|
|
||||||
const sqlString = `
|
const sqlString = `
|
||||||
SELECT *
|
SELECT *
|
||||||
@@ -476,7 +471,7 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
try {
|
try {
|
||||||
const dbService = await this.getDatabaseService()
|
const dbService = await this.getDatabaseService()
|
||||||
const tableName = this.getTableName()
|
const tableName = this.getTableName()
|
||||||
const isSqlServer = dbService.type === 'sqlserver'
|
const dialect = this.getDialect()
|
||||||
const batchSize = 2000
|
const batchSize = 2000
|
||||||
|
|
||||||
// Get unique source numbers
|
// Get unique source numbers
|
||||||
@@ -495,7 +490,7 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
for (let i = 0; i < uniqueSourceNumbers.length; i += batchSize) {
|
for (let i = 0; i < uniqueSourceNumbers.length; i += batchSize) {
|
||||||
const batch = uniqueSourceNumbers.slice(i, i + batchSize)
|
const batch = uniqueSourceNumbers.slice(i, i + batchSize)
|
||||||
const batchNumber = Math.floor(i / batchSize) + 1
|
const batchNumber = Math.floor(i / batchSize) + 1
|
||||||
const placeholders = this.buildPlaceholders(batch.length, isSqlServer)
|
const placeholders = dialect.params(batch.length)
|
||||||
|
|
||||||
const sqlString = `
|
const sqlString = `
|
||||||
DELETE FROM ${tableName}
|
DELETE FROM ${tableName}
|
||||||
@@ -565,23 +560,19 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
try {
|
try {
|
||||||
const dbService = await this.getDatabaseService()
|
const dbService = await this.getDatabaseService()
|
||||||
const tableName = this.getTableName()
|
const tableName = this.getTableName()
|
||||||
const isSqlServer = dbService.type === 'sqlserver'
|
const dialect = this.getDialect()
|
||||||
|
|
||||||
// SQL Server has a limit of 2100 parameters per query
|
// SQL Server has a limit of 2100 parameters per query
|
||||||
// Each record has 28 columns, so max rows per batch = 2100 / 28 = 75
|
// Each record has 28 columns, so max rows per batch = 2100 / 28 = 75
|
||||||
// Leave some margin for query overhead
|
// Leave some margin for query overhead
|
||||||
const columnsPerRow = 28
|
const columnsPerRow = 28
|
||||||
const sqlServerMaxParams = 2000
|
const effectiveBatchSize = Math.min(batchSize, dialect.maxBatchRows(columnsPerRow))
|
||||||
const effectiveBatchSize = isSqlServer
|
|
||||||
? Math.min(batchSize, Math.floor(sqlServerMaxParams / columnsPerRow))
|
|
||||||
: batchSize
|
|
||||||
const totalBatches = Math.ceil(records.length / effectiveBatchSize)
|
const totalBatches = Math.ceil(records.length / effectiveBatchSize)
|
||||||
|
|
||||||
log.info('Batch insert started', {
|
log.info('Batch insert started', {
|
||||||
tableName,
|
tableName,
|
||||||
operationType: 'INSERT',
|
operationType: 'INSERT',
|
||||||
requestId: batchId,
|
requestId: batchId,
|
||||||
isSqlServer,
|
|
||||||
dbType: dbService.type,
|
dbType: dbService.type,
|
||||||
columnsPerRow,
|
columnsPerRow,
|
||||||
effectiveBatchSize,
|
effectiveBatchSize,
|
||||||
@@ -598,7 +589,6 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
dbService,
|
dbService,
|
||||||
tableName,
|
tableName,
|
||||||
batch,
|
batch,
|
||||||
isSqlServer,
|
|
||||||
batchId,
|
batchId,
|
||||||
batchNumber,
|
batchNumber,
|
||||||
totalBatches
|
totalBatches
|
||||||
@@ -643,7 +633,6 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
dbService: IDatabaseService,
|
dbService: IDatabaseService,
|
||||||
tableName: string,
|
tableName: string,
|
||||||
records: MaterialPlanRecord[],
|
records: MaterialPlanRecord[],
|
||||||
isSqlServer: boolean,
|
|
||||||
batchId: string,
|
batchId: string,
|
||||||
batchNumber: number,
|
batchNumber: number,
|
||||||
totalBatches: number
|
totalBatches: number
|
||||||
@@ -689,7 +678,7 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
const rowPlaceholders: string[] = []
|
const rowPlaceholders: string[] = []
|
||||||
|
|
||||||
records.forEach((record, rowIndex) => {
|
records.forEach((record, rowIndex) => {
|
||||||
const rowValues = this.buildRowValues(record, columns, rowIndex, isSqlServer, values)
|
const rowValues = this.buildRowValues(record, columns, rowIndex, values)
|
||||||
rowPlaceholders.push(`(${rowValues.join(',')})`)
|
rowPlaceholders.push(`(${rowValues.join(',')})`)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -718,10 +707,9 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
private async insertBatch(
|
private async insertBatch(
|
||||||
dbService: IDatabaseService,
|
dbService: IDatabaseService,
|
||||||
tableName: string,
|
tableName: string,
|
||||||
records: MaterialPlanRecord[],
|
records: MaterialPlanRecord[]
|
||||||
isSqlServer: boolean
|
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
return this.insertBatchWithTracking(dbService, tableName, records, isSqlServer, 'unknown', 1, 1)
|
return this.insertBatchWithTracking(dbService, tableName, records, 'unknown', 1, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -731,18 +719,14 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
record: MaterialPlanRecord,
|
record: MaterialPlanRecord,
|
||||||
columns: string[],
|
columns: string[],
|
||||||
_rowIndex: number,
|
_rowIndex: number,
|
||||||
isSqlServer: boolean,
|
|
||||||
values: any[]
|
values: any[]
|
||||||
): string[] {
|
): string[] {
|
||||||
|
const dialect = this.getDialect()
|
||||||
return columns.map((col) => {
|
return columns.map((col) => {
|
||||||
const value = this.getColumnValue(record, col)
|
const value = this.getColumnValue(record, col)
|
||||||
values.push(value)
|
values.push(value)
|
||||||
|
|
||||||
if (isSqlServer) {
|
return dialect.param(values.length - 1)
|
||||||
return `@p${values.length - 1}`
|
|
||||||
} else {
|
|
||||||
return '?'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,9 +823,9 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
try {
|
try {
|
||||||
const dbService = await this.getDatabaseService()
|
const dbService = await this.getDatabaseService()
|
||||||
const tableName = this.getTableName()
|
const tableName = this.getTableName()
|
||||||
const isSqlServer = dbService.type === 'sqlserver'
|
const dialect = this.getDialect()
|
||||||
|
|
||||||
const placeholder = isSqlServer ? '@p0' : '?'
|
const placeholder = dialect.param(0)
|
||||||
const sqlString = `
|
const sqlString = `
|
||||||
SELECT COUNT(*) as count
|
SELECT COUNT(*) as count
|
||||||
FROM ${tableName}
|
FROM ${tableName}
|
||||||
@@ -876,7 +860,7 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
try {
|
try {
|
||||||
const dbService = await this.getDatabaseService()
|
const dbService = await this.getDatabaseService()
|
||||||
const tableName = this.getTableName()
|
const tableName = this.getTableName()
|
||||||
const isSqlServer = dbService.type === 'sqlserver'
|
const dialect = this.getDialect()
|
||||||
|
|
||||||
if (sourceNumbers && sourceNumbers.length > 0) {
|
if (sourceNumbers && sourceNumbers.length > 0) {
|
||||||
const batchSize = 1500
|
const batchSize = 1500
|
||||||
@@ -884,7 +868,7 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
|
|
||||||
for (let i = 0; i < sourceNumbers.length; i += batchSize) {
|
for (let i = 0; i < sourceNumbers.length; i += batchSize) {
|
||||||
const batch = sourceNumbers.slice(i, i + batchSize)
|
const batch = sourceNumbers.slice(i, i + batchSize)
|
||||||
const placeholders = this.buildPlaceholders(batch.length, isSqlServer)
|
const placeholders = dialect.params(batch.length)
|
||||||
|
|
||||||
const sqlString = `
|
const sqlString = `
|
||||||
SELECT DISTINCT MaterialName
|
SELECT DISTINCT MaterialName
|
||||||
@@ -975,6 +959,7 @@ export class DiscreteMaterialPlanDAO {
|
|||||||
if (this.dbService) {
|
if (this.dbService) {
|
||||||
await this.dbService.disconnect()
|
await this.dbService.disconnect()
|
||||||
this.dbService = null
|
this.dbService = null
|
||||||
|
this.dialect = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user