ms-ai-architect/skills/ms-ai-engineering/references/data-engineering/etl-vs-elt-ai.md
Kjell Tore Guttormsen 3a73eeafdc refactor(ms-ai-architect): R13 del 1 — nøytraliser Cosmo-personaen i ref-korpusets headinger, etter å ha rettet en gate som var målt usann to ganger
Ordre 20260912T193441Z-7358817909. Steg 1 var ikke transformen, men å rette
roadmapens R13-gate og få den ratifisert. Gaten `grep -rl "Cosmo"
skills/*/references -> 0` var usann på to uavhengige måter:

1. Ordren fanget den første: 451 av forekomstene er Azure Cosmos DB, ekte
   produktinnhold. Diskriminatoren er ikke bokstaven «s» — `Cosmos <norsk
   substantiv>` er genitiv av personaen (`### Cosmos tonalitet`), mens
   `Cosmos DB`/`CosmosClient`/`cosmos_ru` er produkt.
2. Denne økten fant den andre: 132 persona-forekomster ligger i prosa,
   tabeller, dialog-replikker og proveniens-linjer. Heading-nøytralisering
   kan ikke nå dem, så «0 persona» er uoppnåelig også under den ratifiserte
   formen. Operatøren ratifiserte alternativ A: gaten speiler formen, og de
   132 bokføres til R13b/R14.

Tre korreksjoner av premisser som sto i ordren og STATE:
  «ca 320 produkt»   -> 451 (case-sensitivt nett manglet 327 lowercase
                        TOC-ankre + 99 identifikatorer; sann nevner 1 638)
  «169 headinger»    -> 401. 169 var `^## For Cosmo`-prefikset (168) og var
                        internt inkonsistent med sin egen topp-variant (204)
  «417 matcher ingen
   populasjon»       -> 417 er cosmo-headinger utenfor kodefences; briefens
                        nevner var reell hele tiden

Fence-bevissthet er målt skadelig, ikke nødvendig: begge toggle-regler er
gale på dette korpuset (naiv toggle skjuler en ekte heading i
chain-of-thought-prompting.md, CommonMark-regelen ubalanserer
service-level-documentation-dr.md). Fence-agnostisk deteksjon finner 401
heading-linjer i nøyaktig de samme 40 variantene som fence-bevisst finner
400 i — ingen kodeblokk-linje er byte-identisk til en persona-heading. Derfor
nøkles transformen på 40 enumererte heading-tekster og ignorerer fences. En
ukjent variant kaster; en slug-kollisjon kaster. Ingenting auto-fikses.

TOC-en regenereres ikke, den rettes kirurgisk: alle 327 persona-lenker hadde
lenketekst lik én av de 40 heading-tekstene og anker lik slugify av den
(327/327, 0 avvik), så heading og TOC-entry skrives i samme operasjon og
ingen mellomtilstand etterlater en død lenke.

Ratifisert målform: `For Cosmo`, `For Cosmo Skyberg` og `For arkitekten
(Cosmo)` konvergerer på `For arkitekten`. To filer kolliderte og er adjudisert
ved å lese dem, ikke ved regel.

Verifisering (alle 7 kriterier fra ordren):
  G1 persona på heading-linjer   401 -> 0
  G2 døde fragmentlenker         1 -> 1 (pre-eksisterende, unntatt)
  G3 produkt-forekomster         451 -> 451; `Cosmos DB|Azure Cosmos` 308 = 308
  de 3 kun-produkt-filene        byte-identiske
  nettet validert begge veier    injisert persona feller G1; genitiv feller G1;
                                 produkt-heading og de 3 filene passerer
  hele diffen                    802 heading-linjer + 654 TOC-linjer, ANNET = 0
  linjeantall                    728 lagt til = 728 slettet
  suite                          1120/1120 (1097 + 23 nye)
  validate-plugin                250 PASS / 0 FAIL
  stikkprøve                     10 filer, alle 5 skills, inkl. de 3 mest
                                 produkt-tunge (26/20/19) — kun heading+TOC

Utenfor scope, urørt: de 4 SKILL.md, de 23 commands, CLAUDE.md, README.md,
NOTICE.md, docs/ (alt R14).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 22:12:28 +02:00

16 KiB

ETL vs ELT Strategies for AI Workloads

Last updated: 2026-06-24 Status: GA Category: Data Engineering for AI Type: reference Source: https://learn.microsoft.com/fabric/data-factory/data-factory-overview


Innhold

Introduksjon

Valget mellom ETL (Extract, Transform, Load) og ELT (Extract, Load, Transform) er en av de mest grunnleggende arkitekturbeslutningene for dataintegrasjon i AI-prosjekter. Tradisjonell ETL transformerer data før lasting i et dedikert transformasjonsengine, mens moderne ELT laster rådata først og utnytter målsystemets beregningskraft for transformasjon. Microsoft Fabric støtter begge tilnærminger og hybride mønstre.

For norsk offentlig sektor er dette valget påvirket av flere faktorer: regulatoriske krav til dataminimering (GDPR artikkel 5), behov for sporbarhet, budsjettbegrensninger, og kompetanse i organisasjonen. ELT-tilnærmingen har blitt dominerende for AI-arbeidsbelastninger fordi den bevarer rådata for utforskende analyse og gir fleksibilitet til å endre transformasjonslogikk uten re-innhenting fra kildesystemer.

Denne referansen sammenligner ETL og ELT for AI-brukstilfeller, med fokus på Fabric-spesifikke implementasjoner, hybridmønstre, inkrementell prosessering, og kostnadsoptimalisering.


ELT Advantages: Cost, Scalability, Schema-Flexibility

Hvorfor ELT dominerer for AI

Fordel Beskrivelse AI-relevans
Bevarer rådata Original data tilgjengelig for nye analyser Nye features uten re-innhenting
Skalerer med beregning Fabric Spark/SQL skalerer elastisk Store treningsdatasett
Schema-fleksibilitet Schema-on-read i Bronze Nye datakilder uten re-design
Forenkling Ingen separat transformasjonsserver Lavere TCO
Parallellisering Transformasjoner på distribuert Spark Raskere feature engineering

ELT i Fabric

Kilde ──> Extract ──> Load (OneLake) ──> Transform (Spark/SQL)
                          │
                    ┌─────▼──────┐
                    │ Lakehouse  │
                    │ (Bronze)   │
                    │ Rådata i   │
                    │ Delta Lake │
                    └─────┬──────┘
                          │ Spark Notebook / SQL
                    ┌─────▼──────┐
                    │ Lakehouse  │
                    │ (Silver)   │
                    │ Validert   │
                    └─────┬──────┘
                          │ Spark Notebook / SQL
                    ┌─────▼──────┐
                    │ Lakehouse  │
                    │ (Gold)     │
                    │ ML-features│
                    └────────────┘

Fabric ELT Implementation

# Steg 1: Extract + Load (Copy Job / Pipeline)
# Data Factory laster rådata direkte til Bronze Lakehouse

# Steg 2: Transform i Lakehouse med Spark
# Bronze → Silver transformasjon
bronze_data = spark.read.format("delta").table("bronze.raw_transactions")

silver_data = (
    bronze_data
    # Fjern duplikater
    .dropDuplicates(["transaction_id"])
    # Typevalidering
    .withColumn("amount", col("amount").cast("double"))
    .filter(col("amount") > 0)
    # Standardiser datoformat
    .withColumn("transaction_date",
        F.to_timestamp("transaction_date", "yyyy-MM-dd'T'HH:mm:ss"))
    # Fjern null i obligatoriske felt
    .filter(col("customer_id").isNotNull())
)

silver_data.write.format("delta").mode("overwrite") \
    .saveAsTable("silver.validated_transactions")

# Steg 3: Silver → Gold (ML-features)
gold_features = (
    spark.read.format("delta").table("silver.validated_transactions")
    .groupBy("customer_id")
    .agg(
        F.count("*").alias("total_transactions"),
        F.sum("amount").alias("total_amount"),
        F.avg("amount").alias("avg_amount"),
        F.stddev("amount").alias("std_amount"),
        F.max("transaction_date").alias("last_transaction_date")
    )
)

gold_features.write.format("delta").mode("overwrite") \
    .saveAsTable("gold.customer_features")

ETL Data Minimization for Regulated Environments

Når ETL er riktig valg

ETL er foretrukket i regulerte miljøer der dataminimering er påkrevd:

Scenario Begrunnelse Regulering
PII-filtrering Fjern personnummer før lasting GDPR Art. 5(1)(c)
Dataminimering Last kun nødvendige felter GDPR Art. 5(1)(c)
Kryptering Krypter sensitive felt i transit Sikkerhetskrav
Konsolidering Slå sammen kilder før lasting Kostnadsbegrensning
Format-konvertering Konverter proprietære formater Interoperabilitet

ETL i Fabric med Dataflow Gen2

Kilde ──> Dataflow Gen2 (Transform) ──> Lakehouse (Silver/Gold)
              │
              ├── Fjern PII-kolonner
              ├── Masker fødselsnummer
              ├── Aggreger til anonymt nivå
              ├── Validerer datatyper
              └── Berik med referansedata

Dataflow Gen2 for ETL

Dataflow Gen2 bruker Power Query Online med over 300 transformasjoner:

// M-kode (Power Query) for ETL med dataminimering
let
    Source = Sql.Database("server.database.windows.net", "hrdb"),
    employees = Source{[Schema="dbo", Item="Employees"]}[Data],

    // Fjern sensitive kolonner (dataminimering)
    removedPII = Table.RemoveColumns(employees,
        {"SocialSecurityNumber", "BankAccount", "HomeAddress"}),

    // Masker e-post
    maskedEmail = Table.TransformColumns(removedPII,
        {{"Email", each Text.BeforeDelimiter(_, "@") & "@***.no"}}),

    // Aggreger alder til aldersgrupper
    addAgeGroup = Table.AddColumn(maskedEmail, "AgeGroup",
        each if [Age] < 30 then "Under 30"
        else if [Age] < 50 then "30-49"
        else "50+"),

    // Fjern eksakt alder (kun aldersgruppe beholdes)
    removedAge = Table.RemoveColumns(addAgeGroup, {"Age"}),

    // Filtrer kun aktive ansatte
    filtered = Table.SelectRows(removedAge, each [Status] = "Active")
in
    filtered

Hybrid ETL/ELT Patterns

Pattern: Pre-filter ETL + In-place ELT

                    ETL (Dataflow Gen2)              ELT (Spark)
                    ┌──────────────────┐            ┌──────────────────┐
Kildesystem ───────>│ Fjern PII        │──> Bronze ─│ Feature engineer │──> Silver
                    │ Masker sensitive  │            │ Aggreger         │
                    │ Validerer format  │            │ Join             │
                    └──────────────────┘            │ Dedupliser       │
                                                    └──────────────────┘

Pattern: Metadata-driven Hybrid Pipeline

# Metadata-drevet pipeline som velger ETL eller ELT per datakilde
pipeline_config = {
    "sources": [
        {
            "name": "crm_customers",
            "strategy": "ETL",  # Inneholder PII
            "reason": "PII-filtrering påkrevd",
            "transformations": ["remove_ssn", "mask_email", "age_to_group"],
            "tool": "dataflow_gen2"
        },
        {
            "name": "traffic_events",
            "strategy": "ELT",  # Ingen sensitive data
            "reason": "Stort volum, ingen PII",
            "transformations": ["aggregate_5min", "add_road_segment"],
            "tool": "spark_notebook"
        },
        {
            "name": "health_records",
            "strategy": "ETL",  # Helseopplysninger (særkategori)
            "reason": "GDPR Art. 9 - særlig kategori",
            "transformations": [
                "pseudonymize_patient_id",
                "remove_diagnosis_text",
                "aggregate_to_cohort"
            ],
            "tool": "dataflow_gen2"
        }
    ]
}

Fabric Pipeline Orchestration

{
    "name": "HybridETL_ELT_Pipeline",
    "properties": {
        "activities": [
            {
                "name": "ETL_SensitiveData",
                "type": "DataflowGen2",
                "description": "ETL for PII-data via Dataflow Gen2",
                "typeProperties": {
                    "dataflowName": "pii_cleansing_flow"
                }
            },
            {
                "name": "ELT_BulkLoad",
                "type": "Copy",
                "description": "ELT: Last rådata direkte til Bronze",
                "dependsOn": [],
                "typeProperties": {
                    "source": { "type": "BlobSource" },
                    "sink": { "type": "LakehouseSink" }
                }
            },
            {
                "name": "Transform_Silver",
                "type": "Notebook",
                "description": "ELT: Transform i Spark",
                "dependsOn": [
                    { "activity": "ETL_SensitiveData" },
                    { "activity": "ELT_BulkLoad" }
                ],
                "typeProperties": {
                    "notebookPath": "silver_transformations"
                }
            }
        ]
    }
}

Data Staging and Incremental Processing

Inkrementelle lastingsmønstre

Mønster Beskrivelse Bruk i Fabric
Full load Last alt hver gang Copy Job (full load)
Incremental append Last kun nye rader Copy Job (append) + watermark
CDC (Change Data Capture) Strøm av endringer Copy Job (CDC), Mirroring
Watermark Last rader etter siste timestamp Pipeline med parameter
Delta load Merge nye/endrede rader Copy Job (upsert)

Watermark-basert inkrementell lasting

# Hent siste watermark (høyeste lastede timestamp)
last_watermark = spark.sql("""
    SELECT MAX(loaded_timestamp) as watermark
    FROM bronze.raw_events
""").collect()[0]["watermark"]

# Last kun nye data
new_data = (
    spark.read
    .format("jdbc")
    .option("url", jdbc_url)
    .option("dbtable", f"""
        (SELECT * FROM events
         WHERE modified_date > '{last_watermark}') AS new_events
    """)
    .load()
)

# Append til Bronze
new_data.withColumn("loaded_timestamp", F.current_timestamp()) \
    .write.format("delta").mode("append") \
    .saveAsTable("bronze.raw_events")

print(f"Lastet {new_data.count()} nye rader etter {last_watermark}")

Staging Layer Pattern

Kildesystem ──> Staging Area ──> Bronze ──> Silver ──> Gold
                    │
                    ├── Midlertidig lagring
                    ├── Validering før insert
                    ├── Duplikatsjekk
                    └── Slettet etter vellykket last
# Staging → Bronze med validering
staging_data = spark.read.format("delta").table("staging.incoming_data")

# Valider
valid_records = staging_data.filter(
    col("customer_id").isNotNull() &
    col("amount").isNotNull() &
    (col("amount") > 0)
)

rejected_records = staging_data.subtract(valid_records)

# Last gyldige poster til Bronze
valid_records.write.format("delta").mode("append").saveAsTable("bronze.validated_events")

# Logg avviste poster
rejected_records.write.format("delta").mode("append").saveAsTable("governance.rejected_records")

# Rydd opp staging
spark.sql("TRUNCATE TABLE staging.incoming_data")

Compute Cost Allocation: ETL vs ELT

Kostnadsmodell i Fabric

Komponent Fabric CU-meter Kostnadsdriver
Copy Job / Activity Data Movement Datamengde (GB)
Dataflow Gen2 Standard Compute / High Scale Compute Kompleksitet, rader
Spark Notebook Spark Compute vCores x tid
Pipeline Orchestration Data Orchestration Antall aktiviteter
OneLake Storage OneLake Storage Lagret data (GB/mnd)

ETL vs ELT kostnadsprofil

Aspekt ETL (Dataflow Gen2) ELT (Spark)
Oppsettskostnad Lav (no-code) Medium (kode)
Kjøretidskostnad per rad Høyere (transformasjon + last) Lavere (kun last, transform i batch)
Skalerbarhet Begrenset (single-node-lik) Høy (distribuert Spark)
Lagringskostnad Lavere (kun transformert data) Høyere (rådata + transformert)
Vedlikeholdskostnad Lav (visuell editor) Medium (kodevedlikehold)
Optimal for < 10 GB, enkel transformasjon > 10 GB, kompleks transformasjon

Kostnadsoptimalisering

# 1. Bruk Copy Job i stedet for Copy Activity for bulk-lasting
# Copy Job: Automatisk CDC, inkrementell lasting, lavere kostnad

# 2. Bruk Optimized Write for å redusere små filer
spark.conf.set("spark.microsoft.delta.optimizeWrite.enabled", "true")

# 3. Bruk Spark autoscale for å matche beregning med behov
# Konfigureres i Workspace Settings > Spark Compute

# 4. Planlegg tunge transformasjoner utenfor peak-timer
# Pipeline schedule: "0 2 * * *" (kl 02:00)

# 5. Bruk materialized lake views for repetitive transformasjoner
# Unngår gjentatt beregning av samme transformasjoner

Beslutningsmatrise

Faktor Velg ETL Velg ELT Velg Hybrid
Datamengde < 10 GB > 10 GB Variabel
PII i kildedata Ja (GDPR) Nei Noen kilder med PII
Schema-stabilitet Stabilt Variabel Blandet
Team-kompetanse Power Query/Excel PySpark/SQL Blandet
Transformasjonskompleksitet Enkel (filter, rename) Kompleks (vindus, ML) Blandet
Latenskrav Minutter Sekunder-minutter Variabel
Budget Begrenset Fleksibelt Variabel

Referanser


For arkitekten

  • Bruk denne referansen når brukeren evaluerer dataintegrerings-strategier for AI, vurderer kostnader og ytelse, eller trenger å håndtere sensitive data i pipelines.
  • For norsk offentlig sektor: Anbefal hybrid ETL/ELT som standard -- ETL for kilder med personopplysninger (Dataflow Gen2 med PII-filtrering), ELT for alle andre kilder (Copy Job + Spark).
  • ELT er generelt best for AI fordi det bevarer rådata i Bronze, noe som muliggjør eksperimentering med nye features uten å måtte re-hente data fra kildesystemer.
  • Dataflow Gen2 er undervurdert for ETL i offentlig sektor -- det er et Power Query-basert verktøy som mange forvaltere allerede kjenner fra Excel/Power BI, og det håndterer dataminimering visuelt.
  • Ved kostnadsestimering: Husk at ELT har høyere lagringskostnad (bevarer rådata) men lavere beregningskostnad for gjentatte transformasjoner. For AI-prosjekter veier fleksibilitetsgevinsten vanligvis opp for ekstra lagring.