SYSTEM NOTICE

Auto translation by AI. Be sure, accuracy, nuances and authorial intent may not be fully reflected.
見出し画像

The story of how migrating Tableau to Django led to a complete rebuild due to design flaws


Design is 90% of the work

Introduction

I once rebuilt a dashboard that was originally created in Tableau as a web system using Django.

Right before the release, this is what happened:

  • It took over 10 seconds to display after clicking a button

  • The screen froze every time a filter was changed

  • The numbers were slightly different every time the same screen was opened

"It works, sort of. But we can't put this into production, can we?"

I spent a week worrying about whether I could just fix it as is.

I tried optimizing queries, adding indexes, and implementing caching.

But it didn't improve.

That's when I finally realized:

This isn't an implementation issue, it's a design issue

→ I decided to rebuild everything from scratch.


Why did I migrate?

・Tableau's response was slow
・Complex UI control was not possible
・It was difficult to integrate into business workflows

"If we make it a web app, it will be solved."

Thinking that, I rebuilt it in Django.


The first mistake I made

Replicating Tableau screens exactly as they were


What happened

I ended up with something that "worked but was unusable."

SELECT
    s.store_id,
    SUM(s.amount),
    COUNT(oi.order_id)
FROM sales s
JOIN order_items oi ON s.order_id = oi.order_id
JOIN customers c ON s.customer_id = c.id
WHERE s.sales_date BETWEEN :from AND :to
GROUP BY s.store_id;

I was executing this every time a screen action was performed.


Why it was unusable

・Changing filters → 10-second wait every time
・Concurrent access → Timeouts
・SQL different for every screen → Fixes spread horizontally
・Numbers misaligned → Unable to trace the cause

"It displays, but it's not usable for business."


Why it failed

I didn't do any data design


What I did to redo it

I threw away the screens and rebuilt it starting from the data.


1. Data mart design

・Determine granularity (1 store × 1 day)
・Eliminate the need for JOINs
・Create pre-aggregated tables

CREATE TABLE sales_daily (
    store_id INT,
    sales_date DATE,
    total_amount DECIMAL,
    order_count INT,
    PRIMARY KEY (store_id, sales_date)
);

2. Pre-aggregation via ETL

INSERT INTO sales_daily
SELECT
    s.store_id,
    s.sales_date,
    SUM(s.amount),
    COUNT(*)
FROM sales s
GROUP BY s.store_id, s.sales_date;

3. Simplification on the Django side

SELECT *
FROM sales_daily
WHERE sales_date BETWEEN :from AND :to;

4. Separation of roles

ETL: Calculation
DB: Storage
Django: Retrieval
Screen: Display


Results

・Response: 10 seconds → 0.5 seconds
・SQL: Complex → Simple
・Fixes: Completed in one place


The biggest lesson learned

BI is determined by data design, not appearance


Summary

This is what went wrong this time.

What I tried to replicate


The correct order is as follows.

1. Data design
2. Data flow
3. Backend
4. UI


Conclusion

If you get the design wrong, you will break in the same place even if you change tools.


Related articles

・6 common Tableau accidents
・6 accidents caused by poor data mart design
・6 practical SQL techniques


For those who are also moving BI to the web, please start by thinking about data design first.

If you found this helpful, a like would be encouraging.

いいなと思ったら応援しよう!