将表列移动到新表并在 PostgreSQL 中作为外键引用

bcs*_*tos 2 ruby sql postgresql data-modeling sequel

假设我们有一个带有字段的数据库表

"id", "category", "subcategory", "brand", "name", "description", etc. 
Run Code Online (Sandbox Code Playgroud)

category,subcategorybrand 原始表中的相应列和行创建单独的表成为外键引用的好方法是什么 ?

概述所涉及的操作:

  • 获取原始表的每一列中应该成为外键的所有唯一值;
  • 为那些创建表
  • 在原始表(或副本)中创建外键引用列

在这种情况下,PostgreSQL 数据库是通过 Ruby 应用程序中的 Sequel 访问的,因此可用的接口是命令行、Sequel、PGAdmin 等...

问题:你会怎么做?

wil*_*ser 9

        -- Some test data
CREATE TABLE animals
        ( id SERIAL NOT NULL PRIMARY KEY
        , name varchar
        , category varchar
        , subcategory varchar
        );
INSERT INTO animals(name, category, subcategory) VALUES
 ( 'Chimpanzee' , 'mammals', 'apes' )
,( 'Urang Utang' , 'mammals', 'apes' )
,( 'Homo Sapiens' , 'mammals', 'apes' )
,( 'Mouse' , 'mammals', 'rodents' )
,( 'Rat' , 'mammals', 'rodents' )
        ;

        -- [empty] table to contain the "squeezed out" domain
CREATE TABLE categories
        ( id SERIAL NOT NULL PRIMARY KEY
        , category varchar
        , subcategory varchar
        , UNIQUE (category,subcategory)
        );

        -- The original table needs a "link" to the new table
ALTER TABLE animals
        ADD column category_id INTEGER -- NOT NULL
        REFERENCES categories(id)
        ;
        -- FK constraints are helped a lot by a supportive index.
CREATE INDEX animals_categories_fk ON animals (category_id);

        -- Chained query to:
        -- * populate the domain table
        -- * initialize the FK column in the original table
WITH ins AS (
        INSERT INTO categories(category, subcategory)
        SELECT DISTINCT a.category, a.subcategory
        FROM animals a
        RETURNING *
        )
UPDATE animals ani
SET category_id = ins.id
FROM ins
WHERE ins.category = ani.category
AND ins.subcategory = ani.subcategory
        ;

        -- Now that we have the FK pointing to the new table,
        -- we can drop the redundant columns.
ALTER TABLE animals DROP COLUMN category, DROP COLUMN subcategory;

        -- show it to the world
SELECT a.*
        , c.category, c.subcategory
FROM animals a
JOIN categories c ON c.id = a.category_id
        ;
Run Code Online (Sandbox Code Playgroud)

注意:片段:

哪里 ins.category = ani.category AND ins.subcategory = ani.subcategory

如果这些列包含 NULL,则会导致问题。最好使用它们进行比较

(ins.category,ins.subcategory) 与 (ani.category,ani.subcategory) 没有区别