【问题标题】:Why can I not set this unique constraint in PostgreSQL?为什么我不能在 PostgreSQL 中设置这个唯一约束?
【发布时间】:2011-07-02 11:04:30
【问题描述】:

我不断得到:

SQL 错误:错误:无法创建 唯一索引 “service_import_checksum_key”详细信息: 密钥(校验和)=() 重复。

在声明中:

ALTER TABLE "public"."service_import" ADD CONSTRAINT "service_import_checksum_key" UNIQUE ("checksum")

但是这个约束不是重复的。在整个数据库中的任何地方都没有其他类似的约束,我不知道为什么它一直坚持认为它是重复的。我假设这是我在这里遗漏的 postgres 的一些奇怪的细微差别。

我做错了什么?

表转储:

--
-- PostgreSQL database dump
--

SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = off;
SET check_function_bodies = false;
SET client_min_messages = warning;
SET escape_string_warning = off;

SET search_path = public, pg_catalog;

SET default_tablespace = '';

SET default_with_oids = false;

--
-- Name: service_import; Type: TABLE; Schema: public; Owner: cvs_tar; Tablespace: 
--

CREATE TABLE service_import (
    id integer NOT NULL,
    name character varying(32) NOT NULL,
    importfile character varying(64) NOT NULL,
    reportfile character varying(64) NOT NULL,
    percent smallint NOT NULL,
    message text NOT NULL,
    stamp timestamp without time zone DEFAULT now() NOT NULL,
    complete smallint DEFAULT 0 NOT NULL,
    checksum character varying(40) NOT NULL
);


ALTER TABLE public.service_import OWNER TO cvs_tar;

--
-- Name: service_imports_id_seq; Type: SEQUENCE; Schema: public; Owner: cvs_tar
--

CREATE SEQUENCE service_imports_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


ALTER TABLE public.service_imports_id_seq OWNER TO cvs_tar;

--
-- Name: service_imports_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: cvs_tar
--

ALTER SEQUENCE service_imports_id_seq OWNED BY service_import.id;


--
-- Name: id; Type: DEFAULT; Schema: public; Owner: cvs_tar
--

ALTER TABLE service_import ALTER COLUMN id SET DEFAULT nextval('service_imports_id_seq'::regclass);


--
-- Name: service_import_name_key; Type: CONSTRAINT; Schema: public; Owner: cvs_tar; Tablespace: 
--

ALTER TABLE ONLY service_import
    ADD CONSTRAINT service_import_name_key UNIQUE (name);


--
-- Name: service_import_pkey; Type: CONSTRAINT; Schema: public; Owner: cvs_tar; Tablespace: 
--

ALTER TABLE ONLY service_import
    ADD CONSTRAINT service_import_pkey PRIMARY KEY (id);


--
-- Name: service_import_complete_idx; Type: INDEX; Schema: public; Owner: cvs_tar; Tablespace: 
--

CREATE INDEX service_import_complete_idx ON service_import USING btree (complete);


--
-- Name: service_import_stamp_idx; Type: INDEX; Schema: public; Owner: cvs_tar; Tablespace: 
--

CREATE INDEX service_import_stamp_idx ON service_import USING btree (stamp);


--
-- PostgreSQL database dump complete
--

【问题讨论】:

  • 您能否向我们展示完整的CREATE TABLE,或者提供表格的其他描述?另外,CREATE UNIQUE INDEX 可以代替吗?

标签: postgresql constraints unique-constraint unique-key


【解决方案1】:

再次阅读错误信息:

SQL 错误:错误:无法创建唯一索引“service_import_checksum_key” 详细信息:密钥 (checksum)=() 重复

看起来它告诉您checksum 列中有重复值,并且您正尝试使用您的约束对该列强制执行唯一性。约束不重复,数据有重复。

此外,“()”部分表示您在checksum 列中有多个空字符串。唯一约束允许多个NULL 值(因为NULL = NULLNULL,这是不正确的),但空字符串不是NULL

一个说明发生了什么的例子:

=> CREATE TABLE x (s VARCHAR NULL);
CREATE TABLE
=> INSERT INTO x (s) VALUES (''), ('a'), ('');
INSERT 0 3
=> ALTER TABLE x ADD CONSTRAINT ux UNIQUE(s);
NOTICE:  ALTER TABLE / ADD UNIQUE will create implicit index "ux" for table "x"
ERROR:  could not create unique index "ux"
DETAIL:  Key (s)=() is duplicated.
=> delete from x where s='';
DELETE 2
=> INSERT INTO x (s) VALUES ('a');
INSERT 0 1
=> ALTER TABLE x ADD CONSTRAINT ux UNIQUE(s);
NOTICE:  ALTER TABLE / ADD UNIQUE will create implicit index "ux" for table "x"
ERROR:  could not create unique index "ux"
DETAIL:  Key (s)=(a) is duplicated.

特别要注意 ERRORDETAIL 的含义,并将其与 INSERT 进行比较。

【讨论】:

  • 这是一个需要额外检查约束来阻止开发人员作弊的典型例子。该字段不是 NULL 而是未经检查的字符串,因此他们将空字符串用于规避 NOT NULL 约束。我希望在 SQL 标准中添加一个不允许“空”字符串(任何长度)的字符串类型。
  • @Matthew Wood:我会使用明确的 CHECK 约束来确保校验和具有完全所需的长度(不多也不少)并且只包含校验和的有效字符;校验和可能是一个固定大小的十六进制值,开发人员可以在必要时对其进行零填充。我们将把整个“空字符串为 NULL,因为 !empty_string 为真,而我的 ORM 太愚蠢,无法区分”的愚蠢留到另一天 :) 我不是 DBA,但我知道 '' 和 NULL 是不同的东西甚至在数据库之外。
  • 关于:“唯一约束允许多个 NULL 值(因为 NULL = NULL 为假)但空字符串不是 NULL。”叮!搞定了。都修好了。愚蠢的事情是在我创建列时自动填充空字符串,然后从那里添加索引是不可能的。
  • @Joshua Pech:ORM 试图让事情变得“简单”?哈!您可能希望将 CHECK 约束 (postgresql.org/docs/current/static/ddl-constraints.html#AEN2385) 添加到 checksum 以防止将来发生这种情况,一个简单的 LENGTH(checksum) > 0 应该可以解决问题。我倾向于尽可能限制我的数据:损坏的代码可以修复,损坏的数据是永久的。
  • 真的,只要它代表一个 sha1 总和,字符串就永远不应该是 40 个字符,所以我可能会这样做。此外,这不是一个 ORM 设置该字段,它是 phpPgAdmin。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-04
  • 1970-01-01
  • 2021-04-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多