munin/internal/store/migrations/002_projekt_in_nebentabellen.sql
2026-09-15 20:36:26 +02:00

47 lines
2.7 KiB
SQL

-- Die Projektzugehörigkeit auch in den Nebentabellen führen.
--
-- ── Warum? ───────────────────────────────────────────────────────────────────
--
-- Bisher stand project_id nur an "entries". Wer die Schlagworte eines Projekts
-- zählen wollte, musste deshalb über entries verbinden:
--
-- SELECT t.tag, COUNT(*) FROM entry_tags t
-- JOIN entries e ON e.id = t.entry_id
-- WHERE e.project_id = ? GROUP BY t.tag
--
-- SQLite läuft dafür durch ALLE Einträge des Projekts und schlägt zu jedem die
-- Schlagworte nach — der Aufwand wächst also mit der Zahl der Einträge, obwohl
-- am Ende nur ein paar Dutzend Schlagworte herauskommen. Gemessen bei 5.000
-- Einträgen: 6,7 ms. Seit die Schlagworte in der Seitenleiste stehen, läuft
-- diese Abfrage bei JEDEM Seitenaufruf.
--
-- Dasselbe bei den Rückverweisen: Dort kippte zusätzlich die Reihenfolge der
-- Verknüpfung, sobald ein ORDER BY dazukam — statt vom kleinen Verweis-Index
-- auszugehen, lief SQLite wieder durch alle Einträge (3,1 ms statt 9 µs).
--
-- ── Der Handel ───────────────────────────────────────────────────────────────
--
-- Eine Spalte doppelt zu führen ist eine Redundanz, und Redundanz kann
-- auseinanderlaufen. Hier ist das vertretbar: Beide Tabellen werden
-- ausschließlich in replaceTags/replaceLinks geschrieben, innerhalb derselben
-- Transaktion wie der Eintrag selbst. Es gibt keinen zweiten Schreibweg.
-- ALTER TABLE ... ADD COLUMN können SQLite und PostgreSQL gleichermaßen.
-- NOT NULL braucht dabei einen Vorgabewert, sonst wüssten bestehende Zeilen
-- nicht, was hineingehört.
ALTER TABLE entry_tags ADD COLUMN project_id TEXT NOT NULL DEFAULT '';
UPDATE entry_tags SET project_id = (SELECT e.project_id FROM entries e WHERE e.id = entry_tags.entry_id);
ALTER TABLE entry_links ADD COLUMN project_id TEXT NOT NULL DEFAULT '';
UPDATE entry_links SET project_id = (SELECT e.project_id FROM entries e WHERE e.id = entry_links.from_entry_id);
-- Die alten Indizes werden nicht mehr gebraucht: Jede Abfrage kennt jetzt das
-- Projekt und kann den zusammengesetzten Index nutzen.
DROP INDEX idx_entry_tags_tag;
DROP INDEX idx_entry_links_to;
-- project_id zuerst, dann das eigentliche Suchmerkmal: So deckt der Index
-- sowohl "alle Schlagworte eines Projekts" als auch "ein bestimmtes Schlagwort
-- in einem Projekt" ab.
CREATE INDEX idx_entry_tags_projekt ON entry_tags (project_id, tag);
CREATE INDEX idx_entry_links_projekt ON entry_links (project_id, to_slug);