暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

OpenAI 大战 DeepSeek: 设计美国社保系统的数据库结构

Bytebase 2025-02-20
101

原文地址:https://www.bytebase.com/blog/openai-vs-deepseek-ssn-database-schema-design

https://x.com/elonmusk/status/1888484555092312466

马斯克关于 SSN 数据库去重的评论在开发者群体中引发了巨大的争论,许多软件工程师对他的言论不屑一顾。让我们看看 OpenAI 和 DeepSeek 是如何设计这个 SSN 数据库 schema 的。

什么是 SSN?‍‍‍‍

给不在美国生活的读者介绍一下 -- SSN 就是社会安全号码,一个用于识别美国境内个人身份的九位数字。它的主要用途有:

  • 收入识别:雇主使用 SSN 向政府报告雇员的工资和薪金,确保收入得到正确记录。

  • 收税:美国国税局要求在报税(包括所得税申报)时使用 SSN,以跟踪个人纳税义务和预扣税款情况。

  • 社会福利分配:政府机构使用 SSN 确定资格并分配福利(如社会保障、医疗保险、医疗补助和失业救济)。

  • 信用评分:放款人、银行和信用局使用 SSN 跟踪个人的信用记录、贷款申请和债务偿还行为,从而形成信用评分的基础。

在美国,SSN 是高度敏感的信息。有这样一种说法:结婚誓言无法证明真爱;信任伴侣到能分享 SSN 的程度,才能证明是真爱。

第一回合

提示词:

    You are a relational database schema designer. You are given the task to design
    the schema for the United States' Social Security Number (SSN) database.
    The database needs to track basic personal information and payment profile.


    Minimal design, no whistles and bells. Generate Postgres SQL dialect.

    你是一名关系数据库 schema 设计员。你的任务是设计美国社会保障号(SSN)数据库的 schema。该数据库需要跟踪个人基本信息和支付情况。


    最小化设计,不要花里胡哨。使用 Postgres SQL 语言生成代码。

    ChatGPT 4o

      CREATE TABLE person (
      -- SSN is always 9 digits (without dashes)
      ssn CHAR(9) PRIMARY KEY,
      first_name VARCHAR(50) NOT NULL,
      last_name VARCHAR(50) NOT NULL,
      date_of_birth DATE NOT NULL,
      -- M: Male, F: Female, O: Other
      gender CHAR(1) CHECK (gender IN ('M', 'F', 'O')),
      address TEXT NOT NULL
      );


      CREATE TABLE payment_profile (
      ssn CHAR(9) PRIMARY KEY REFERENCES person(ssn) ON DELETE CASCADE,
      -- Basic validation for routing number
      bank_routing_number CHAR(9) NOT NULL CHECK (bank_routing_number ~ '^[0-9]{9}$'),
      bank_account_number VARCHAR(20) NOT NULL,
      payment_method VARCHAR(10) CHECK (payment_method IN ('ACH', 'Check')) NOT NULL,
      last_payment_date DATE
      );


      CREATE INDEX idx_person_last_name ON person(last_name);
      CREATE INDEX idx_payment_method ON payment_profile(payment_method);
      ChatGPT 解释了设计思路:
      • person.ssn 是主键,确保唯一性。

      • person.date_of_birth 确保有效的日期输入。

      • person.gender 是单字符字段,有约束条件(M、F、O)。

      • payment_profile(付款档案)引用 person.ssn(ON DELETE CASCADE,确保在删除某人时删除付款档案)。

      • payment_profile.bank_routing_number(银行路由号码)有一个 regex 约束,以确保它正好包含 9 位数字。

      • payment_profile.payment_method(付款方式)只允许使用 ACH 或支票。

      • 关于 last_namepayment_method 的索引优化了常见的查找。

      ChatGPT 将性别设计为非二元选项,使用 O 表示其他性别,令人惊喜。

      DeepSeek R1

        -- Table to store personal information
        CREATE TABLE person (
        ssn CHAR(9) PRIMARY KEY, -- Social Security Number (9 digits, no hyphens)
        first_name VARCHAR(50) NOT NULL, -- First name
        last_name VARCHAR(50) NOT NULL, -- Last name
        date_of_birth DATE NOT NULL, -- Date of birth
        gender CHAR(1) NOT NULL, -- Gender (M/F/O for Male, Female, Other)
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Record creation timestamp
        );


        -- Table to store payment profiles
        CREATE TABLE payment_profile (
        id SERIAL PRIMARY KEY, -- Unique payment profile ID
        ssn CHAR(9) NOT NULL REFERENCES person(ssn) ON DELETE CASCADE, -- Foreign key to person
        total_contributions DECIMAL(12, 2) DEFAULT 0.00, -- Total contributions made
        last_payment_date DATE, -- Date of the last payment
        last_payment_amount DECIMAL(10, 2), -- Amount of the last payment
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Record creation timestamp
        );
        DeepSeek 设计了与 ChatGPT 类似的 schema。DeepSeek 没有解释设计选择,只是对列进行了注释。

        DeepSeek 的一个缺点是没有使用列检查来确保数据的有效性。

        第二回合 - 改名者的新账户‍‍‍‍

        提示词:

          A person can change the name (e.g. after marriage)

          个人可以更改姓名(例如:婚后改姓)

          ChatGPT 4o

            CREATE TABLE person (
            ssn CHAR(9) PRIMARY KEY, -- Immutable identifier
            date_of_birth DATE NOT NULL,
            gender CHAR(1) CHECK (gender IN ('M', 'F', 'O')), -- M: Male, F: Female, O: Other
            address TEXT NOT NULL
            );


            CREATE TABLE person_name (
            id SERIAL PRIMARY KEY,
            ssn CHAR(9) NOT NULL REFERENCES person(ssn) ON DELETE CASCADE,
            first_name VARCHAR(50) NOT NULL,
            last_name VARCHAR(50) NOT NULL,
            is_current BOOLEAN NOT NULL DEFAULT TRUE, -- Tracks the current name
            changed_at TIMESTAMP DEFAULT NOW() -- Records when the name was changed
            );


            CREATE TABLE payment_profile (
            ssn CHAR(9) PRIMARY KEY REFERENCES person(ssn) ON DELETE CASCADE,
            bank_routing_number CHAR(9) NOT NULL CHECK (bank_routing_number ~ '^[0-9]{9}$'), -- 9-digit validation
            bank_account_number VARCHAR(20) NOT NULL,
            payment_method VARCHAR(10) CHECK (payment_method IN ('ACH', 'Check')) NOT NULL,
            last_payment_date DATE
            );


            -- Ensure only one current name per person
            CREATE UNIQUE INDEX idx_person_name_unique ON person_name(ssn) WHERE is_current = TRUE;


            CREATE INDEX idx_person_name_last_name ON person_name(last_name);
            CREATE INDEX idx_payment_method ON payment_profile(payment_method);
            代码更改及理由:
            1. 将姓名分到 person_name 中:
              • 允许每个人有多个历史姓名。
              • 使用 is_current 追踪当前与过去的姓名(使用唯一索引强制执行)。
              • 使用 changed_at 记录姓名更改的时间。

            2. 保持 SSN 不变:
              • person 表存储不会改变的属性(SSN、出生日期、性别、地址)。

              • 姓名历史记录单独处理。
            3. 索引增强:
              • idx_person_name_unique 可确保每个 SSN 只有一条 is_current = TRUE 记录。

              • idx_person_name_last_name 可以有效地按姓氏进行搜索。

            DeepSeek R1
              -- Table to store personal information
              CREATE TABLE person (
              ssn CHAR(9) PRIMARY KEY, -- Social Security Number (9 digits, no hyphens)
              first_name VARCHAR(50) NOT NULL, -- Current first name
              last_name VARCHAR(50) NOT NULL, -- Current last name
              date_of_birth DATE NOT NULL, -- Date of birth
              gender CHAR(1) NOT NULL, -- Gender (M/F/O for Male, Female, Other)
              created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Record creation timestamp
              );


              -- Table to store historical name changes
              CREATE TABLE name_history (
              id SERIAL PRIMARY KEY, -- Unique identifier for each name change record
              ssn CHAR(9) NOT NULL REFERENCES person(ssn) ON DELETE CASCADE, -- Foreign key to person
              first_name VARCHAR(50) NOT NULL, -- First name at the time of the change
              last_name VARCHAR(50) NOT NULL, -- Last name at the time of the change
              change_date DATE NOT NULL, -- Date of the name change
              created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Record creation timestamp
              );


              -- Table to store payment profiles
              CREATE TABLE payment_profile (
              id SERIAL PRIMARY KEY, -- Unique payment profile ID
              ssn CHAR(9) NOT NULL REFERENCES person(ssn) ON DELETE CASCADE, -- Foreign key to person
              total_contributions DECIMAL(12, 2) DEFAULT 0.00, -- Total contributions made
              last_payment_date DATE, -- Date of the last payment
              last_payment_amount DECIMAL(10, 2), -- Amount of the last payment
              created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Record creation timestamp
              );

              DeepSeek 这次解释了设计的优势:

              • 当前姓名:person 表总是反映最新的姓名。
              • 历史跟踪:name_history 表跟踪所有姓名随时间发生的变化。
              • 数据完整性:外键和 ON DELETE CASCADE 确保了数据的一致性。

              DeepSeek 在这里作出了不同的设计选择:person 表仍然保留 first_namelast_name 列,只是被重新用于存储当前姓名。

              第三回合 - 添加代理密钥

              提示词:

                Using SSN as the primary key could expose security risk as SSN is sensitive.
                使用 SSN 作为主键可能会暴露安全风险,因为 SSN 是敏感信息。

                ChatGPT 4o

                  CREATE TABLE person (
                  person_id SERIAL PRIMARY KEY, -- Surrogate key for security
                  ssn CHAR(9) UNIQUE NOT NULL CHECK (ssn ~ '^[0-9]{9}$'), -- Enforced uniqueness and validation
                  date_of_birth DATE NOT NULL,
                  gender CHAR(1) CHECK (gender IN ('M', 'F', 'O')), -- M: Male, F: Female, O: Other
                  address TEXT NOT NULL
                  );


                  CREATE TABLE person_name (
                  id SERIAL PRIMARY KEY,
                  person_id INT NOT NULL REFERENCES person(person_id) ON DELETE CASCADE,
                  first_name VARCHAR(50) NOT NULL,
                  last_name VARCHAR(50) NOT NULL,
                  is_current BOOLEAN NOT NULL DEFAULT TRUE, -- Tracks the current name
                  changed_at TIMESTAMP DEFAULT NOW() -- Records when the name was changed
                  );


                  CREATE TABLE payment_profile (
                  person_id INT PRIMARY KEY REFERENCES person(person_id) ON DELETE CASCADE,
                  bank_routing_number CHAR(9) NOT NULL CHECK (bank_routing_number ~ '^[0-9]{9}$'), -- 9-digit validation
                  bank_account_number VARCHAR(20) NOT NULL,
                  payment_method VARCHAR(10) CHECK (payment_method IN ('ACH', 'Check')) NOT NULL,
                  last_payment_date DATE
                  );


                  -- Ensure only one current name per person
                  CREATE UNIQUE INDEX idx_person_name_unique ON person_name(person_id) WHERE is_current = TRUE;


                  CREATE INDEX idx_person_name_last_name ON person_name(last_name);
                  CREATE INDEX idx_payment_method ON payment_profile(payment_method);
                  改进安全性后的最优解:
                  1. 代理主键(person_id):
                    • 取代 ssn 作为主键,降低暴露风险。

                    • 使外键关系参考 person_id,而不是 ssn
                  2. SSN 保持唯一并经过验证:
                    • UNIQUE NOT NULL 约束确保没有重复。

                    • 正则表达式约束(CHECK (ssn ~ '^[0-9]{9}$')) 确保格式有效。
                  3. 外键使用 person_id:
                    • person_namepayment_profile 引用 person_id 而不是 ssn

                    • 避免在多个地方暴露 SSN。

                  DeepSeek R1

                    -- Table to store personal information
                    CREATE TABLE person (
                    id SERIAL PRIMARY KEY, -- Surrogate key (auto-incrementing integer)
                    ssn CHAR(9) NOT NULL UNIQUE, -- Social Security Number (9 digits, no hyphens)
                    first_name VARCHAR(50) NOT NULL, -- Current first name
                    last_name VARCHAR(50) NOT NULL, -- Current last name
                    date_of_birth DATE NOT NULL, -- Date of birth
                    gender CHAR(1) NOT NULL, -- Gender (M/F/O for Male, Female, Other)
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Record creation timestamp
                    );


                    -- Table to store historical name changes
                    CREATE TABLE name_history (
                    id SERIAL PRIMARY KEY, -- Unique identifier for each name change record
                    person_id INT NOT NULL REFERENCES person(id) ON DELETE CASCADE, -- Foreign key to person
                    first_name VARCHAR(50) NOT NULL, -- First name at the time of the change
                    last_name VARCHAR(50) NOT NULL, -- Last name at the time of the change
                    change_date DATE NOT NULL, -- Date of the name change
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Record creation timestamp
                    );


                    -- Table to store payment profiles
                    CREATE TABLE payment_profile (
                    id SERIAL PRIMARY KEY, -- Unique payment profile ID
                    person_id INT NOT NULL REFERENCES person(id) ON DELETE CASCADE, -- Foreign key to person
                    total_contributions DECIMAL(12, 2) DEFAULT 0.00, -- Total contributions made
                    last_payment_date DATE, -- Date of the last payment
                    last_payment_amount DECIMAL(10, 2), -- Amount of the last payment
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Record creation timestamp
                    );

                    出于安全的重要更改:

                    1. person 表中的代理键(id):

                      • person 表现在使用自动递增整数(id)作为主键。
                      • ssn 列仍然存储,但不再是主键。它被标记为 UNIQUE,以确保没有两个人共享相同的 SSN。

                    2. 外键引用:

                      • name_history 表和 payment_profile 表现在使用 person_id(代理键)而不是 ssn 来引用人员表。

                    3. SSN 存储:

                      • SSN 列安全地存储在人员表中。在实际应用中,应考虑加密该列或使用其他安全措施(如散列或标记化)来保护敏感数据。

                    总结

                    ChatGPT 4o 和 DeepSeek 都能很好地完成 SSN schema 设计任务:
                    1. 它们最初都能设计出合理的 schema。

                    2. 它们都能根据后续提示更改设计。在上面的例子中,两者都能纳入姓名变更并引入代理密钥来解决安全问题。
                    ChatGPT 4o 的亮点:
                    • gender 列设计为非二元选项。

                    • 使用列检查确保数据有效性。

                    • 在经常被查询的列上包含索引。

                    • 解释设计选择并证明设计决策的合理性。
                    DeepSeek R1 的亮点:
                    • 我个人喜欢它精炼 person 表的设计,因为这样可以返回当前姓名,使查询更加方便。
                    • 解释简洁明了,切中要害。

                    写在最后的感想

                    如上所述,schema 设计涉及各种权衡。确定唯一 SSN 是否是一个好的设计选择需要视情境而定。即使是最先进的模型,也仍然需要人工指导才能生成高质量的 schema 设计,以适应现实世界中的使用情况(如姓名变更)并解决安全问题(如 SSN 暴露)。


                    马斯克炮轰政府数据库设计,被真程序员打脸

                    OpenAI vs DeepSeek:数据库诊断能力

                    Bytebase签约Dwango,助力B站弹幕文化鼻祖优化数据库变更及查询操作

                    Bytebase 3.4.0 - 更多功能开放给社区版

                    文章转载自Bytebase,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                    评论