跪拜 Guibai
← All articles
Database

A Complex BI Query Ran 10x Faster After Moving from SQL Server to KingbaseES

By 一只牛博 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Database migration projects routinely check that tables and objects transfer correctly, but BI teams care about whether month-end reports still return fast under load. This test provides a concrete, repeatable acceptance pattern—semantic verification first, then query rewriting, then concurrency testing—that catches the real failure mode: correct results that arrive too slowly when dozens of users hit the same report.

Summary

A database migration from SQL Server to KingbaseES V9R4C019 was stress-tested with a production BI query joining orders, items, customers, regions, products, and refunds. The query retained a correlated scalar subquery in its first compatible version to verify semantic equivalence, then was rewritten to pre-aggregate refunds by order item, eliminating repeated table access. Under 100 concurrent connections, TPS rose 60% and average response time dropped to roughly one-tenth of the original.

The acceptance process separated three concerns: syntax compatibility, data consistency, and performance. Result sets were compared using bidirectional EXCEPT queries and fixed-precision numeric comparisons to ensure the business logic remained identical. Indexes were designed around the query's actual filter and join paths—order status and pay time for the orders table, order_id for the order-to-item join, and order_item_id for the refund join—and statistics were refreshed before testing.

Execution plans were analyzed with EXPLAIN (ANALYZE, BUFFERS) rather than compared node-by-node. The key change was replacing the correlated subquery with a CTE that aggregates refunds once, letting the optimizer choose a more efficient join path. The project retained original SQL, migrated SQL, result differences, execution plans, and concurrency metrics as verifiable evidence for future re-validation.

Takeaways
Under 100 concurrent users, the complex BI query's TPS improved 60% and average response time dropped to about 1/10 of the SQL Server baseline.
Semantic equivalence was verified first using a minimally adapted SQL version with COALESCE replacing ISNULL and TIMESTAMP literals for date boundaries.
Bidirectional EXCEPT queries and fixed-precision numeric comparisons confirmed identical row counts, amounts, and groupings before any performance comparison.
The correlated scalar subquery was rewritten as a CTE that pre-aggregates refunds by order_item_id, reducing repeated access to the refund table.
Indexes targeted the query's actual filter and join paths: a composite index on (order_status, pay_time, customer_id, order_id) for the orders table, and indexes on (order_id, product_id, order_item_id) and (refund_status, order_item_id) for items and refunds.
EXPLAIN (ANALYZE, BUFFERS) was used to inspect actual row counts and cache behavior rather than comparing plan node names across databases.
Five artifacts were retained for future re-validation: original SQL, migrated SQL, result differences, execution plans, and concurrency metrics.
Conclusions

Most migration acceptance stops at row counts and object lists; performance under concurrency is the part that gets hand-waved as 'tested' and causes the first production incident.

Keeping the correlated subquery in the first migrated version is a disciplined move—it decouples semantic verification from optimization, so you never mistake a missing join for a speed improvement.

Pre-aggregating the refund data before joining eliminated a per-row subquery execution, which is the kind of rewrite that matters far more than syntax-level tuning when concurrency climbs.

Normalizing performance results to a baseline of 1.00 avoids fabricating absolute numbers when hardware specs aren't disclosed, but it also makes the 10x improvement claim impossible to independently reproduce without the original environment.

Concepts & terms
Correlated scalar subquery
A subquery in a SELECT list that references a column from the outer query (here, order_item_id). The database must execute it once per outer row, making it sensitive to row counts and join order.
Bidirectional EXCEPT verification
Running A EXCEPT B and B EXCEPT A to confirm two result sets are identical. If both return zero rows, the sets match exactly; a one-direction check can miss rows present only in the second set.
Pre-aggregation CTE
A Common Table Expression that groups and sums data before joining it to the main query. It replaces repeated per-row lookups with a single pass over the table, giving the optimizer a clearer join boundary.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗