# Master Data Management for AI **Last updated:** 2026-02 **Status:** GA **Category:** Data Engineering for AI --- ## Introduksjon Master Data Management (MDM) skaper en enkelt kilde til sannhet for kritiske forretningsenheter som kunder, produkter, ansatte og lokasjoner. For AI-losninger er kvaliteten pa stamdata direkte avgjorede -- en ML-modell trent pa inkonsistente kundedata vil produsere upaalitelige prediksjoner, og en RAG-losning med dupliserte dokumenter vil gi motstridende svar. Microsoft tilbyr MDM gjennom flere tjenester: Microsoft Purview for data governance og MDM-integrasjoner, Dataverse som operativt masterdatasystem for Dynamics 365 og Power Platform, og Azure-tjenester som Data Factory for datakvalitet og deduplisering. For AI-pipelines i Fabric er det kritisk a sikre at referansedata og enhetsmapping er konsistent pa tvers av domener. For norsk offentlig sektor er MDM spesielt viktig: Folkeregisteret, Enhetsregisteret og Matrikkelen er eksempler pa nasjonale masterdatakilder. Integrasjon med disse via Digdir sine felleslosninger, kombinert med intern MDM i Dataverse eller Fabric, sikrer at AI-losninger opererer pa palitelig grunn. --- ## Golden Record Creation and Reconciliation ### Hva er en Golden Record? En golden record er den autoritative, konsoliderte versjonen av en enhet som kombinerer data fra flere kilder: ``` Kilde A: {navn: "Ola Nordmann", epost: "ola@firma.no", tlf: null} Kilde B: {navn: "O. Nordmann", epost: null, tlf: "+47 90000000"} Kilde C: {navn: "Ola Nordmann", epost: "ola.nordmann@firma.no", tlf: "+47 90000000"} Golden Record: { navn: "Ola Nordmann", -- Mest komplett, hoyest tillit epost: "ola.nordmann@firma.no", -- Fra kilde C (nyeste, mest komplett) tlf: "+47 90000000", -- Fra kilde B/C (konsistent) kilder: ["A", "B", "C"], tillit_score: 0.95 } ``` ### Survivorship-regler | Regel | Beskrivelse | Eksempel | |-------|-------------|---------| | **Most Recent** | Nyeste verdi vinner | Sist oppdatert adresse | | **Most Complete** | Mest utfylt felt vinner | Lengste navn-streng | | **Most Trusted Source** | Autoritativ kilde vinner | Folkeregisteret > CRM | | **Frequency** | Hyppigste verdi vinner | 3 av 4 kilder sier "Oslo" | | **Manual Override** | Manuell beslutning | Datakurator velger | ### Implementering i Fabric ```python from pyspark.sql import functions as F from pyspark.sql.window import Window def create_golden_records(sources: dict, entity_key: str, rules: dict): """ Opprett golden records fra flere kilder med survivorship-regler. Args: sources: Dict med {kildenavn: DataFrame} entity_key: Nokkelkolonne for matching rules: Dict med {kolonne: survivorship_regel} """ # 1. Kombiner alle kilder med kildemerking combined = None for source_name, df in sources.items(): tagged = df.withColumn("_source", F.lit(source_name)) \ .withColumn("_load_time", F.current_timestamp()) combined = combined.unionByName(tagged, allowMissingColumns=True) \ if combined else tagged # 2. Dedupliser og velg vinnere per kolonne golden = combined.groupBy(entity_key) agg_exprs = [] for col_name, rule in rules.items(): if rule == "most_recent": agg_exprs.append( F.last(col_name, ignorenulls=True).alias(col_name) ) elif rule == "most_complete": agg_exprs.append( F.max(F.when(F.col(col_name).isNotNull(), F.col(col_name))) .alias(col_name) ) elif rule == "most_trusted": # Sorter etter kildeprioritering agg_exprs.append( F.first(col_name, ignorenulls=True).alias(col_name) ) golden_df = golden.agg(*agg_exprs) return golden_df # Bruk sources = { "crm": df_crm, "erp": df_erp, "folkeregisteret": df_folkereg } rules = { "full_name": "most_trusted", # Folkeregisteret prioritert "address": "most_recent", # Siste oppdatering "phone": "most_complete", # Mest utfylt "organization_number": "most_trusted" # Enhetsregisteret } golden_customers = create_golden_records(sources, "person_id", rules) ``` --- ## Entity Resolution and Deduplication ### Duplikatdeteksjon i Dataverse Microsoft Dataverse har innebygd duplikatdeteksjon for aktive poster som kontoer og kontakter: | Funksjon | Beskrivelse | |----------|-------------| | **Duplikatdeteksjonsregler** | Definer matchingkriterier per entitet | | **Sanntidssjekk** | Sjekker ved opprettelse/oppdatering | | **Bulk-deteksjon** | Kjor deteksjon pa eksisterende data | | **Merge** | Kombiner duplikater med valg av primaer-post | ### Fuzzy Matching i Fabric For mer avansert entity resolution i AI-pipelines: ```python from pyspark.sql import functions as F from pyspark.ml.feature import NGram, HashingTF, MinHashLSH def fuzzy_match_entities(df, match_columns, threshold=0.7): """ Fuzzy matching med MinHash LSH for skalerbar deduplisering. """ # 1. Kombiner matchkolonner til en tekststreng df = df.withColumn( "match_text", F.lower(F.concat_ws(" ", *match_columns)) ) # 2. Tokeniser til n-grams df = df.withColumn( "tokens", F.split(F.col("match_text"), " ") ) # 3. Hashing for dimensjonsreduksjon hashingTF = HashingTF(inputCol="tokens", outputCol="features", numFeatures=1024) df_hashed = hashingTF.transform(df) # 4. MinHash LSH for approximate nearest neighbors mh = MinHashLSH(inputCol="features", outputCol="hashes", numHashTables=5) model = mh.fit(df_hashed) # 5. Finn lignende par similar_pairs = model.approxSimilarityJoin( df_hashed, df_hashed, threshold, distCol="distance" ) return similar_pairs.filter(F.col("datasetA.id") < F.col("datasetB.id")) # Eksempel: Dedupliser organisasjoner duplicates = fuzzy_match_entities( df_organizations, match_columns=["org_name", "address", "postal_code"], threshold=0.3 # Lav avstand = hoy likhet ) ``` ### Deterministic vs. Probabilistic Matching | Tilnaerming | Bruksomrade | Presisjon | Recall | |-------------|-------------|-----------|--------| | **Deterministic** | Eksakt match pa unike IDer | Hoy | Lav | | **Rule-based** | Kombinasjon av felt med toleranser | Moderat-hoy | Moderat | | **Probabilistic** | Fuzzy matching med ML-modeller | Moderat | Hoy | | **Hybrid** | Deterministic forst, deretter probabilistic | Hoy | Hoy | ```python # Hybrid-tilnaerming def hybrid_entity_resolution(df_source, df_reference): """ Trinn 1: Eksakt match pa organisasjonsnummer Trinn 2: Fuzzy match pa navn + adresse for resterende """ # Trinn 1: Deterministic match exact_matches = df_source.join( df_reference, df_source["org_number"] == df_reference["org_number"], "inner" ) unmatched = df_source.join( df_reference, df_source["org_number"] == df_reference["org_number"], "left_anti" ) # Trinn 2: Fuzzy match for umatchede fuzzy_matches = fuzzy_match_entities( unmatched.unionByName(df_reference, allowMissingColumns=True), match_columns=["org_name", "address"], threshold=0.4 ) return exact_matches, fuzzy_matches ``` --- ## MDM Integration with Dataverse ### Dataverse som Master Data System Dataverse fungerer som operativt masterdatasystem for Dynamics 365-apper: ``` +-------------------+ Service Bus +------------------+ | Dataverse |------ Queue ------->| Logic App | | (Master Data) | | (Transform to | | | | Canonical Model)| | - Accounts | +--------+---------+ | - Contacts | | | - Products | +--------v---------+ | - Addresses | | Service Bus Topic| +-------------------+ | (Canonical Data) | +--------+---------+ | +--------v---------+ | Consumers: | | - Fabric Lakehouse| | - Azure SQL | | - Power BI | +------------------+ ``` ### Synkronisering mellom Dataverse og Fabric ```python # Dataverse til Fabric via Azure Synapse Link # Konfigureres i Power Platform Admin Center # Alternativt: Dataverse connector i Fabric Data Pipeline # Pipeline -> Copy Activity -> Source: Dataverse -> Sink: Lakehouse # Eksempel: Les masterdata fra Dataverse via Spark df_accounts = spark.read \ .format("com.microsoft.cdm") \ .option("entity", "account") \ .option("dataverseUrl", "https://org.crm.dynamics.com") \ .load() # Opprett referansetabell i Lakehouse df_accounts.select( "accountid", "name", "address1_city", "accountnumber", "industrycode" ).write \ .format("delta") \ .mode("overwrite") \ .saveAsTable("lakehouse.default.ref_organizations") ``` ### Duplikatdeteksjon i Dataverse ``` # Dataverse duplikatdeteksjonsregler: # # 1. Opprett regel i Power Platform Admin Center # - Entitet: Account # - Matchkriterier: accountname (fuzzy match) # + address1_city (eksakt match) # # 2. Aktiver sanntidssjekk # - Ved opprettelse av ny post # - Ved oppdatering av eksisterende post # # 3. Bulk-deteksjon # - Kjor pa eksisterende data # - Planlegg regelmessig kjoring ``` --- ## Reference Data Versioning ### Versjonerte referansetabeller ```python # Implementer SCD Type 2 for referansedata i Fabric from delta.tables import DeltaTable def upsert_reference_data(ref_table_name, new_data_df, key_columns, tracked_columns): """ SCD Type 2 upsert for referansedata. Bevarer historikk for endrede verdier. """ ref_table = DeltaTable.forName(spark, ref_table_name) # Bygg match-betingelse match_condition = " AND ".join( [f"target.{col} = source.{col}" for col in key_columns] ) # Bygg endringsbetingelse change_condition = " OR ".join( [f"target.{col} != source.{col}" for col in tracked_columns] ) # Merk utgaatte rader og sett inn nye ref_table.alias("target").merge( new_data_df.alias("source"), match_condition ).whenMatchedUpdate( condition=change_condition, set={ "is_current": F.lit(False), "valid_to": F.current_timestamp() } ).whenNotMatchedInsertAll().execute() # Sett inn oppdaterte rader som nye changed = new_data_df.alias("s").join( ref_table.toDF().filter("is_current = false").alias("t"), [F.col(f"s.{c}") == F.col(f"t.{c}") for c in key_columns], "inner" ).select("s.*") \ .withColumn("is_current", F.lit(True)) \ .withColumn("valid_from", F.current_timestamp()) \ .withColumn("valid_to", F.lit(None).cast("timestamp")) changed.write.format("delta").mode("append").saveAsTable(ref_table_name) # Eksempel: Oppdater kommuner-referanse (relevant ved kommunesammenslaainger) upsert_reference_data( ref_table_name="lakehouse.default.ref_municipalities", new_data_df=df_new_municipalities, key_columns=["municipality_code"], tracked_columns=["municipality_name", "county_code", "county_name"] ) ``` ### Referansedata fra nasjonale registre | Register | Eier | Bruksomrade for AI | |----------|------|-------------------| | **Folkeregisteret** | Skatteetaten | Personentiteter i NER, chatbots | | **Enhetsregisteret** | Bronnoysundregistrene | Organisasjonsdata for bedriftsanalyse | | **Matrikkelen** | Kartverket | Eiendomsdata for geospatial AI | | **Sektor-/fagregistre** | Statlig sektoretat | Domenespesifikk data for AI-modeller | | **Kommuneregisteret** | SSB | Geografisk referanse | ```python # Eksempel: Last inn kommuneregister fra SSB API import requests import json def load_ssb_municipality_register(): """Last inn offisiell kommuneliste fra SSB.""" url = "https://data.ssb.no/api/klass/v1/classifications/131/codes" params = {"from": "2026-01-01", "to": "2026-12-31"} response = requests.get(url, params=params) data = response.json() # Konverter til Spark DataFrame df = spark.createDataFrame([ { "code": item["code"], "name": item["name"], "valid_from": item["validFrom"], "valid_to": item.get("validTo") } for item in data["codes"] ]) return df ``` --- ## Data Quality SLAs for MDM Entities ### Kvalitetsdimensjoner for masterdata | Dimensjon | Definisjon | Malemetode | Eksempel SLA | |-----------|-----------|------------|-------------| | **Completeness** | Andel utfylte felt | % ikke-null verdier | >= 98% | | **Uniqueness** | Ingen duplikater | Antall duplikater / totalt | = 100% | | **Accuracy** | Korrekthet mot kilde | Stikkprovekontroll | >= 99% | | **Timeliness** | Ferskhet pa data | Tid siden siste oppdatering | < 24 timer | | **Consistency** | Samsvar mellom systemer | Cross-system validering | >= 99.5% | | **Validity** | Overholder forretningsregler | Regelvalidering | >= 99% | ### Implementere DQ-SLAer i Fabric ```python from datetime import datetime, timedelta def check_mdm_quality_slas(table_name: str, slas: dict) -> dict: """ Sjekk datakvalitet mot definerte SLAer for en MDM-tabell. """ df = spark.table(table_name) total_rows = df.count() results = {} # Completeness if "completeness" in slas: for col_name, threshold in slas["completeness"].items(): non_null = df.filter(F.col(col_name).isNotNull()).count() pct = (non_null / total_rows) * 100 results[f"completeness_{col_name}"] = { "actual": round(pct, 2), "threshold": threshold, "passed": pct >= threshold } # Uniqueness if "uniqueness" in slas: key_cols = slas["uniqueness"]["columns"] distinct = df.select(key_cols).distinct().count() is_unique = distinct == total_rows results["uniqueness"] = { "distinct": distinct, "total": total_rows, "duplicates": total_rows - distinct, "passed": is_unique } # Timeliness if "timeliness" in slas: max_age = slas["timeliness"]["max_age_hours"] timestamp_col = slas["timeliness"]["column"] latest = df.agg(F.max(timestamp_col)).collect()[0][0] age_hours = (datetime.now() - latest).total_seconds() / 3600 results["timeliness"] = { "latest_update": str(latest), "age_hours": round(age_hours, 1), "threshold_hours": max_age, "passed": age_hours <= max_age } # Validity if "validity" in slas: for rule_name, rule in slas["validity"].items(): valid_count = df.filter(rule["condition"]).count() pct = (valid_count / total_rows) * 100 results[f"validity_{rule_name}"] = { "actual": round(pct, 2), "threshold": rule["threshold"], "passed": pct >= rule["threshold"] } return results # Eksempel: SLA-sjekk for organisasjons-masterdata slas = { "completeness": { "org_name": 100.0, "org_number": 100.0, "address": 95.0, "contact_email": 80.0 }, "uniqueness": { "columns": ["org_number"] }, "timeliness": { "column": "last_updated", "max_age_hours": 24 }, "validity": { "valid_org_number": { "condition": "LENGTH(org_number) = 9 AND org_number RLIKE '^[0-9]+$'", "threshold": 100.0 } } } results = check_mdm_quality_slas("lakehouse.default.ref_organizations", slas) ``` ### Alerting ved SLA-brudd ```python # Integrer med Power Automate for varsling def alert_on_sla_breach(results: dict, webhook_url: str): """Send varsel via Power Automate webhook ved SLA-brudd.""" breaches = {k: v for k, v in results.items() if not v.get("passed", True)} if breaches: payload = { "title": "MDM Quality SLA Breach", "summary": f"{len(breaches)} SLA-brudd oppdaget", "details": json.dumps(breaches, indent=2, default=str) } requests.post(webhook_url, json=payload) ``` --- ## Referanser - [Master data management in Microsoft Purview](https://learn.microsoft.com/en-us/purview/data-governance-master-data-management) -- MDM-oversikt i Purview - [Detect duplicate records and merge](https://learn.microsoft.com/en-us/power-platform/admin/detect-duplicate-records) -- Duplikatdeteksjon i Dataverse - [Dataverse as a master data system](https://learn.microsoft.com/en-us/dynamics365/guidance/reference-architectures/dataverse-master-data-system) -- Referansearkitektur for Dataverse MDM - [Master data management](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/cloud-scale-analytics/govern-master-data) -- MDM i Cloud Adoption Framework - [CluedIn integration for MDM](https://learn.microsoft.com/en-us/purview/data-governance-master-data-management-cluedin) -- Eventual connectivity for MDM - [Profisee integration for MDM](https://learn.microsoft.com/en-us/purview/data-governance-master-data-management-profisee) -- MDM med Azure-integrasjon - [Set up duplicate detection rules](https://learn.microsoft.com/en-us/power-platform/admin/set-up-duplicate-detection-rules-keep-data-clean) -- Konfigurere duplikatregler i Dataverse --- ## For Cosmo - **Bruk denne referansen** naar kunder trenger a sikre datakvalitet i stamdata for bruk i AI/ML-modeller, eller naar de planlegger MDM-strategi for Microsoft-plattformen. - **Dataverse er det naturlige valget** for operativ MDM i organisasjoner som allerede bruker Dynamics 365 eller Power Platform. For analytisk MDM, bruk Fabric Lakehouse med SCD Type 2. - **Entity resolution er kritisk for AI**: Uten riktig deduplisering vil ML-modeller laere fra inkonsistente data. Anbefal hybrid-tilnaerming: deterministic match forst, deretter fuzzy match. - **For norsk offentlig sektor**: Integrer med nasjonale registre (Folkeregisteret, Enhetsregisteret) som autoritative kilder. Disse bor ha hoyest prioritet i survivorship-regler. - **SLA-monitorering bor automatiseres**: Sett opp kvalitetssjekker som kjorer daglig og varsler ved brudd, spesielt for data som inngaar i AI-treningspipelines.