【问题标题】:Prevent empty strings in CHARACTER VARYING field防止 CHARACTER VARYING 字段中出现空字符串
【发布时间】:2012-03-04 15:23:31
【问题描述】:

我正在使用 PostgreSQL,并希望阻止某些必需的 CHARACTER VARYING (VARCHAR) 字段允许空字符串输入。

这些字段还需要包含唯一值,所以我已经使用了唯一约束;但是,这不会阻止原始(唯一)空值。

基本示例,用户名必须唯一且不能为空

| id | username | password |
+----+----------+----------+
| 1  | User1    | pw1      | #Allowed
| 2  | User2    | pw1      | #Allowed
| 3  | User2    | pw2      | #Already prevented by constraint
| 4  | ''       | pw2      | #Currently allowed, but needs to be prevented

【问题讨论】:

    标签: postgresql constraints varchar


    【解决方案1】:

    使用check constraint

    CREATE TABLE foobar(
      x TEXT NOT NULL UNIQUE,
      CHECK (x <> '')
    );
    
    INSERT INTO foobar(x) VALUES('');
    

    【讨论】:

      【解决方案2】:

      您可以在定义表字段时使用标准 SQL 'CONSTRAINT...CHECK' 子句:

      CREATE TABLE test
      (
          nonempty VARCHAR NOT NULL UNIQUE CONSTRAINT non_empty CHECK(length(nonempty)>0)
      )
      

      【讨论】:

        【解决方案3】:

        作为一种特殊的约束,你可以把数据类型+约束放到一个DOMAIN中:

        -- set search_path='tmp';
        
        DROP DOMAIN birthdate CASCADE;
        CREATE DOMAIN birthdate AS date DEFAULT NULL
            CHECK (value >= '1900-01-01' AND value <= now())
            ;
        
        DROP DOMAIN username CASCADE;
        CREATE DOMAIN username AS VARCHAR NOT NULL
            CHECK (length(value) > 0)
            ;
        
        DROP TABLE employee CASCADE;
        CREATE TABLE employee
            ( empno INTEGER NOT NULL PRIMARY KEY
            , dob birthdate
            , zname username
            , UNIQUE (zname)
            );
        INSERT INTO employee(empno,dob,zname) 
          VALUES (1,'1980-02-02', 'John Doe' ), (2,'1980-02-02', 'Jon Doeh' );
        INSERT INTO employee(empno,dob,zname)
          VALUES (3,'1980-02-02', '' ), (4,'1980-01-01', 'Joan Doh' );
        

        这将允许您一次又一次地重用域,而不必每次都复制约束。


        -- 2021-03-25 更新(感谢 @AlexanderPavlov)

        Postgres 的实现似乎存在严重缺陷:可以插入 NULL from the results of an empty scalar subquery.

        下面的(无意义的)COALESCE()“修复”了这种行为。

        这允许我们将数据库置于禁止状态。


        \echo literal NULL
        
            INSERT INTO employee(empno,dob,zname) VALUES (5,'2021-02-02', NULL );
        
        \echo empty (scalar) set
        
            INSERT INTO employee(empno,dob,zname) VALUES (6,'2021-02-02', (select zname from employee where 1=0) );
        
        \echo empty COALESCE((scalar, NULL) ) set
        
            INSERT INTO employee(empno,dob,zname) VALUES (7,'2021-02-02', (select COALESCE(zname,NULL) from employee where 1=0) );
        
        \echo empty set#2
        
            INSERT INTO employee(empno,dob,zname) (select 8,'2021-03-03', zname from employee where 1=0 );
        
        \echo duplicate the complete table
        
            INSERT INTO employee(empno,dob,zname) (select 100+empno,dob+'1mon':: interval, upper(zname) from employee );
        
        select * from employee;
        

        额外结果:


        literal NULL
        ERROR:  domain username does not allow null values
        empty (scalar) set
        INSERT 0 1
        empty COALESCE((scalar, NULL) ) set
        ERROR:  domain username does not allow null values
        empty set#2
        INSERT 0 0
        duplicate the complete table
        ERROR:  domain username does not allow null values
         empno |    dob     |  zname   
        -------+------------+----------
             1 | 1980-02-02 | John Doe
             2 | 1980-02-02 | Jon Doeh
             6 | 2021-02-02 | 
        (3 rows)
        

        【讨论】:

        • PG 团队建议不要将NOT NULL 添加到域中,而是将其添加到列中,请参阅“注释”部分postgresql.org/docs/12/sql-createdomain.html
        • 感谢您的提示。 [不过,我确实认为 Postgres 的行为是错误的。不应允许在不可为空的列中输入 NULL。期间]
        • 没错,但现在我们必须接受它;)
        猜你喜欢
        • 1970-01-01
        • 2012-08-02
        • 1970-01-01
        • 2021-09-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-19
        • 1970-01-01
        相关资源
        最近更新 更多