{
  "schemaVersion": "1.0",
  "item": {
    "slug": "sql-toolkit",
    "name": "SQL Toolkit",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/gitgoodordietrying/sql-toolkit",
    "canonicalUrl": "https://clawhub.ai/gitgoodordietrying/sql-toolkit",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/sql-toolkit",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=sql-toolkit",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-07T17:22:31.273Z",
      "expiresAt": "2026-05-14T17:22:31.273Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=afrexai-annual-report",
        "contentDisposition": "attachment; filename=\"afrexai-annual-report-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/sql-toolkit"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/sql-toolkit",
    "agentPageUrl": "https://openagent3.xyz/skills/sql-toolkit/agent",
    "manifestUrl": "https://openagent3.xyz/skills/sql-toolkit/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/sql-toolkit/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "SQL Toolkit",
        "body": "Work with relational databases directly from the command line. Covers SQLite, PostgreSQL, and MySQL with patterns for schema design, querying, migrations, indexing, and operations."
      },
      {
        "title": "When to Use",
        "body": "Creating or modifying database schemas\nWriting complex queries (joins, aggregations, window functions, CTEs)\nBuilding migration scripts\nOptimizing slow queries with indexes and EXPLAIN\nBacking up and restoring databases\nQuick data exploration with SQLite (zero setup)"
      },
      {
        "title": "SQLite (Zero Setup)",
        "body": "SQLite is included with Python and available on every system. Use it for local data, prototyping, and single-file databases."
      },
      {
        "title": "Quick Start",
        "body": "# Create/open a database\nsqlite3 mydb.sqlite\n\n# Import CSV directly\nsqlite3 mydb.sqlite \".mode csv\" \".import data.csv mytable\" \"SELECT COUNT(*) FROM mytable;\"\n\n# One-liner queries\nsqlite3 mydb.sqlite \"SELECT * FROM users WHERE created_at > '2026-01-01' LIMIT 10;\"\n\n# Export to CSV\nsqlite3 -header -csv mydb.sqlite \"SELECT * FROM orders;\" > orders.csv\n\n# Interactive mode with headers and columns\nsqlite3 -header -column mydb.sqlite"
      },
      {
        "title": "Schema Operations",
        "body": "-- Create table\nCREATE TABLE users (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    email TEXT NOT NULL UNIQUE,\n    name TEXT NOT NULL,\n    created_at TEXT DEFAULT (datetime('now')),\n    updated_at TEXT DEFAULT (datetime('now'))\n);\n\n-- Create with foreign key\nCREATE TABLE orders (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n    total REAL NOT NULL CHECK(total >= 0),\n    status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','paid','shipped','cancelled')),\n    created_at TEXT DEFAULT (datetime('now'))\n);\n\n-- Add column\nALTER TABLE users ADD COLUMN phone TEXT;\n\n-- Create index\nCREATE INDEX idx_orders_user_id ON orders(user_id);\nCREATE UNIQUE INDEX idx_users_email ON users(email);\n\n-- View schema\n.schema users\n.tables"
      },
      {
        "title": "Connection",
        "body": "# Connect\npsql -h localhost -U myuser -d mydb\n\n# Connection string\npsql \"postgresql://user:pass@localhost:5432/mydb?sslmode=require\"\n\n# Run single query\npsql -h localhost -U myuser -d mydb -c \"SELECT NOW();\"\n\n# Run SQL file\npsql -h localhost -U myuser -d mydb -f migration.sql\n\n# List databases\npsql -l"
      },
      {
        "title": "Schema Design Patterns",
        "body": "-- Use UUIDs for distributed-friendly primary keys\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\n\nCREATE TABLE users (\n    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),\n    email TEXT NOT NULL,\n    name TEXT NOT NULL,\n    password_hash TEXT NOT NULL,\n    role TEXT NOT NULL DEFAULT 'user' CHECK(role IN ('user','admin','moderator')),\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    CONSTRAINT users_email_unique UNIQUE(email)\n);\n\n-- Auto-update updated_at\nCREATE OR REPLACE FUNCTION update_modified_column()\nRETURNS TRIGGER AS $$\nBEGIN\n    NEW.updated_at = NOW();\n    RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n\nCREATE TRIGGER update_users_modtime\n    BEFORE UPDATE ON users\n    FOR EACH ROW EXECUTE FUNCTION update_modified_column();\n\n-- Enum type (PostgreSQL-specific)\nCREATE TYPE order_status AS ENUM ('pending', 'paid', 'shipped', 'delivered', 'cancelled');\n\nCREATE TABLE orders (\n    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),\n    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n    status order_status NOT NULL DEFAULT 'pending',\n    total NUMERIC(10,2) NOT NULL CHECK(total >= 0),\n    metadata JSONB DEFAULT '{}',\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\n-- Partial index (only index active orders — smaller, faster)\nCREATE INDEX idx_orders_active ON orders(user_id, created_at)\n    WHERE status NOT IN ('delivered', 'cancelled');\n\n-- GIN index for JSONB queries\nCREATE INDEX idx_orders_metadata ON orders USING GIN(metadata);"
      },
      {
        "title": "JSONB Queries (PostgreSQL)",
        "body": "-- Store JSON\nINSERT INTO orders (user_id, total, metadata)\nVALUES ('...', 99.99, '{\"source\": \"web\", \"coupon\": \"SAVE10\", \"items\": [{\"sku\": \"A1\", \"qty\": 2}]}');\n\n-- Query JSON fields\nSELECT * FROM orders WHERE metadata->>'source' = 'web';\nSELECT * FROM orders WHERE metadata->'items' @> '[{\"sku\": \"A1\"}]';\nSELECT metadata->>'coupon' AS coupon, COUNT(*) FROM orders GROUP BY 1;\n\n-- Update JSON field\nUPDATE orders SET metadata = jsonb_set(metadata, '{source}', '\"mobile\"') WHERE id = '...';"
      },
      {
        "title": "Connection",
        "body": "mysql -h localhost -u root -p mydb\nmysql -h localhost -u root -p -e \"SELECT NOW();\" mydb"
      },
      {
        "title": "Key Differences from PostgreSQL",
        "body": "-- Auto-increment (not SERIAL)\nCREATE TABLE users (\n    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n    email VARCHAR(255) NOT NULL UNIQUE,\n    name VARCHAR(255) NOT NULL,\n    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\n-- JSON type (MySQL 5.7+)\nCREATE TABLE orders (\n    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n    user_id BIGINT UNSIGNED NOT NULL,\n    metadata JSON,\n    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n);\n\n-- Query JSON\nSELECT * FROM orders WHERE JSON_EXTRACT(metadata, '$.source') = 'web';\n-- Or shorthand:\nSELECT * FROM orders WHERE metadata->>'$.source' = 'web';"
      },
      {
        "title": "Joins",
        "body": "-- Inner join (only matching rows)\nSELECT u.name, o.total, o.status\nFROM users u\nINNER JOIN orders o ON o.user_id = u.id\nWHERE o.created_at > '2026-01-01';\n\n-- Left join (all users, even without orders)\nSELECT u.name, COUNT(o.id) AS order_count, COALESCE(SUM(o.total), 0) AS total_spent\nFROM users u\nLEFT JOIN orders o ON o.user_id = u.id\nGROUP BY u.id, u.name;\n\n-- Self-join (find users with same email domain)\nSELECT a.name, b.name, SPLIT_PART(a.email, '@', 2) AS domain\nFROM users a\nJOIN users b ON SPLIT_PART(a.email, '@', 2) = SPLIT_PART(b.email, '@', 2)\nWHERE a.id < b.id;"
      },
      {
        "title": "Aggregations",
        "body": "-- Group by with having\nSELECT status, COUNT(*) AS cnt, SUM(total) AS revenue\nFROM orders\nGROUP BY status\nHAVING COUNT(*) > 10\nORDER BY revenue DESC;\n\n-- Running total (window function)\nSELECT date, revenue,\n    SUM(revenue) OVER (ORDER BY date) AS cumulative_revenue\nFROM daily_sales;\n\n-- Rank within groups\nSELECT user_id, total,\n    RANK() OVER (PARTITION BY user_id ORDER BY total DESC) AS rank\nFROM orders;\n\n-- Moving average (last 7 entries)\nSELECT date, revenue,\n    AVG(revenue) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma_7\nFROM daily_sales;"
      },
      {
        "title": "Common Table Expressions (CTEs)",
        "body": "-- Readable multi-step queries\nWITH monthly_revenue AS (\n    SELECT DATE_TRUNC('month', created_at) AS month,\n           SUM(total) AS revenue\n    FROM orders\n    WHERE status = 'paid'\n    GROUP BY 1\n),\ngrowth AS (\n    SELECT month, revenue,\n           LAG(revenue) OVER (ORDER BY month) AS prev_revenue,\n           ROUND((revenue - LAG(revenue) OVER (ORDER BY month)) /\n                 NULLIF(LAG(revenue) OVER (ORDER BY month), 0) * 100, 1) AS growth_pct\n    FROM monthly_revenue\n)\nSELECT * FROM growth ORDER BY month;\n\n-- Recursive CTE (org chart / tree traversal)\nWITH RECURSIVE org_tree AS (\n    SELECT id, name, manager_id, 0 AS depth\n    FROM employees\n    WHERE manager_id IS NULL\n    UNION ALL\n    SELECT e.id, e.name, e.manager_id, t.depth + 1\n    FROM employees e\n    JOIN org_tree t ON e.manager_id = t.id\n)\nSELECT REPEAT('  ', depth) || name AS org_chart FROM org_tree ORDER BY depth, name;"
      },
      {
        "title": "Manual Migration Script Pattern",
        "body": "#!/bin/bash\n# migrate.sh - Run numbered SQL migration files\nDB_URL=\"${1:?Usage: migrate.sh <db-url>}\"\nMIGRATIONS_DIR=\"./migrations\"\n\n# Create tracking table\npsql \"$DB_URL\" -c \"CREATE TABLE IF NOT EXISTS schema_migrations (\n    version TEXT PRIMARY KEY,\n    applied_at TIMESTAMPTZ DEFAULT NOW()\n);\"\n\n# Run pending migrations in order\nfor file in $(ls \"$MIGRATIONS_DIR\"/*.sql | sort); do\n    version=$(basename \"$file\" .sql)\n    already=$(psql \"$DB_URL\" -tAc \"SELECT 1 FROM schema_migrations WHERE version='$version';\")\n    if [ \"$already\" = \"1\" ]; then\n        echo \"SKIP: $version (already applied)\"\n        continue\n    fi\n    echo \"APPLY: $version\"\n    psql \"$DB_URL\" -f \"$file\" && \\\n    psql \"$DB_URL\" -c \"INSERT INTO schema_migrations (version) VALUES ('$version');\" || {\n        echo \"FAILED: $version\"\n        exit 1\n    }\ndone\necho \"All migrations applied.\""
      },
      {
        "title": "Migration File Convention",
        "body": "migrations/\n  001_create_users.sql\n  002_create_orders.sql\n  003_add_users_phone.sql\n  004_add_orders_metadata_index.sql\n\nEach file:\n\n-- 003_add_users_phone.sql\n-- Up\nALTER TABLE users ADD COLUMN phone TEXT;\n\n-- To reverse: ALTER TABLE users DROP COLUMN phone;"
      },
      {
        "title": "EXPLAIN (PostgreSQL)",
        "body": "-- Show query plan\nEXPLAIN SELECT * FROM orders WHERE user_id = '...' AND status = 'paid';\n\n-- Show actual execution times\nEXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)\nSELECT * FROM orders WHERE user_id = '...' AND status = 'paid';\n\nWhat to look for:\n\nSeq Scan on large tables → needs an index\nNested Loop with large row counts → consider Hash Join (may need more work_mem)\nRows Removed by Filter being high → index doesn't cover the filter\nActual rows far from estimated → run ANALYZE tablename; to update statistics"
      },
      {
        "title": "Index Strategy",
        "body": "-- Single column (most common)\nCREATE INDEX idx_orders_user_id ON orders(user_id);\n\n-- Composite (for queries filtering on both columns)\nCREATE INDEX idx_orders_user_status ON orders(user_id, status);\n-- Column ORDER matters: put equality filters first, range filters last\n\n-- Covering index (includes data columns to avoid table lookup)\nCREATE INDEX idx_orders_covering ON orders(user_id, status) INCLUDE (total, created_at);\n\n-- Partial index (smaller, faster — only index what you query)\nCREATE INDEX idx_orders_pending ON orders(user_id) WHERE status = 'pending';\n\n-- Check unused indexes\nSELECT schemaname, tablename, indexname, idx_scan\nFROM pg_stat_user_indexes\nWHERE idx_scan = 0 AND indexname NOT LIKE '%pkey%'\nORDER BY pg_relation_size(indexrelid) DESC;"
      },
      {
        "title": "SQLite EXPLAIN",
        "body": "EXPLAIN QUERY PLAN SELECT * FROM orders WHERE user_id = 5;\n-- Look for: SCAN (bad) vs SEARCH USING INDEX (good)"
      },
      {
        "title": "PostgreSQL",
        "body": "# Full dump (custom format, compressed)\npg_dump -Fc -h localhost -U myuser mydb > backup.dump\n\n# Restore\npg_restore -h localhost -U myuser -d mydb --clean --if-exists backup.dump\n\n# SQL dump (portable, readable)\npg_dump -h localhost -U myuser mydb > backup.sql\n\n# Dump specific tables\npg_dump -h localhost -U myuser -t users -t orders mydb > partial.sql\n\n# Copy table to CSV\npsql -c \"\\copy (SELECT * FROM users) TO 'users.csv' CSV HEADER\""
      },
      {
        "title": "SQLite",
        "body": "# Backup (just copy the file, but use .backup for consistency)\nsqlite3 mydb.sqlite \".backup backup.sqlite\"\n\n# Dump to SQL\nsqlite3 mydb.sqlite .dump > backup.sql\n\n# Restore from SQL\nsqlite3 newdb.sqlite < backup.sql"
      },
      {
        "title": "MySQL",
        "body": "# Dump\nmysqldump -h localhost -u root -p mydb > backup.sql\n\n# Restore\nmysql -h localhost -u root -p mydb < backup.sql"
      },
      {
        "title": "Tips",
        "body": "Always use parameterized queries in application code — never concatenate user input into SQL\nUse TIMESTAMPTZ (not TIMESTAMP) in PostgreSQL for timezone-aware dates\nSet PRAGMA journal_mode=WAL; in SQLite for concurrent read performance\nUse EXPLAIN before deploying any query that runs on large tables\nPostgreSQL: \\d+ tablename shows columns, indexes, and size. \\di+ lists all indexes with sizes\nFor quick data exploration, import any CSV into SQLite: sqlite3 :memory: \".mode csv\" \".import file.csv t\" \"SELECT ...\""
      }
    ],
    "body": "SQL Toolkit\n\nWork with relational databases directly from the command line. Covers SQLite, PostgreSQL, and MySQL with patterns for schema design, querying, migrations, indexing, and operations.\n\nWhen to Use\nCreating or modifying database schemas\nWriting complex queries (joins, aggregations, window functions, CTEs)\nBuilding migration scripts\nOptimizing slow queries with indexes and EXPLAIN\nBacking up and restoring databases\nQuick data exploration with SQLite (zero setup)\nSQLite (Zero Setup)\n\nSQLite is included with Python and available on every system. Use it for local data, prototyping, and single-file databases.\n\nQuick Start\n# Create/open a database\nsqlite3 mydb.sqlite\n\n# Import CSV directly\nsqlite3 mydb.sqlite \".mode csv\" \".import data.csv mytable\" \"SELECT COUNT(*) FROM mytable;\"\n\n# One-liner queries\nsqlite3 mydb.sqlite \"SELECT * FROM users WHERE created_at > '2026-01-01' LIMIT 10;\"\n\n# Export to CSV\nsqlite3 -header -csv mydb.sqlite \"SELECT * FROM orders;\" > orders.csv\n\n# Interactive mode with headers and columns\nsqlite3 -header -column mydb.sqlite\n\nSchema Operations\n-- Create table\nCREATE TABLE users (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    email TEXT NOT NULL UNIQUE,\n    name TEXT NOT NULL,\n    created_at TEXT DEFAULT (datetime('now')),\n    updated_at TEXT DEFAULT (datetime('now'))\n);\n\n-- Create with foreign key\nCREATE TABLE orders (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n    total REAL NOT NULL CHECK(total >= 0),\n    status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','paid','shipped','cancelled')),\n    created_at TEXT DEFAULT (datetime('now'))\n);\n\n-- Add column\nALTER TABLE users ADD COLUMN phone TEXT;\n\n-- Create index\nCREATE INDEX idx_orders_user_id ON orders(user_id);\nCREATE UNIQUE INDEX idx_users_email ON users(email);\n\n-- View schema\n.schema users\n.tables\n\nPostgreSQL\nConnection\n# Connect\npsql -h localhost -U myuser -d mydb\n\n# Connection string\npsql \"postgresql://user:pass@localhost:5432/mydb?sslmode=require\"\n\n# Run single query\npsql -h localhost -U myuser -d mydb -c \"SELECT NOW();\"\n\n# Run SQL file\npsql -h localhost -U myuser -d mydb -f migration.sql\n\n# List databases\npsql -l\n\nSchema Design Patterns\n-- Use UUIDs for distributed-friendly primary keys\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\n\nCREATE TABLE users (\n    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),\n    email TEXT NOT NULL,\n    name TEXT NOT NULL,\n    password_hash TEXT NOT NULL,\n    role TEXT NOT NULL DEFAULT 'user' CHECK(role IN ('user','admin','moderator')),\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    CONSTRAINT users_email_unique UNIQUE(email)\n);\n\n-- Auto-update updated_at\nCREATE OR REPLACE FUNCTION update_modified_column()\nRETURNS TRIGGER AS $$\nBEGIN\n    NEW.updated_at = NOW();\n    RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n\nCREATE TRIGGER update_users_modtime\n    BEFORE UPDATE ON users\n    FOR EACH ROW EXECUTE FUNCTION update_modified_column();\n\n-- Enum type (PostgreSQL-specific)\nCREATE TYPE order_status AS ENUM ('pending', 'paid', 'shipped', 'delivered', 'cancelled');\n\nCREATE TABLE orders (\n    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),\n    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,\n    status order_status NOT NULL DEFAULT 'pending',\n    total NUMERIC(10,2) NOT NULL CHECK(total >= 0),\n    metadata JSONB DEFAULT '{}',\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\n-- Partial index (only index active orders — smaller, faster)\nCREATE INDEX idx_orders_active ON orders(user_id, created_at)\n    WHERE status NOT IN ('delivered', 'cancelled');\n\n-- GIN index for JSONB queries\nCREATE INDEX idx_orders_metadata ON orders USING GIN(metadata);\n\nJSONB Queries (PostgreSQL)\n-- Store JSON\nINSERT INTO orders (user_id, total, metadata)\nVALUES ('...', 99.99, '{\"source\": \"web\", \"coupon\": \"SAVE10\", \"items\": [{\"sku\": \"A1\", \"qty\": 2}]}');\n\n-- Query JSON fields\nSELECT * FROM orders WHERE metadata->>'source' = 'web';\nSELECT * FROM orders WHERE metadata->'items' @> '[{\"sku\": \"A1\"}]';\nSELECT metadata->>'coupon' AS coupon, COUNT(*) FROM orders GROUP BY 1;\n\n-- Update JSON field\nUPDATE orders SET metadata = jsonb_set(metadata, '{source}', '\"mobile\"') WHERE id = '...';\n\nMySQL\nConnection\nmysql -h localhost -u root -p mydb\nmysql -h localhost -u root -p -e \"SELECT NOW();\" mydb\n\nKey Differences from PostgreSQL\n-- Auto-increment (not SERIAL)\nCREATE TABLE users (\n    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n    email VARCHAR(255) NOT NULL UNIQUE,\n    name VARCHAR(255) NOT NULL,\n    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\n-- JSON type (MySQL 5.7+)\nCREATE TABLE orders (\n    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n    user_id BIGINT UNSIGNED NOT NULL,\n    metadata JSON,\n    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE\n);\n\n-- Query JSON\nSELECT * FROM orders WHERE JSON_EXTRACT(metadata, '$.source') = 'web';\n-- Or shorthand:\nSELECT * FROM orders WHERE metadata->>'$.source' = 'web';\n\nQuery Patterns\nJoins\n-- Inner join (only matching rows)\nSELECT u.name, o.total, o.status\nFROM users u\nINNER JOIN orders o ON o.user_id = u.id\nWHERE o.created_at > '2026-01-01';\n\n-- Left join (all users, even without orders)\nSELECT u.name, COUNT(o.id) AS order_count, COALESCE(SUM(o.total), 0) AS total_spent\nFROM users u\nLEFT JOIN orders o ON o.user_id = u.id\nGROUP BY u.id, u.name;\n\n-- Self-join (find users with same email domain)\nSELECT a.name, b.name, SPLIT_PART(a.email, '@', 2) AS domain\nFROM users a\nJOIN users b ON SPLIT_PART(a.email, '@', 2) = SPLIT_PART(b.email, '@', 2)\nWHERE a.id < b.id;\n\nAggregations\n-- Group by with having\nSELECT status, COUNT(*) AS cnt, SUM(total) AS revenue\nFROM orders\nGROUP BY status\nHAVING COUNT(*) > 10\nORDER BY revenue DESC;\n\n-- Running total (window function)\nSELECT date, revenue,\n    SUM(revenue) OVER (ORDER BY date) AS cumulative_revenue\nFROM daily_sales;\n\n-- Rank within groups\nSELECT user_id, total,\n    RANK() OVER (PARTITION BY user_id ORDER BY total DESC) AS rank\nFROM orders;\n\n-- Moving average (last 7 entries)\nSELECT date, revenue,\n    AVG(revenue) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS ma_7\nFROM daily_sales;\n\nCommon Table Expressions (CTEs)\n-- Readable multi-step queries\nWITH monthly_revenue AS (\n    SELECT DATE_TRUNC('month', created_at) AS month,\n           SUM(total) AS revenue\n    FROM orders\n    WHERE status = 'paid'\n    GROUP BY 1\n),\ngrowth AS (\n    SELECT month, revenue,\n           LAG(revenue) OVER (ORDER BY month) AS prev_revenue,\n           ROUND((revenue - LAG(revenue) OVER (ORDER BY month)) /\n                 NULLIF(LAG(revenue) OVER (ORDER BY month), 0) * 100, 1) AS growth_pct\n    FROM monthly_revenue\n)\nSELECT * FROM growth ORDER BY month;\n\n-- Recursive CTE (org chart / tree traversal)\nWITH RECURSIVE org_tree AS (\n    SELECT id, name, manager_id, 0 AS depth\n    FROM employees\n    WHERE manager_id IS NULL\n    UNION ALL\n    SELECT e.id, e.name, e.manager_id, t.depth + 1\n    FROM employees e\n    JOIN org_tree t ON e.manager_id = t.id\n)\nSELECT REPEAT('  ', depth) || name AS org_chart FROM org_tree ORDER BY depth, name;\n\nMigrations\nManual Migration Script Pattern\n#!/bin/bash\n# migrate.sh - Run numbered SQL migration files\nDB_URL=\"${1:?Usage: migrate.sh <db-url>}\"\nMIGRATIONS_DIR=\"./migrations\"\n\n# Create tracking table\npsql \"$DB_URL\" -c \"CREATE TABLE IF NOT EXISTS schema_migrations (\n    version TEXT PRIMARY KEY,\n    applied_at TIMESTAMPTZ DEFAULT NOW()\n);\"\n\n# Run pending migrations in order\nfor file in $(ls \"$MIGRATIONS_DIR\"/*.sql | sort); do\n    version=$(basename \"$file\" .sql)\n    already=$(psql \"$DB_URL\" -tAc \"SELECT 1 FROM schema_migrations WHERE version='$version';\")\n    if [ \"$already\" = \"1\" ]; then\n        echo \"SKIP: $version (already applied)\"\n        continue\n    fi\n    echo \"APPLY: $version\"\n    psql \"$DB_URL\" -f \"$file\" && \\\n    psql \"$DB_URL\" -c \"INSERT INTO schema_migrations (version) VALUES ('$version');\" || {\n        echo \"FAILED: $version\"\n        exit 1\n    }\ndone\necho \"All migrations applied.\"\n\nMigration File Convention\nmigrations/\n  001_create_users.sql\n  002_create_orders.sql\n  003_add_users_phone.sql\n  004_add_orders_metadata_index.sql\n\n\nEach file:\n\n-- 003_add_users_phone.sql\n-- Up\nALTER TABLE users ADD COLUMN phone TEXT;\n\n-- To reverse: ALTER TABLE users DROP COLUMN phone;\n\nQuery Optimization\nEXPLAIN (PostgreSQL)\n-- Show query plan\nEXPLAIN SELECT * FROM orders WHERE user_id = '...' AND status = 'paid';\n\n-- Show actual execution times\nEXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)\nSELECT * FROM orders WHERE user_id = '...' AND status = 'paid';\n\n\nWhat to look for:\n\nSeq Scan on large tables → needs an index\nNested Loop with large row counts → consider Hash Join (may need more work_mem)\nRows Removed by Filter being high → index doesn't cover the filter\nActual rows far from estimated → run ANALYZE tablename; to update statistics\nIndex Strategy\n-- Single column (most common)\nCREATE INDEX idx_orders_user_id ON orders(user_id);\n\n-- Composite (for queries filtering on both columns)\nCREATE INDEX idx_orders_user_status ON orders(user_id, status);\n-- Column ORDER matters: put equality filters first, range filters last\n\n-- Covering index (includes data columns to avoid table lookup)\nCREATE INDEX idx_orders_covering ON orders(user_id, status) INCLUDE (total, created_at);\n\n-- Partial index (smaller, faster — only index what you query)\nCREATE INDEX idx_orders_pending ON orders(user_id) WHERE status = 'pending';\n\n-- Check unused indexes\nSELECT schemaname, tablename, indexname, idx_scan\nFROM pg_stat_user_indexes\nWHERE idx_scan = 0 AND indexname NOT LIKE '%pkey%'\nORDER BY pg_relation_size(indexrelid) DESC;\n\nSQLite EXPLAIN\nEXPLAIN QUERY PLAN SELECT * FROM orders WHERE user_id = 5;\n-- Look for: SCAN (bad) vs SEARCH USING INDEX (good)\n\nBackup & Restore\nPostgreSQL\n# Full dump (custom format, compressed)\npg_dump -Fc -h localhost -U myuser mydb > backup.dump\n\n# Restore\npg_restore -h localhost -U myuser -d mydb --clean --if-exists backup.dump\n\n# SQL dump (portable, readable)\npg_dump -h localhost -U myuser mydb > backup.sql\n\n# Dump specific tables\npg_dump -h localhost -U myuser -t users -t orders mydb > partial.sql\n\n# Copy table to CSV\npsql -c \"\\copy (SELECT * FROM users) TO 'users.csv' CSV HEADER\"\n\nSQLite\n# Backup (just copy the file, but use .backup for consistency)\nsqlite3 mydb.sqlite \".backup backup.sqlite\"\n\n# Dump to SQL\nsqlite3 mydb.sqlite .dump > backup.sql\n\n# Restore from SQL\nsqlite3 newdb.sqlite < backup.sql\n\nMySQL\n# Dump\nmysqldump -h localhost -u root -p mydb > backup.sql\n\n# Restore\nmysql -h localhost -u root -p mydb < backup.sql\n\nTips\nAlways use parameterized queries in application code — never concatenate user input into SQL\nUse TIMESTAMPTZ (not TIMESTAMP) in PostgreSQL for timezone-aware dates\nSet PRAGMA journal_mode=WAL; in SQLite for concurrent read performance\nUse EXPLAIN before deploying any query that runs on large tables\nPostgreSQL: \\d+ tablename shows columns, indexes, and size. \\di+ lists all indexes with sizes\nFor quick data exploration, import any CSV into SQLite: sqlite3 :memory: \".mode csv\" \".import file.csv t\" \"SELECT ...\""
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/gitgoodordietrying/sql-toolkit",
    "publisherUrl": "https://clawhub.ai/gitgoodordietrying/sql-toolkit",
    "owner": "gitgoodordietrying",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/sql-toolkit",
    "downloadUrl": "https://openagent3.xyz/downloads/sql-toolkit",
    "agentUrl": "https://openagent3.xyz/skills/sql-toolkit/agent",
    "manifestUrl": "https://openagent3.xyz/skills/sql-toolkit/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/sql-toolkit/agent.md"
  }
}