Your Column Names Now Determine How Smart Your AI Is: Metadata Quality in the AI Era

Your Column Names Now Determine How Smart Your AI Is: Metadata Quality in the AI Era – SQLYARD

Your Column Names Now Determine How Smart Your AI Is: Metadata Quality in the AI Era


For most of SQL Server’s history, metadata quality was a housekeeping concern. Meaningful table names, descriptive column names, and extended property documentation were good practice, worth doing when time permitted, easy to defer when it did not. The database worked regardless of whether tbl_OrdHdr was named dbo.OrderHeader or whether column c4 had a description.

That calculus changed when AI tools started using your schema to generate SQL. The LLM generating a query against your database reads your table names, your column names, and your schema documentation to understand what your data means. If those names are cryptic abbreviations from 1998, the AI guesses what they mean. Sometimes it guesses right. Often it does not.

Recent evaluations of LLM SQL generation found that even the best general-purpose models achieve only around 52% execution accuracy on complex real-world queries. The primary driver of that failure rate is not the model. It is poor schema context. When a model receives rich metadata including table descriptions, column definitions, and relationship mappings, accuracy improves dramatically. When it receives cryptic names and no descriptions, it fabricates its best guess and the query runs incorrectly or not at all.

This article covers what metadata quality means in SQL Server terms, how to audit what you have, how to fix it using built-in SQL Server mechanisms, and how to export it into your AI knowledge base so the LLM understands your data correctly.

This is Part 2 of the SQLYARD AI architecture series. Part 1, How AI Connects to Your Company Data, covers the complete architecture from knowledge base to SQL Server to MCP. Read that first if you are new to this topic. This article assumes you understand how the LLM uses schema metadata and focuses specifically on improving that metadata quality.

1 Why Poor Metadata Makes Your AI Look Stupid Beginner

When an LLM generates SQL against your database, it needs to understand three things: which table contains the data being asked about, which columns within that table are relevant, and what the values in those columns actually mean in business terms. Every one of those questions is answered by your metadata.

A table named tbl_OrdHdr with columns named c1, c2, c4, flg1, and amt_net gives the LLM almost nothing to work with. It will attempt to infer meaning from abbreviations, make assumptions about what flg1 represents, and generate SQL that may look plausible but returns incorrect results. It will not tell you it is guessing. It will generate confident-looking SQL based on its best interpretation of cryptic names.

A table named dbo.OrderHeader with columns named OrderDate, CustomerID, ShippingStatus, IsHighPriority, and NetAmount gives the LLM clear, unambiguous context. Combined with an extended property description explaining what IsHighPriority means in your business context, the LLM generates correct SQL on the first attempt.

Poor Metadata — LLM Guesses

Table: tbl_OrdHdr
Columns: c1, c2, c4, flg1, amt_net

LLM generates SQL based on guesses about what each column means. Results may be wrong. No way to know until someone checks.

Good Metadata — LLM Understands

Table: dbo.OrderHeader
Columns: OrderDate, CustomerID, ShippingStatus, IsHighPriority, NetAmount

LLM generates correct SQL immediately. Column names are self-documenting. No ambiguity, no guessing.

The LLM will not tell you when it is guessing. It generates SQL confidently regardless of how good or bad the schema context is. A query generated against poorly named columns may run without errors, return results that look reasonable, and still be completely wrong because the LLM joined the wrong tables or filtered on the wrong column. The only protection is good metadata that leaves no room for misinterpretation.

2 The Three Types of Metadata the LLM Needs Beginner

To generate correct SQL, an LLM needs three categories of context. Each category answers a different question the model has when processing a natural language request about your data.

Schema Context: What Exists

Table names, column names, data types, nullability, primary keys, and foreign key relationships. This is the structural map of your database. Without it the LLM cannot construct a syntactically valid query. With good naming it can construct a semantically correct one.

Business Context: What It Means

What does the OrderStatus column actually contain? What are the valid values? What does status code 4 mean in business terms? What is the difference between CreatedDate and ProcessedDate? What does the IsLegacy flag indicate? This context lives in extended properties, documentation, and business glossaries. Without it the LLM cannot translate business questions into correct SQL predicates.

Usage Context: How It Is Used

Which tables are joined together in typical queries? Which columns are commonly filtered on? Which aggregations are standard for business reporting? Are there known quirks like a table that always requires a tenant filter to avoid cross-customer data leakage? Usage context is the hardest to document but the most valuable for preventing incorrect queries in multi-tenant or complex environments.

Context TypeSource in SQL ServerWithout ItWith It
Schema context sys.tables, sys.columns, sys.foreign_keys LLM cannot build valid queries LLM builds syntactically correct SQL
Business context Extended properties (MS_Description) LLM guesses what columns mean LLM understands business meaning
Usage context Schema documentation in knowledge base LLM may join wrong tables or miss required filters LLM applies correct patterns and business rules

3 Auditing Your Current Metadata Quality Intermediate

Before fixing anything, measure what you have. These queries give you an honest picture of your current metadata quality across tables, columns, and documentation coverage.

-- AUDIT 1: Tables with no extended property description
-- These are the tables the LLM knows nothing about beyond their name

SELECT
    s.name                              AS SchemaName,
    t.name                              AS TableName,
    t.create_date,
    COUNT(c.column_id)                  AS ColumnCount,
    'No description'                    AS DocumentationStatus
FROM sys.tables        t
JOIN sys.schemas       s ON s.schema_id = t.schema_id
LEFT JOIN sys.extended_properties ep
    ON  ep.major_id    = t.object_id
    AND ep.minor_id    = 0
    AND ep.name        = 'MS_Description'
    AND ep.class       = 1
JOIN sys.columns       c ON c.object_id = t.object_id
WHERE ep.value IS NULL
AND   t.is_ms_shipped  = 0
GROUP BY s.name, t.name, t.create_date
ORDER BY ColumnCount DESC;

-- AUDIT 2: Columns with cryptic names (short names that give LLM nothing to work with)
-- Names under 4 characters are almost always abbreviations

SELECT
    OBJECT_NAME(c.object_id)            AS TableName,
    c.name                              AS ColumnName,
    t.name                              AS DataType,
    LEN(c.name)                         AS NameLength,
    'Potentially cryptic name'          AS Issue
FROM sys.columns        c
JOIN sys.types          t ON t.user_type_id = c.user_type_id
WHERE LEN(c.name)      <= 4
AND   OBJECTPROPERTY(c.object_id, 'IsUserTable') = 1
AND   c.name NOT IN ('ID', 'Name', 'Code', 'Date', 'Type', 'Flag')
ORDER BY LEN(c.name), OBJECT_NAME(c.object_id);

-- AUDIT 3: Documentation coverage percentage by schema
-- Shows what percentage of tables and columns have descriptions

SELECT
    s.name                              AS SchemaName,
    COUNT(DISTINCT t.object_id)         AS TotalTables,
    COUNT(DISTINCT CASE WHEN ep_t.value IS NOT NULL
        THEN t.object_id END)           AS TablesWithDescription,
    COUNT(c.column_id)                  AS TotalColumns,
    COUNT(CASE WHEN ep_c.value IS NOT NULL
        THEN 1 END)                     AS ColumnsWithDescription,
    ROUND(100.0 * COUNT(DISTINCT CASE WHEN ep_t.value IS NOT NULL
        THEN t.object_id END)
        / NULLIF(COUNT(DISTINCT t.object_id), 0), 1) AS TableDocPct,
    ROUND(100.0 * COUNT(CASE WHEN ep_c.value IS NOT NULL
        THEN 1 END)
        / NULLIF(COUNT(c.column_id), 0), 1)          AS ColumnDocPct
FROM sys.schemas        s
JOIN sys.tables         t  ON t.schema_id = s.schema_id
JOIN sys.columns        c  ON c.object_id = t.object_id
LEFT JOIN sys.extended_properties ep_t
    ON  ep_t.major_id = t.object_id AND ep_t.minor_id = 0
    AND ep_t.name = 'MS_Description' AND ep_t.class = 1
LEFT JOIN sys.extended_properties ep_c
    ON  ep_c.major_id = c.object_id AND ep_c.minor_id = c.column_id
    AND ep_c.name = 'MS_Description' AND ep_c.class = 1
WHERE t.is_ms_shipped = 0
GROUP BY s.name
ORDER BY TableDocPct ASC;  -- worst documented schemas first

Target: 100% table description coverage on all tables exposed to AI tools, 80% column description coverage on non-obvious columns. Not every column needs a description. A column named CustomerID INT NOT NULL on a Customers table is self-explanatory. A column named StatusCode TINYINT NOT NULL with no description is opaque. Focus documentation effort on columns where the name alone does not communicate the business meaning or the valid values.

4 Before and After: What Poor vs Good Metadata Produces Beginner

These examples show exactly how metadata quality affects the SQL an LLM generates. Both examples use the same natural language question asked against two different schemas containing the same underlying data.

Example 1: Revenue by Customer Last Month

The question: "Show me revenue by customer for last month."

-- SCHEMA A: Poor metadata (real abbreviations from legacy systems)
-- tbl_TrnHdr: transaction header
-- Columns: tid, cid, tdt, amt, typ, sts

-- What the LLM generates (guessing):
SELECT cid, SUM(amt)
FROM tbl_TrnHdr
WHERE tdt >= DATEADD(MONTH, -1, GETDATE())
GROUP BY cid;
-- Problems:
-- 1. Did not filter by typ or sts -- may include refunds, voids, test transactions
-- 2. tdt might be transaction date or might be transaction posted date
-- 3. "Revenue" might not be amt -- there might be a separate net_amt column
-- 4. Results look plausible but may be significantly wrong
-- SCHEMA B: Good metadata with extended property descriptions
-- dbo.TransactionHeader
-- Columns:
--   TransactionID       int         -- Primary key
--   CustomerID          int         -- Foreign key to dbo.Customers
--   TransactionDate     date        -- Date the transaction was placed by the customer
--   NetRevenueAmount    decimal     -- Revenue after discounts, excludes tax and shipping
--   TransactionType     tinyint     -- 1=Sale 2=Refund 3=Void (exclude 2 and 3 for revenue)
--   TransactionStatus   tinyint     -- 1=Completed 2=Pending 3=Cancelled (include only 1)

-- What the LLM generates (understands the schema):
SELECT
    CustomerID,
    SUM(NetRevenueAmount)   AS TotalRevenue
FROM dbo.TransactionHeader
WHERE TransactionDate >= DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - 1, 0)
AND   TransactionDate <  DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)
AND   TransactionType   = 1   -- Sales only
AND   TransactionStatus = 1   -- Completed only
GROUP BY CustomerID
ORDER BY TotalRevenue DESC;
-- Correct: right date range, right filters, right amount column
-- The LLM knew exactly what to filter because descriptions said so

Example 2: A Business Term the LLM Needs to Know

-- The question: "How many high-priority orders are open right now?"

-- SCHEMA A: No documentation
-- dbo.Orders with column: PriorityCode int
-- LLM guesses: WHERE PriorityCode = 1  (maybe right, maybe not)

-- SCHEMA B: Extended property on PriorityCode says:
-- "Order priority level. Values: 1=Standard, 2=Expedited, 3=High Priority, 4=Emergency.
--  High Priority and Emergency orders (3 and 4) are tracked separately by the operations team."

-- LLM generates:
SELECT COUNT(*) AS HighPriorityOpenOrders
FROM dbo.Orders
WHERE PriorityCode IN (3, 4)   -- High Priority and Emergency per documentation
AND   OrderStatus NOT IN (5, 6) -- 5=Shipped, 6=Cancelled per OrderStatus documentation
-- Correct on first try because the documentation removed all ambiguity

5 Extended Properties: The Built-In SQL Server Mechanism Intermediate

SQL Server has a built-in mechanism for storing documentation alongside schema objects: extended properties. The standard property name MS_Description is the one used by SSMS, Visual Studio, and documentation tools as the default description field. It is also the property that should be populated for AI readiness because it is the first place any tool will look for a description.

-- Add a description to a table
EXEC sys.sp_addextendedproperty
    @name       = N'MS_Description',
    @value      = N'Contains one row per order placed by a customer.
                    Includes all order states from draft through completed.
                    Join to dbo.OrderLines for line item detail.
                    Always filter by TenantID in multi-tenant deployments.',
    @level0type = N'SCHEMA',  @level0name = N'dbo',
    @level1type = N'TABLE',   @level1name = N'OrderHeader';

-- Add a description to a column
EXEC sys.sp_addextendedproperty
    @name       = N'MS_Description',
    @value      = N'Current status of the order.
                    Values: 1=Draft, 2=Submitted, 3=Processing,
                            4=Shipped, 5=Delivered, 6=Cancelled, 7=Refunded.
                    Only statuses 2-5 represent active orders for reporting purposes.',
    @level0type = N'SCHEMA',  @level0name = N'dbo',
    @level1type = N'TABLE',   @level1name = N'OrderHeader',
    @level2type = N'COLUMN',  @level2name = N'OrderStatusID';

-- Update an existing description (use sp_updateextendedproperty if it already exists)
EXEC sys.sp_updateextendedproperty
    @name       = N'MS_Description',
    @value      = N'Updated description text here',
    @level0type = N'SCHEMA',  @level0name = N'dbo',
    @level1type = N'TABLE',   @level1name = N'OrderHeader',
    @level2type = N'COLUMN',  @level2name = N'OrderStatusID';

-- Read all descriptions for a table (confirm what is stored)
SELECT
    c.name                      AS ColumnName,
    t.name                      AS DataType,
    c.is_nullable,
    CAST(ep.value AS NVARCHAR(MAX)) AS Description
FROM sys.columns        c
JOIN sys.types          t  ON t.user_type_id = c.user_type_id
LEFT JOIN sys.extended_properties ep
    ON  ep.major_id    = c.object_id
    AND ep.minor_id    = c.column_id
    AND ep.name        = 'MS_Description'
    AND ep.class       = 1
WHERE c.object_id = OBJECT_ID('dbo.OrderHeader')
ORDER BY c.column_id;

Extended properties survive schema migrations, backups, and restores. They are stored in the database itself, not in an external documentation system. When you back up the database and restore it elsewhere, the descriptions come with it. When you script the database for deployment, the extended properties are included. Documentation stored in extended properties is always with the data it describes.

6 Naming Conventions That Teach the LLM Beginner

The single highest-return metadata improvement for AI readiness is meaningful naming. Extended properties add context on top of names, but names are what the LLM sees first and uses most. Good names eliminate entire categories of ambiguity before any description is needed.

Table Naming

PatternExampleAI ReadinessWhy
Meaningless abbreviation tbl_OrdHdr Poor LLM cannot infer meaning from OrdHdr reliably
Single word, clear noun dbo.Orders Acceptable Clear but does not distinguish header from lines
Descriptive compound noun dbo.OrderHeader Good Unambiguous, tells LLM this is the header record
Descriptive with schema context Sales.OrderHeader Best Schema name adds domain context that helps the LLM scope queries correctly

Column Naming

PatternExampleAI ReadinessWhy
Single character c1, c2, c4 Useless No information whatsoever for the LLM
Cryptic abbreviation flg1, amt_net, dt_proc Poor LLM guesses meaning, often incorrectly
Clear noun or verb phrase IsHighPriority, NetAmount, ProcessedDate Good Self-documenting, LLM understands immediately
Clear name plus description OrderStatusID with values documented in extended property Best Name identifies the column, description explains valid values and business meaning

7 Writing Descriptions That Actually Help Intermediate

Not all descriptions are equally useful. A description that says "the order date" on a column named OrderDate adds no information. A good description tells the LLM something it cannot infer from the name alone.

What Makes a Good Column Description

  • Document valid values and their meanings. For any column where the name alone does not convey what values mean (status codes, type flags, category IDs), list the values and their business interpretations.
  • Explain business rules embedded in the data. If a column is only populated under certain conditions, note that. If certain values should be excluded from standard reports, say so.
  • Clarify ambiguous terms. If your business uses "revenue" to mean net revenue after discounts and the column name is just Amount, explain what amount means in your context.
  • Note relationships not enforced by foreign keys. Legacy systems often have logical relationships that are not enforced by FK constraints. Document them so the LLM joins correctly.
-- Examples of good vs poor descriptions

-- POOR: restates what the name already says
EXEC sys.sp_addextendedproperty
    @name = N'MS_Description',
    @value = N'The customer ID.',
    @level0type = N'SCHEMA', @level0name = N'dbo',
    @level1type = N'TABLE',  @level1name = N'OrderHeader',
    @level2type = N'COLUMN', @level2name = N'CustomerID';

-- GOOD: adds context the name does not provide
EXEC sys.sp_addextendedproperty
    @name = N'MS_Description',
    @value = N'Foreign key to dbo.Customers.CustomerID.
               For B2B orders this is the company account ID.
               For B2C orders this is the individual consumer ID.
               Use the CustomerType column in dbo.Customers to distinguish.',
    @level0type = N'SCHEMA', @level0name = N'dbo',
    @level1type = N'TABLE',  @level1name = N'OrderHeader',
    @level2type = N'COLUMN', @level2name = N'CustomerID';

-- GOOD: documents valid values for a status/type column
EXEC sys.sp_addextendedproperty
    @name = N'MS_Description',
    @value = N'Order processing status.
               1=Draft: order started but not submitted.
               2=Submitted: order placed, awaiting processing.
               3=Processing: in fulfillment queue.
               4=Shipped: dispatched to carrier.
               5=Delivered: confirmed delivery.
               6=Cancelled: cancelled before shipment.
               7=Refunded: completed then refunded.
               For revenue reporting include only status 5 (Delivered).
               For active order counts include statuses 2, 3, and 4.',
    @level0type = N'SCHEMA', @level0name = N'dbo',
    @level1type = N'TABLE',  @level1name = N'OrderHeader',
    @level2type = N'COLUMN', @level2name = N'OrderStatusID';

-- TABLE DESCRIPTION: explains what the table is for and how to use it
EXEC sys.sp_addextendedproperty
    @name = N'MS_Description',
    @value = N'One row per customer order. Contains order-level data only.
               For line item detail join to dbo.OrderLines on OrderID.
               For customer information join to dbo.Customers on CustomerID.
               Multi-tenant: always filter by TenantID to prevent cross-tenant data access.
               Soft deletes: IsDeleted = 1 rows are logically deleted, exclude from all reports.',
    @level0type = N'SCHEMA', @level0name = N'dbo',
    @level1type = N'TABLE',  @level1name = N'OrderHeader';

8 Documenting Relationships and Business Rules Intermediate

Foreign key constraints teach the LLM how tables connect. But most production databases have logical relationships that are not enforced by foreign keys, business rules that determine how tables should be queried, and required filters that prevent incorrect results. These need to be documented explicitly because the LLM cannot infer them from the schema structure alone.

-- Add an extended property to a foreign key relationship
-- Documents the join pattern and any business rules around it

EXEC sys.sp_addextendedproperty
    @name  = N'MS_Description',
    @value = N'Links OrderHeader to Customers.
               Note: Some legacy orders (pre-2019) have CustomerID = -1
               indicating the customer record was archived.
               Exclude CustomerID = -1 for customer-level reporting.',
    @level0type = N'SCHEMA',       @level0name = N'dbo',
    @level1type = N'TABLE',        @level1name = N'OrderHeader',
    @level2type = N'CONSTRAINT',   @level2name = N'FK_OrderHeader_Customers';

-- Document a logical relationship not enforced by a foreign key
-- Use a table-level extended property to explain the join pattern
EXEC sys.sp_addextendedproperty
    @name  = N'MS_Description',
    @value = N'Stores product pricing history.
               To get the price that applied to a specific order line:
               JOIN dbo.PricingHistory ph
                 ON ph.ProductID = ol.ProductID
                AND ph.EffectiveDate <= o.OrderDate
                AND (ph.EndDate IS NULL OR ph.EndDate > o.OrderDate)
               There is no FK constraint because this is a temporal relationship.',
    @level0type = N'SCHEMA', @level0name = N'dbo',
    @level1type = N'TABLE',  @level1name = N'PricingHistory';

9 Exporting Schema Documentation Into the AI Knowledge Base Intermediate

Once your extended properties are populated, export the schema documentation into your AI knowledge base so the LLM can retrieve it during query generation. This is what bridges the gap between documentation stored in SQL Server and the LLM being able to use it.

-- Generate schema documentation for AI knowledge base ingestion
-- This query produces one row per table with complete documentation
-- Export this and load into your dbo.KnowledgeBase vector table

SELECT
    s.name                              AS SchemaName,
    t.name                              AS TableName,
    CAST(ep_t.value AS NVARCHAR(MAX))   AS TableDescription,
    -- Build a structured column documentation string
    STRING_AGG(
        CAST(c.name AS NVARCHAR(MAX))
        + ' ('
        + ty.name
        + CASE WHEN c.is_nullable = 0 THEN ', NOT NULL' ELSE ', NULL' END
        + ')'
        + CASE WHEN ep_c.value IS NOT NULL
               THEN ': ' + CAST(ep_c.value AS NVARCHAR(500))
               ELSE ''
          END,
        CHAR(13) + CHAR(10)
    ) WITHIN GROUP (ORDER BY c.column_id) AS ColumnDocumentation
FROM sys.schemas            s
JOIN sys.tables             t   ON t.schema_id  = s.schema_id
JOIN sys.columns            c   ON c.object_id  = t.object_id
JOIN sys.types              ty  ON ty.user_type_id = c.user_type_id
LEFT JOIN sys.extended_properties ep_t
    ON  ep_t.major_id = t.object_id
    AND ep_t.minor_id = 0
    AND ep_t.name     = 'MS_Description'
    AND ep_t.class    = 1
LEFT JOIN sys.extended_properties ep_c
    ON  ep_c.major_id = c.object_id
    AND ep_c.minor_id = c.column_id
    AND ep_c.name     = 'MS_Description'
    AND ep_c.class    = 1
WHERE t.is_ms_shipped = 0
AND   s.name NOT IN ('sys', 'INFORMATION_SCHEMA')
GROUP BY s.name, t.name, ep_t.value
ORDER BY s.name, t.name;

-- The output of this query becomes the document text for each table's
-- entry in dbo.KnowledgeBase
-- Feed it through the Python ingestion pipeline from Part 1:
-- Document type: 'schema_documentation'
-- Chunk strategy: one chunk per table (keeps table context together)
-- Update frequency: regenerate whenever schema changes
# Python: load schema documentation into the knowledge base
# Run after the SQL export above, or automate via a DDL trigger pipeline

import openai
import pyodbc
from datetime import date

client      = openai.OpenAI()
CONN_STRING = "your connection string"

def load_schema_documentation():
    """
    Read schema documentation from sys.extended_properties
    and load into the AI knowledge base as vector embeddings.
    """
    conn   = pyodbc.connect(CONN_STRING)
    cursor = conn.cursor()

    # Pull the schema documentation export query
    cursor.execute("""
        SELECT s.name AS SchemaName, t.name AS TableName,
               CAST(ep_t.value AS NVARCHAR(MAX)) AS TableDescription,
               STRING_AGG(
                   CAST(c.name AS NVARCHAR(MAX)) + ' (' + ty.name + ')'
                   + CASE WHEN ep_c.value IS NOT NULL
                          THEN ': ' + CAST(ep_c.value AS NVARCHAR(500))
                          ELSE '' END,
                   ', '
               ) WITHIN GROUP (ORDER BY c.column_id) AS Columns
        FROM sys.schemas s
        JOIN sys.tables t ON t.schema_id = s.schema_id
        JOIN sys.columns c ON c.object_id = t.object_id
        JOIN sys.types ty ON ty.user_type_id = c.user_type_id
        LEFT JOIN sys.extended_properties ep_t
            ON ep_t.major_id = t.object_id AND ep_t.minor_id = 0
            AND ep_t.name = 'MS_Description' AND ep_t.class = 1
        LEFT JOIN sys.extended_properties ep_c
            ON ep_c.major_id = c.object_id AND ep_c.minor_id = c.column_id
            AND ep_c.name = 'MS_Description' AND ep_c.class = 1
        WHERE t.is_ms_shipped = 0
        GROUP BY s.name, t.name, ep_t.value
    """)

    rows = cursor.fetchall()

    for row in rows:
        # Build a rich document string for each table
        doc_text = f"""Table: {row.SchemaName}.{row.TableName}
Description: {row.TableDescription or 'No description provided'}
Columns: {row.Columns}"""

        # Generate embedding
        embedding = client.embeddings.create(
            model = "text-embedding-3-small",
            input = doc_text
        ).data[0].embedding

        # Load into knowledge base
        cursor.execute("""
            -- Deactivate old version of this table's documentation
            UPDATE dbo.KnowledgeBase SET IsActive = 0
            WHERE DocumentName = ? AND DocumentType = 'schema_documentation';

            -- Insert fresh version
            INSERT INTO dbo.KnowledgeBase
                (DocumentName, DocumentType, Department, Classification,
                 Owner, LastVerified, ChunkText, Embedding)
            VALUES (?, 'schema_documentation', 'Database', 'Internal',
                    'DBA Team', ?, ?, CAST(? AS VECTOR(1536)))
        """,
            f"{row.SchemaName}.{row.TableName}",
            f"{row.SchemaName}.{row.TableName}",
            date.today(),
            doc_text,
            str(embedding)
        )

    conn.commit()
    print(f"Loaded schema documentation for {len(rows)} tables")

load_schema_documentation()

10 The Metadata Quality Checklist Beginner

Use this checklist to assess and improve metadata quality for any database being connected to an AI system. Work through it in order. The first items have the highest impact on AI output quality.

Naming

  • All tables have meaningful, descriptive names. No single-word abbreviations, no Hungarian notation prefixes like tbl_ or t_.
  • Tables are organized by schema that reflects business domains (Sales, HR, Finance, Operations) rather than all in dbo.
  • All columns have names that convey their meaning without needing a description. Column names are at least 5 characters and spell out the concept.
  • Boolean columns use Is, Has, or Can prefix: IsActive, HasOverride, CanDelete.
  • Date columns specify what date they represent: OrderDate, ShipDate, DeliveredDate. Never just Date or DT.
  • Amount columns specify what amount: NetRevenue, GrossAmount, TaxAmount. Never just Amount or Amt.
  • ID columns use the parent entity name: CustomerID, OrderID, ProductID. Never just ID or cid.

Extended Properties

  • Every table exposed to AI tools has an MS_Description extended property explaining what it contains, how to use it, and any required filters.
  • Every status, type, category, and flag column has a description listing all valid values and their business meanings.
  • Any column with business rules embedded (exclude soft deletes, always filter by tenant, only include completed records for revenue) has those rules documented.
  • Tables with non-enforced logical relationships have join patterns documented in their table description.
  • Columns where the column name could be misinterpreted have disambiguating descriptions.

Knowledge Base Integration

  • Schema documentation export query runs on a schedule and refreshes the knowledge base after every schema change.
  • Business glossary terms (domain-specific words like LegacyRefID, ChargebackCode, etc.) are documented in the knowledge base separately from schema documentation.
  • Known query patterns and anti-patterns are documented (what joins are correct, what filters are always required, what columns should never appear in a WHERE clause).
  • Metadata quality audit queries run monthly and flag tables and columns that fall below documentation thresholds.

Metadata quality is now a first-class DBA deliverable, not a nice-to-have. In organizations running AI tools against SQL Server, the quality of the LLM's SQL output is directly and measurably tied to the quality of the schema documentation. A DBA team that maintains excellent extended properties and exports them into the knowledge base is directly improving the accuracy of every AI-generated query against their databases. That is a new and concrete way to demonstrate value.

References


Discover more from SQLYARD

Subscribe to get the latest posts sent to your email.

Leave a Reply

Discover more from SQLYARD

Subscribe now to keep reading and get access to the full archive.

Continue reading