{"id":110766,"date":"2026-06-29T12:00:00","date_gmt":"2026-06-29T12:00:00","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=110766"},"modified":"2026-06-15T09:09:41","modified_gmt":"2026-06-15T09:09:41","slug":"i-let-copilot-build-my-database-heres-what-i-learned-and-everything-id-do-differently-next-time","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/i-let-copilot-build-my-database-heres-what-i-learned-and-everything-id-do-differently-next-time\/","title":{"rendered":"I let Copilot build my database. Here&#8217;s what I learned &#8211; and everything I&#8217;d do differently next time (part two)"},"content":{"rendered":"\n<p><strong>I handed Microsoft Copilot the keys to design a PostgreSQL schema for Azure Database for PostgreSQL Flexible Server, with as little intervention from me as I could stand. What came back was a working four-table schema (dialects, terms, translations, artifacts) with foreign keys, JSONB metadata, seed data for three SQL dialects, and a deployment checklist that <em>actually ran<\/em>. <\/strong> <\/p>\n\n\n\n<p><strong>But that&#8217;s just a small part of the story. In this article, I&#8217;ll shout about what it got right &#8211; but also emphasize what it got wrong, why it happened, and what I&#8217;ve learned. Includes tips, advice, and the full code repo for if you want to give<\/strong> <strong>it a go yourself.<\/strong><\/p>\n\n\n\n<p><em>This is the second part of Grant Fritchey&#8217;s <a href=\"https:\/\/www.red-gate.com\/simple-talk\/collections\/how-i-built-an-app-just-by-talking-to-an-ai-llm\/\" target=\"_blank\" rel=\"noreferrer noopener\">series on building an app and database almost entirely through an LLM.<\/a> The repo, including every prompt, lives <a href=\"https:\/\/github.com\/ScaryDBA\/dbRosetta\" target=\"_blank\" rel=\"noreferrer noopener\">on GitHub<\/a>.<\/em><\/p>\n\n\n\n<p>In part one, I established the rules of this experiment and got the AI to commit to a project plan. This time, we actually build something. The first thing on the list, per Copilot&#8217;s own recommendation in part one, is the database. So: a <a href=\"https:\/\/www.red-gate.com\/simple-talk\/resources\/books\/introduction-to-postgresql-for-the-data-professional\/\" target=\"_blank\" rel=\"noreferrer noopener\">PostgreSQL<\/a> schema for dbRosetta, hosted on <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/postgresql\/overview\" target=\"_blank\" rel=\"noreferrer noopener\">Azure Flexible Server<\/a>. And the most important bit: it&#8217;s all designed and generated by <a href=\"https:\/\/copilot.microsoft.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Copilot<\/a>, with as little intervention from me as I can stand.<\/p>\n\n\n\n<p>What follows is what happened, what I let slide, what I overrode, and a slightly uncomfortable question I haven&#8217;t fully answered for myself yet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-generating-the-prompt\">Generating the prompt<\/h2>\n\n\n\n<p>I&#8217;d originally said I would write two prompts in parallel \u2014 one by hand, one from Copilot \u2014 and compare them. On reflection I dropped that. The whole point of this series is to see what happens when the AI runs the show, and writing my own version in parallel would have biased everything that came after it. <\/p>\n\n\n\n<p>So, I gave Copilot a one-liner asking for the <a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Visual Studio Code (VSCode)<\/a> prompt and got back a properly structured one: schema name, table list, constraints, JSONB metadata, <a href=\"https:\/\/www.red-gate.com\/hub\/product-learning\/test-data-manager\/deterministic-data-masking-in-redgate-test-data-manager\/\" target=\"_blank\" rel=\"noreferrer noopener\">deterministic<\/a> style, <em>&#8220;do not include destructive statements,&#8221;<\/em> sample seed inserts for three dialects, and a deployment checklist.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><em><strong>Click here for the full prompt, and Copilot&#8217;s response<\/strong><\/em><\/summary>\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Prompt<\/strong><\/td><td><strong>Response<\/strong><\/td><\/tr><tr><td><em>OK. Let&#8217;s start with the initial prompt for vsCode to create the database. Would you like to create an example of that prompt? Do you need more information before you create the database that I&#8217;ve roughly described?<\/em><\/td><td><strong>VS Code prompt for Copilot (create initial Azure Postgres schema)<\/strong>&nbsp;Use this prompt in VS Code Copilot (or your prompt-runner) to generate the initial SQL DDL and a tiny deployment checklist. It assumes an Azure Database for PostgreSQL instance and creates the minimal tables for dbRosetta v0: dialects, terms, translations, and a simple artifacts\/metadata table. It also records the assumptions used so the output is deterministic.Prompt SYSTEM: You are a careful SQL developer and infra assistant. Produce a single SQL file (UTF-8) that can be applied to an Azure PostgreSQL instance to create a schema named dbrosetta and four tables: dialects, terms, translations, and artifacts. Include sensible indexes, constraints, and comments that reflect the acceptance criteria for term translation (no users\/auth stored, cacheable translations, metadata JSON). Use standard PostgreSQL types and features compatible with Azure Database for PostgreSQL (single server\/postgres-compatible). Do not include any destructive statements (DROP TABLE). At the top of the file, include a brief assumptions block (postgres version, encoding, owner placeholder). Provide example INSERTs for a few dialects (postgres, sqlserver, mysql). USER: Generate the SQL file now, then provide a one-paragraph deployment checklist with the minimal Azure connection\/env vars and a sample psql command to apply the file. Keep SQL and checklist separate and clearly labeled. Use low creativity and deterministic style.<strong><br>Example SQL DDL result (what Copilot should produce)<\/strong>&#8212; Assumptions:<br>&#8212; Postgres version: compatible with 12+ features<br>&#8212; Encoding: UTF8<br>&#8212; Schema: dbrosetta<br>&#8212; Owner: replace with your DB user&nbsp;CREATE SCHEMA IF NOT EXISTS dbrosetta AUTHORIZATION current_user;&#8211; dialects: supported DB dialects and a docs home URL CREATE TABLE dbrosetta.dialects ( id SERIAL PRIMARY KEY, slug TEXT NOT NULL UNIQUE, name TEXT NOT NULL, documentation_home_url TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); COMMENT ON TABLE dbrosetta.dialects IS &#8216;Registry of supported dialects and their canonical documentation home page&#8217;;&#8211; terms: canonical terms per dialect for potential seed data CREATE TABLE dbrosetta.terms ( id SERIAL PRIMARY KEY, dialect_id INT NOT NULL REFERENCES dbrosetta.dialects(id) ON DELETE RESTRICT, term TEXT NOT NULL, canonical_normalized_term TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (dialect_id, canonical_normalized_term) ); COMMENT ON TABLE dbrosetta.terms IS &#8216;Optional seeding of known terms per dialect; canonical_normalized_term is lowercased normalization&#8217;;&#8211; translations: cached translation results and metadata from runs CREATE TABLE dbrosetta.translations ( id BIGSERIAL PRIMARY KEY, from_dialect_id INT NOT NULL REFERENCES dbrosetta.dialects(id) ON DELETE RESTRICT, to_dialect_id INT NOT NULL REFERENCES dbrosetta.dialects(id) ON DELETE RESTRICT, input_term TEXT NOT NULL, preferred_term TEXT, alternates JSONB DEFAULT &#8216;[]&#8217;::jsonb, description TEXT, doc_url TEXT, mapping_report JSONB DEFAULT &#8216;[]&#8217;::jsonb, metadata JSONB NOT NULL, cached BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (from_dialect_id, to_dialect_id, lower(input_term)) WHERE cached ); COMMENT ON TABLE dbrosetta.translations IS &#8216;Cached translations: metadata includes prompt_version, model_settings, run_id, timestamp&#8217;;&#8211; artifacts: optional generic artifacts produced by runs CREATE TABLE dbrosetta.artifacts ( id BIGSERIAL PRIMARY KEY, run_id UUID NOT NULL, artifact_type TEXT NOT NULL, &#8212; e.g., translation.json, metadata.json payload JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); COMMENT ON TABLE dbrosetta.artifacts IS &#8216;Store run artifacts for auditing and reproducibility&#8217;;&#8211; Example seeds INSERT INTO dbrosetta.dialects (slug, name, documentation_home_url) VALUES (&#8216;postgres&#8217;, &#8216;PostgreSQL&#8217;, &#8216;https:\/\/www.postgresql.org\/docs\/&#8217;), (&#8216;sqlserver&#8217;, &#8216;Microsoft SQL Server&#8217;, &#8216;https:\/\/learn.microsoft.com\/sql\/&#8217;), (&#8216;mysql&#8217;, &#8216;MySQL&#8217;, &#8216;https:\/\/dev.mysql.com\/doc\/&#8217;);&#8211; Optional: index to speed lookup by normalized input term in translations CREATE INDEX IF NOT EXISTS idx_translations_from_to_input ON dbrosetta.translations(from_dialect_id, to_dialect_id, lower(input_term));<strong>&nbsp;<\/strong><strong>Minimal deployment checklist (one-paragraph)<\/strong>Assumptions: you have an Azure PostgreSQL server and a database created; export connection info as environment variables PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD. Apply the SQL file with: psql &#8220;host=$PGHOST port=$PGPORT dbname=$PGDATABASE user=$PGUSER password=$PGPASSWORD sslmode=require&#8221; -f dbrosetta-init.sql. . In CI, store secrets in GH Actions secrets or Azure Key Vault and run the same psql command in a job step; verify success by querying SELECT count(*) FROM dbrosetta.dialects;.<strong>&nbsp;<\/strong><strong>Assumptions I used (so you can edit them into the prompt-runner)<\/strong>Azure Postgres supports standard PostgreSQL 12+ features and JSONB.No row-level security or advanced role setup is included; owner replacement is manual.Caching is enabled by default: translations are inserted with cached=true.Metadata JSON must include prompt_version and model_settings as strings\/objects during run-time (enforced by application logic, not DB constraints).If this meets your needs, I will produce a ready-to-save dbrosetta-init.sql file and a short GitHub Actions job snippet next.<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/details>\n\n\n\n<p><\/p>\n\n\n\n<p>The interesting part was the deviation. Back in part one, Copilot and I agreed a canonical prompt template \u2014 frontmatter with version and model settings, explicit input and output contracts, acceptance criteria, telemetry keys. The prompt it produced for the database didn&#8217;t follow that template. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-in-response-here-s-what-i-then-asked-it-directly\">In response, here&#8217;s what I then asked it directly:<\/h4>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>That&#8217;s great and I&#8217;ll use it as written within VSCode if it&#8217;s what you want. However, is that following the template that you defined for me earlier?<\/em><\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong><em>Click here for the full prompt, and Copilot&#8217;s response<\/em><\/strong><\/summary>\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Prompt<\/strong><\/td><td><strong>Response<\/strong><\/td><\/tr><tr><td><em>That&#8217;s great and I&#8217;ll use it as written within vsCode if it&#8217;s what you want. However, is that following the template that you defined for me earlier?<\/em><\/td><td><strong>Assessment<\/strong>Yes \u2014 the VS Code prompt and the example SQL DDL you accepted broadly follow the canonical prompt template you asked for, but they\u2019re not a 1:1 fulfillment of every template section. The prompt covers Intent, Context, Template Prompt text, and a deterministic ModelSettings requirement, and the DDL output aligns with the Acceptance Criteria and Guardrails you described.<strong>Where it already matches the canonical template<\/strong><strong>Intent<\/strong>: Clearly stated (create initial Azure Postgres schema for dbRosetta).<strong>Context<\/strong>: Included assumptions about Azure Postgres, schema name, and non\u2011destructive output.<strong>Template Prompt (literal)<\/strong>: Provided a SYSTEM and USER instruction that can be pasted into Copilot or a prompt-runner.<strong>Guardrails<\/strong>: Explicit \u201cDo not include destructive statements\u201d and determinism guidance (low creativity).<strong>Post Processing expectations<\/strong>: You asked for a separate checklist and told Copilot to keep SQL and checklist separate.<strong>Small gaps to make it fully conformant (suggested, optional)<\/strong><strong>Frontmatter \/ ModelSettings block<\/strong>: Add a short frontmatter header (Title, Version, ModelSettings) to the prompt file so it matches the template structure and can be parsed by your prompt-runner.<strong>Input \/ Output Contracts<\/strong>: Explicitly state the Input Contract (e.g., target schema name, owner placeholder, desired seed dialects) and Output Contract (single SQL file path; checklist text file) inside the prompt text so CI and schema validators can assert them.<strong>Acceptance Criteria<\/strong>: Convert the existing informal checks into 3\u20136 bullet acceptance sentences (e.g., &#8220;DDL creates schema and tables without DROP statements&#8221;; &#8220;translations table contains metadata JSON and cache uniqueness constraint&#8221;; &#8220;psql apply command succeeds in CI&#8221;).<strong>Telemetry keys<\/strong>: Ask the run to emit a run_id and include metadata keys (prompt_version, model_settings) in an artifacts JSON when the prompt-runner executes.<strong>Minimal actionable edit (one small snippet you can paste)<\/strong>Add this frontmatter and two short contract lines at the top of your prompt before the SYSTEM line:Title: &#8220;dbRosetta init schema&#8221; Version: &#8220;0.1.0&#8221; ModelSettings: { temperature: 0.0, max_tokens: 1024, top_p: 1.0 }Input Contract: { schema_name: &#8220;dbrosetta&#8221;, owner_placeholder: &#8220;&lt;DB_USER&gt;&#8221;, seed_dialects: [&#8220;postgres&#8221;,&#8221;sqlserver&#8221;,&#8221;mysql&#8221;] } Output Contract: { file: &#8220;dbrosetta-init.sql&#8221;, checklist: &#8220;dbrosetta-deploy-checklist.txt&#8221; }&#8230;&lt;additional stuff snipped&gt;<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/details>\n<\/blockquote>\n\n\n\n<p><\/p>\n\n\n\n<p>Copilot acknowledged the gaps cheerfully, told me what was missing, and offered a small frontmatter snippet to bolt on. To paraphrase: <em>yes, broadly, but here are five things you could add to make it fully conformant.<\/em> Which is fine. But it would never have flagged any of this had I not asked. <\/p>\n\n\n\n<p><strong>An LLM will produce something that resembles what you asked for, much faster than it will produce something that conforms to a standard you defined three sessions ago.<\/strong> Bookmark that one.<\/p>\n\n\n\n<p>I let it run with the corrected prompt. Over to VSCode.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-frankenstein-with-relational-integrity\">Frankenstein, with relational integrity<\/h2>\n\n\n\n<p>I&#8217;m not going to lie. There is something deeply odd about watching an LLM create a file in your workspace, fill it with two hundred lines of DDL, and then write you a deployment checklist that <em>actually works<\/em>. <\/p>\n\n\n\n<p>From the prompt, it generated <code>init-dbrosetta-schema.sql<\/code>, four tables,<a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/sql-server-index-basics\/\" target=\"_blank\" rel=\"noreferrer noopener\"> indexes<\/a>, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/questions-about-primary-and-foreign-keys-you-were-too-shy-to-ask\/\" target=\"_blank\" rel=\"noreferrer noopener\">foreign keys<\/a>, comments on every column, sample seed data for PostgreSQL, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Server<\/a> and <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/mysql\/getting-started-mysql\/\" target=\"_blank\" rel=\"noreferrer noopener\">MySQL<\/a>, and a tidy little <code>psql<\/code> command to apply it.<\/p>\n\n\n\n<p>I tested it. It worked. Schema present, tables present, seed data queryable. I feel just a little like Victor Frankenstein. Actually, I feel a lot like Victor Frankenstein.<\/p>\n\n\n\n<p>Here&#8217;s the interesting part of what it produced \u2014 the <code>translations<\/code> table, which is where the domain actually lives:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE TABLE translations (\n    id SERIAL PRIMARY KEY,\n    term_id INTEGER NOT NULL REFERENCES terms(id) ON DELETE CASCADE,\n    dialect_id INTEGER NOT NULL REFERENCES dialects(id) ON DELETE CASCADE,\n    translated_term VARCHAR(500) NOT NULL,\n    syntax_pattern TEXT,\n    examples TEXT,\n    notes TEXT,\n    confidence_level INTEGER DEFAULT 100\n        CHECK (confidence_level &gt;= 0 AND confidence_level &lt;= 100),\n    is_active BOOLEAN NOT NULL DEFAULT true,\n    metadata JSONB,\n    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    UNIQUE(term_id, dialect_id)\n);<\/pre><\/div>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong><em>Click here to see the full code<\/em><\/strong><\/summary>\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">-- =============================================================================\n-- dbRosetta Schema Initialization\n-- =============================================================================\n-- ASSUMPTIONS:\n-- - PostgreSQL version 11+ (Azure Database for PostgreSQL compatible)\n-- - UTF-8 encoding\n-- - Database owner\/admin privileges required for schema creation\n-- - Target database already exists\n-- - Schema owner placeholder: replace 'dbrosetta_owner' with actual role\n-- =============================================================================\n-- Create schema\nCREATE SCHEMA IF NOT EXISTS dbrosetta;\n-- Set search path for this session\nSET search_path TO dbrosetta, public;\n-- =============================================================================\n-- TABLE: dialects\n-- Stores SQL dialect definitions and metadata\n-- =============================================================================\nCREATE TABLE dialects (\n    id SERIAL PRIMARY KEY,\n    name VARCHAR(50) NOT NULL UNIQUE,\n    display_name VARCHAR(100) NOT NULL,\n    version VARCHAR(20),\n    description TEXT,\n    is_active BOOLEAN NOT NULL DEFAULT true,\n    metadata JSONB,\n    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n-- Indexes for dialects table\nCREATE INDEX idx_dialects_name ON dialects (name);\nCREATE INDEX idx_dialects_active ON dialects (is_active);\nCREATE INDEX idx_dialects_metadata ON dialects USING GIN (metadata);\n-- Comments for dialects table\nCOMMENT ON TABLE dialects IS 'SQL dialect definitions and metadata for translation system';\nCOMMENT ON COLUMN dialects.name IS 'Unique identifier for the dialect (lowercase, no spaces)';\nCOMMENT ON COLUMN dialects.display_name IS 'Human-readable name for the dialect';\nCOMMENT ON COLUMN dialects.version IS 'Version identifier for the dialect';\nCOMMENT ON COLUMN dialects.is_active IS 'Flag to enable\/disable dialect for translations';\nCOMMENT ON COLUMN dialects.metadata IS 'Additional dialect configuration and features as JSON';\n-- =============================================================================\n-- TABLE: terms\n-- Stores canonical SQL terms and their definitions\n-- =============================================================================\nCREATE TABLE terms (\n    id SERIAL PRIMARY KEY,\n    canonical_term VARCHAR(200) NOT NULL UNIQUE,\n    category VARCHAR(50) NOT NULL,\n    subcategory VARCHAR(50),\n    description TEXT NOT NULL,\n    usage_context TEXT,\n    is_active BOOLEAN NOT NULL DEFAULT true,\n    metadata JSONB,\n    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n \n-- Indexes for terms table\nCREATE INDEX idx_terms_canonical ON terms (canonical_term);\nCREATE INDEX idx_terms_category ON terms (category);\nCREATE INDEX idx_terms_active ON terms (is_active);\nCREATE INDEX idx_terms_category_sub ON terms (category, subcategory);\nCREATE INDEX idx_terms_metadata ON terms USING GIN (metadata);\n-- Comments for terms table\nCOMMENT ON TABLE terms IS 'Canonical SQL terms and definitions for cross-dialect translation';\nCOMMENT ON COLUMN terms.canonical_term IS 'Standardized term identifier (case-sensitive)';\nCOMMENT ON COLUMN terms.category IS 'Primary classification of the term (e.g., function, operator, keyword)';\nCOMMENT ON COLUMN terms.subcategory IS 'Secondary classification for more specific grouping';\nCOMMENT ON COLUMN terms.usage_context IS 'Additional context about when and how to use this term';\nCOMMENT ON COLUMN terms.metadata IS 'Additional term properties and configuration as JSON';\n \n-- =============================================================================\n-- TABLE: translations\n-- Stores dialect-specific translations for canonical terms\n-- =============================================================================\nCREATE TABLE translations (\n    id SERIAL PRIMARY KEY,\n    term_id INTEGER NOT NULL REFERENCES terms(id) ON DELETE CASCADE,\n    dialect_id INTEGER NOT NULL REFERENCES dialects(id) ON DELETE CASCADE,\n    translated_term VARCHAR(500) NOT NULL,\n    syntax_pattern TEXT,\n    examples TEXT,\n    notes TEXT,\n    confidence_level INTEGER DEFAULT 100 CHECK (confidence_level &gt;= 0 AND confidence_level &lt;= 100),\n    is_active BOOLEAN NOT NULL DEFAULT true,\n    metadata JSONB,\n    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    UNIQUE(term_id, dialect_id)\n);\n \n-- Indexes for translations table\nCREATE INDEX idx_translations_term ON translations (term_id);\nCREATE INDEX idx_translations_dialect ON translations (dialect_id);\nCREATE INDEX idx_translations_active ON translations (is_active);\nCREATE INDEX idx_translations_confidence ON translations (confidence_level);\nCREATE INDEX idx_translations_term_dialect ON translations (term_id, dialect_id);\nCREATE INDEX idx_translations_metadata ON translations USING GIN (metadata);\n-- Comments for translations table\nCOMMENT ON TABLE translations IS 'Dialect-specific translations for canonical terms with caching support';\nCOMMENT ON COLUMN translations.translated_term IS 'The dialect-specific equivalent of the canonical term';\nCOMMENT ON COLUMN translations.syntax_pattern IS 'Template or pattern showing how to use the translated term';\nCOMMENT ON COLUMN translations.examples IS 'Example usage in the target dialect';\nCOMMENT ON COLUMN translations.confidence_level IS 'Translation accuracy confidence (0-100)';\nCOMMENT ON COLUMN translations.metadata IS 'Additional translation properties and caching hints as JSON';\n \n-- =============================================================================\n-- TABLE: artifacts\n-- Stores generated SQL artifacts and their metadata\n-- =============================================================================\nCREATE TABLE artifacts (\n    id SERIAL PRIMARY KEY,\n    name VARCHAR(200) NOT NULL,\n    artifact_type VARCHAR(50) NOT NULL,\n    source_dialect_id INTEGER REFERENCES dialects(id) ON DELETE SET NULL,\n    target_dialect_id INTEGER REFERENCES dialects(id) ON DELETE SET NULL,\n    original_sql TEXT,\n    translated_sql TEXT,\n    translation_summary TEXT,\n    status VARCHAR(20) NOT NULL DEFAULT 'draft',\n    metadata JSONB,\n    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n-- Indexes for artifacts table\nCREATE INDEX idx_artifacts_name ON artifacts (name);\nCREATE INDEX idx_artifacts_type ON artifacts (artifact_type);\nCREATE INDEX idx_artifacts_source_dialect ON artifacts (source_dialect_id);\nCREATE INDEX idx_artifacts_target_dialect ON artifacts (target_dialect_id);\nCREATE INDEX idx_artifacts_status ON artifacts (status);\nCREATE INDEX idx_artifacts_created ON artifacts (created_at);\nCREATE INDEX idx_artifacts_metadata ON artifacts USING GIN (metadata);\n-- Comments for artifacts table\nCOMMENT ON TABLE artifacts IS 'Generated SQL artifacts from translation processes';\nCOMMENT ON COLUMN artifacts.name IS 'User-friendly name for the artifact';\nCOMMENT ON COLUMN artifacts.artifact_type IS 'Type of artifact (e.g., script, procedure, schema)';\nCOMMENT ON COLUMN artifacts.original_sql IS 'Source SQL before translation';\nCOMMENT ON COLUMN artifacts.translated_sql IS 'SQL after dialect translation';\nCOMMENT ON COLUMN artifacts.translation_summary IS 'Summary of changes made during translation';\nCOMMENT ON COLUMN artifacts.status IS 'Artifact status (draft, validated, deployed, etc.)';\nCOMMENT ON COLUMN artifacts.metadata IS 'Additional artifact properties and processing metadata as JSON';\n-- =============================================================================\n-- EXAMPLE DATA\n-- Sample dialects and basic term translations\n-- =============================================================================\n \n-- Insert sample dialects\nINSERT INTO dialects (name, display_name, version, description, metadata) VALUES\n('postgresql', 'PostgreSQL', '13+', 'PostgreSQL database system', '{\"features\": [\"arrays\", \"jsonb\", \"cte\"], \"url\": \"https:\/\/postgresql.org\"}'),\n('sqlserver', 'Microsoft SQL Server', '2019+', 'Microsoft SQL Server database system', '{\"features\": [\"cte\", \"windowing\", \"xml\"], \"url\": \"https:\/\/microsoft.com\/sql\"}'),\n('mysql', 'MySQL', '8.0+', 'MySQL database system', '{\"features\": [\"json\", \"cte\"], \"url\": \"https:\/\/mysql.com\"}');\n-- Insert sample terms\nINSERT INTO terms (canonical_term, category, subcategory, description, usage_context) VALUES\n('CONCAT', 'function', 'string', 'Concatenates two or more strings together', 'String manipulation and formatting'),\n('ISNULL', 'function', 'conditional', 'Tests if a value is NULL and returns alternative', 'NULL handling and conditional logic'),\n('LIMIT', 'clause', 'query', 'Limits the number of rows returned by a query', 'Result set pagination and performance');\n-- Insert sample translations\nINSERT INTO translations (term_id, dialect_id, translated_term, syntax_pattern, examples, confidence_level) VALUES\n-- CONCAT translations\n(1, 1, 'CONCAT', 'CONCAT(string1, string2, ...)', 'SELECT CONCAT(''Hello'', '' '', ''World'');', 100),\n(1, 2, 'CONCAT', 'CONCAT(string1, string2, ...)', 'SELECT CONCAT(''Hello'', '' '', ''World'');', 100),\n(1, 3, 'CONCAT', 'CONCAT(string1, string2, ...)', 'SELECT CONCAT(''Hello'', '' '', ''World'');', 100),\n-- ISNULL translations\n(2, 1, 'COALESCE', 'COALESCE(value, replacement)', 'SELECT COALESCE(column, ''N\/A'') FROM table;', 95),\n(2, 2, 'ISNULL', 'ISNULL(value, replacement)', 'SELECT ISNULL(column, ''N\/A'') FROM table;', 100),\n(2, 3, 'IFNULL', 'IFNULL(value, replacement)', 'SELECT IFNULL(column, ''N\/A'') FROM table;', 100),\n-- LIMIT translations\n(3, 1, 'LIMIT', 'LIMIT count [OFFSET offset]', 'SELECT * FROM table LIMIT 10 OFFSET 20;', 100),\n(3, 2, 'TOP', 'TOP (count)', 'SELECT TOP (10) * FROM table ORDER BY id OFFSET 20 ROWS;', 90),\n(3, 3, 'LIMIT', 'LIMIT [offset,] count', 'SELECT * FROM table LIMIT 20, 10;', 100);\n \n-- =============================================================================\n-- GRANTS AND PERMISSIONS\n-- =============================================================================\n-- Grant schema usage (replace 'dbrosetta_owner' with actual role)\n-- GRANT USAGE ON SCHEMA dbrosetta TO dbrosetta_owner;\n-- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA dbrosetta TO dbrosetta_owner;\n-- GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA dbrosetta TO dbrosetta_owner;\n \n-- =============================================================================\n-- SCHEMA VALIDATION\n-- =============================================================================\n-- Verify schema creation\nSELECT\n    schemaname,\n    tablename,\n    tableowner\nFROM pg_tables\nWHERE schemaname = 'dbrosetta'\nORDER BY tablename;\n-- Verify sample data\nSELECT\n    d.name as dialect,\n    t.canonical_term,\n    tr.translated_term,\n    tr.confidence_level\nFROM dialects d\nJOIN translations tr ON d.id = tr.dialect_id\nJOIN terms t ON tr.term_id = t.id\nORDER BY t.canonical_term, d.name;<\/pre><\/div>\n<\/details>\n\n\n\n<p><\/p>\n\n\n\n<p>That&#8217;s a reasonable starting point. It also seeded sensible cross-dialect examples \u2014 <code>ISNULL<\/code> in SQL Server maps to <code><a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-coalesce-explained-how-to-handle-null-values-easily\/\" target=\"_blank\" rel=\"noreferrer noopener\">COALESCE<\/a><\/code> in PostgreSQL and <code>IFNULL<\/code> in MySQL, each with a confidence score and an example query. I didn&#8217;t ask for confidence scores. It added them anyway. I&#8217;m leaving them in for now because they&#8217;re useful, but it&#8217;s a small reminder that the AI is making product decisions whenever you give it room. <\/p>\n\n\n\n<p><em>(The full schema, with all four tables, indexes and seeds, is <a href=\"https:\/\/github.com\/ScaryDBA\/dbRosetta\" target=\"_blank\" rel=\"noreferrer noopener\">in the repo<\/a>.)<\/em><\/p>\n\n\n\n<section id=\"my-first-block-block_c16bb44bd595ac74d2f154fa42750c32\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Accelerate and simplify database development with Redgate<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Automate time-consuming tasks and support consistent workflows.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/solutions\/use-cases\/productivity-and-workflow-automation\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Learn more: Accelerate and simplify database development with Redgate\">Learn more<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<p><\/p>\n\n\n\n<p>What I&#8217;m less keen on is the indexing. There&#8217;s an index on practically every column that could be filtered or joined on, including separate single-column indexes for fields that only appear together in composite lookups. As a starting point on day one of a project with no production workload yet, it&#8217;s defensible. <\/p>\n\n\n\n<p>However, as a pattern to ship, it&#8217;s the kind of thing you&#8217;d flag in code review: <em>this is going to cost you on writes, and most of these will never be used<\/em>. We&#8217;ll see whether it bites later.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-copilot-critiques-copilot\">Copilot critiques Copilot<\/h2>\n\n\n\n<p>This is where it got genuinely interesting. After confirming the script ran, I fed the whole <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/theory-and-design\/data-control-language-aka-security\/\" target=\"_blank\" rel=\"noreferrer noopener\">DDL<\/a> back to Copilot and asked it to validate the result against the acceptance criteria we&#8217;d set in part one. It came back with eight issues. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-the-eight-issues-it-presented-edited-for-length\">The eight issues it presented (edited for length):<\/h3>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li><code>SERIAL<\/code> works on PostgreSQL 11+, but <code>GENERATED ALWAYS AS IDENTITY<\/code> is more portable and avoids implicit sequence ownership issues on managed platforms.<br><br><\/li>\n\n\n\n<li><code>updated_at<\/code> defaults to <code>CURRENT_TIMESTAMP<\/code> but never changes on updates. Add triggers or maintain it in application code.<br><br><\/li>\n\n\n\n<li><code>canonical_term<\/code> is unique but case-sensitive. If lookups should be case-insensitive, add a unique index on <code>lower(canonical_term)<\/code> or store a normalised column.<br><br><\/li>\n\n\n\n<li>Foreign keys and unique constraints aren&#8217;t named. Naming them (<code>fk_translations_term<\/code>, etc.) improves clarity in migrations and error messages.<br><br><\/li>\n\n\n\n<li>Pattern-length limits are arbitrary. Consider <code>TEXT<\/code> instead of <code>VARCHAR(500)<\/code> for fields like <code>translated_term<\/code> and <code>examples<\/code>.<br><br><\/li>\n\n\n\n<li>The validation <code>SELECT<\/code> statements at the end of the script are fine locally but noisy in CI.<br><br><\/li>\n\n\n\n<li>Acceptance criteria mention <code>prompt_version<\/code> and <code>model_settings<\/code> in metadata. The schema doesn&#8217;t enforce them. Consider JSON Schema validation in the app layer, or a <code>CHECK<\/code> constraint.<br><br><\/li>\n\n\n\n<li><code>SET search_path TO dbrosetta, public<\/code> is fine interactively but should be avoided in scripts applied by CI or applications.<\/li>\n<\/ol>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-analyzing-copilot-s-validation-effort\">Analyzing Copilot&#8217;s validation effort<\/h3>\n\n\n\n<p>Most of those are good calls. I accepted six of them. I overrode two: the <code>updated_at<\/code> trigger (overkill for v0, do it in app code when there&#8217;s an app) and the in-script validation <code>SELECT<\/code>s (I&#8217;d rather pull those out entirely than maintain them as test artifacts). <\/p>\n\n\n\n<p>I&#8217;m a little shocked Copilot didn&#8217;t flag the indexing strategy \u2014 every other surface gets a recommendation, but the index-everything pattern got a pass. That tells me something about what&#8217;s overrepresented in the training data. Indexes that exist look correct. Indexes that <em>shouldn&#8217;t<\/em> exist look like prudence.<\/p>\n\n\n\n<p>I asked Copilot to regenerate the script with the six accepted changes \u2014 and <em>only<\/em> those changes. It did. The revised script applied cleanly. Database confirmed.<\/p>\n\n\n\n<p>Mission accomplished &#8211; we have a database. And I&#8217;m pretty excited about it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-awkward-question\">The awkward question<\/h2>\n\n\n\n<p>Here&#8217;s the thing I haven&#8217;t fully resolved. I am, by background, a database person. I have opinions on schema design. When I accepted six of Copilot&#8217;s eight suggestions and rejected two, I did that on the basis of judgement formed over thirty-odd years of doing this work.<\/p>\n\n\n\n<p><strong>Is my intervention biasing the experiment towards a result I&#8217;m comfortable with?<\/strong><\/p>\n\n\n\n<p>Yes. Obviously yes. The bigger question is whether that&#8217;s a problem.<\/p>\n\n\n\n<p>If the experiment is <em>&#8220;can an LLM build a working database with no human intervention,&#8221;<\/em> then every override is a contamination. If the experiment is <em>&#8220;can an LLM build a working database faster than I could on my own, with me as a reviewer rather than an author,&#8221;<\/em>  then my overrides are exactly the point \u2014 they&#8217;re what <em>makes<\/em> the experiment honest.<\/p>\n\n\n\n<p>I&#8217;m going with the second framing, because I think the first one is a fantasy that no real engineering organization should be running.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-the-harder-question-this-raises\">The harder question this raises&#8230;<\/h3>\n\n\n\n<p>Without my reviewing eye, what would have shipped? A database with an index on every column, no consistent foreign key naming, case-sensitive lookups in a field that almost certainly wants to be case-insensitive, and an <code>updated_at<\/code> field that lies about when rows were last updated.<\/p>\n\n\n\n<p>None of those are bugs. They all work. But they&#8217;re also all things that somebody is going to swear at five years from now, when this database is full of real data and somebody is trying to figure out why a particular query is slow, or why two rows that look identical are being treated as different.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-i-learned-and-what-i-d-now-do-differently\">What I learned (and what I&#8217;d now do differently)<\/h2>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong>If you&#8217;re letting an LLM design schema, the moment you have something that runs is exactly the moment to feed it back to a different prompt and ask it to critique its own output against your acceptance criteria<\/strong><br><br>This is the single most useful pattern I&#8217;ve found so far. Copilot was much more honest about the schema&#8217;s weaknesses when reviewing it as a reader than it was when generating it as an author. Two-pass generation, where pass two is <em>find the problems with pass one<\/em>, is worth the extra round trip every single time.<br><br><\/li>\n\n\n\n<li><strong>Watch for what the AI <em>doesn&#8217;t<\/em> flag<\/strong><br><br>The eight issues Copilot raised were all real. The thing it <em>didn&#8217;t<\/em> raise \u2014 a schema indexing strategy that would make a senior DBA wince \u2014 is the more interesting data point. If you&#8217;re using AI to generate code in an area you&#8217;re not expert in, that&#8217;s where the risk is. Not in the things the model is uncertain about; in the things the model is confidently wrong about because the training corpus is full of the same mistake.<br><br><\/li>\n\n\n\n<li><strong>Make peace with the bias question and move on<\/strong><br><br>You will override the AI&#8217;s choices. You should. Pretending otherwise is just performing rigour for the article. What matters is being honest, in writing, about <em>which<\/em> choices you overrode and <em>why<\/em>. This matters, so a reader without your background can see where your judgement entered the process and decide whether they&#8217;d have made the same call.<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-s-next\">What&#8217;s next<\/h2>\n\n\n\n<p>We have a database. The next step is making it deployable. I&#8217;ll use a <a href=\"https:\/\/www.red-gate.com\/products\/flyway\/\" target=\"_blank\" rel=\"noreferrer noopener\">Redgate Flyway<\/a> baseline against the existing schema, then a <a href=\"https:\/\/www.red-gate.com\/simple-talk\/devops\/ci-cd\/demystifying-continuous-integration-vs-continuous-delivery-part-1\/\" target=\"_blank\" rel=\"noreferrer noopener\">continuous integration (CI)<\/a> pipeline that can apply migrations from GitHub to the live Azure instance. That part of the series turned out to be where the AI tried hardest to compromise on security \u2014 and it&#8217;s the article I&#8217;m most interested in writing. More on that next time.<\/p>\n\n\n\n<p>One last thing, though, that has been nagging at me since this article finished generating itself&#8230;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-being-a-beginner-in-it-has-become-much-more-challenging-because-of-these-tools\">Being a beginner in IT has become much more challenging because of these tools<\/h3>\n\n\n\n<p>Look, we can argue about the naming standards, indexing, data type choices, and general table structure all we want. The fact of the matter is, that is a functional database. I did next to nothing to arrive at it other than interrogate the LLM a bit. Heck, <a href=\"https:\/\/github.com\/features\/copilot\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub Copilot<\/a> generated files for me. And filled them in. I could easily allow it to create new resources within Azure, same as everything else we&#8217;re doing. <\/p>\n\n\n\n<p>So, even the steps I&#8217;ve done manually, I didn&#8217;t <em>have<\/em> to. The question isn&#8217;t can a beginner do this. The question is, how does a beginner actually learn how to do this. What&#8217;s right? What&#8217;s wrong? And what&#8217;s simply problematic or a compromise? That&#8217;s all about experience, which this stuff is <em>not<\/em> giving me.<\/p>\n\n\n\n<section id=\"my-first-block-block_e2afcf12757d4f9d073f96fdebace8c5\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">\u201cEveryone wants to move faster with AI, but few are truly ready for it.&#8221;<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            What does the AI landscape look like in 2026? Get the full overview in Redgate&#8217;s 2026 State of the Database Landscape AI mini report >>                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/solutions\/state-of-database-landscape\/2026\/ai-mini-report\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Download the AI mini report: \u201cEveryone wants to move faster with AI, but few are truly ready for it.&quot;\">Download the AI mini report<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: Can Copilot build a database from the ground up?<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. Can Microsoft Copilot design a production-ready PostgreSQL schema?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">It produces a functional schema that runs on Azure: tables, foreign keys, indexes, seed data. But the first pass had case-sensitive lookups that should have been case-insensitive, an <code class=\"bg-text-200\/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]\">updated_at<\/code> column that never updated, unnamed constraints, and an index-on-everything pattern that hurts write performance. It works. It&#8217;s not what you&#8217;d ship.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. What is the two-pass AI code review pattern?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Feed the AI&#8217;s output back to it in a fresh prompt and ask it to critique the result against your acceptance criteria. Copilot was far more honest about the schema&#8217;s weaknesses as a reviewer than as an author &#8211; flagging eight real issues on the second pass. The extra round trip is worth it every time.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. What did Copilot fail to flag in its own schema?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">The indexing strategy. The schema included an index on nearly every column, including redundant ones for fields only used in composite lookups. A senior DBA would flag this on sight. Copilot didn&#8217;t &#8211; suggesting the training corpus treats over-indexing as prudence.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. Should you override the AI&#039;s choices when generating schemas?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">Yes, and being honest about the overrides is the point. Six of Copilot&#8217;s eight self-critique suggestions were accepted here; two were overridden. The override list is part of the deliverable.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">5. How does this affect people learning database design?<\/h3>\n            <div class=\"faq-answer\">\n                <div>\n<div data-test-render-count=\"1\">\n<div class=\"group\">\n<div class=\"contents\">\n<div class=\"group relative relative pb-3\" data-is-streaming=\"false\">\n<div class=\"font-claude-response relative leading-[1.65rem] [&amp;_pre&gt;div]:bg-bg-000\/50 [&amp;_pre&gt;div]:border-0.5 [&amp;_pre&gt;div]:border-border-400 [&amp;_.ignore-pre-bg&gt;div]:bg-transparent [&amp;_.standard-markdown_:is(p,blockquote,h1,h2,h3,h4,h5,h6)]:pl-2 [&amp;_.standard-markdown_:is(p,blockquote,ul,ol,h1,h2,h3,h4,h5,h6)]:pr-8 [&amp;_.progressive-markdown_:is(p,blockquote,h1,h2,h3,h4,h5,h6)]:pl-2 [&amp;_.progressive-markdown_:is(p,blockquote,ul,ol,h1,h2,h3,h4,h5,h6)]:pr-8\">\n<div>\n<div class=\"standard-markdown grid-cols-1 grid [&amp;_&gt;_*]:min-w-0 gap-3 standard-markdown\">\n<p class=\"font-claude-response-body break-words whitespace-normal leading-[1.7]\">A beginner can produce the same functional schema in an afternoon with no understanding of why the choices matter. The tools make it easy to arrive at a working database without the experience that tells you whether it&#8217;s a good one.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>What happens when Microsoft Copilot designs a PostgreSQL schema for Azure? Here&#8217;s what it got right, what it got wrong, and why beginners should pay attention.<br \/>\n&hellip;<\/p>\n","protected":false},"author":221792,"featured_media":110770,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[159169,143523,53,145792,143534,143524],"tags":[159075,4168,4170,159395,159378,5854,158978,4151],"coauthors":[6785],"class_list":["post-110766","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-databases","category-featured","category-mysql","category-postgresql","category-sql-server","tag-ai","tag-database","tag-database-administration","tag-how-i-built-an-app-ai-llm","tag-llm","tag-mysql","tag-postgresql","tag-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/110766","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221792"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=110766"}],"version-history":[{"count":11,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/110766\/revisions"}],"predecessor-version":[{"id":111264,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/110766\/revisions\/111264"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/110770"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=110766"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=110766"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=110766"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=110766"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}