4

我想用它来构造unique_key。但我不想明确提供模式名称。有什么办法可以弄清楚吗?

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
BEGIN
   RETURN NOT EXISTS (
      SELECT header_id, value
      FROM myschema.mytable
      WHERE value LIKE _value AND header_id != _header_id
      LIMIT 1);
END;
$$ LANGUAGE plpgsql;

ALTER TABLE mytable
  ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value))

如果我这样做,它必须工作:

set search_path = schema1;

CREATE FUNCTION AS ABOVE

set search_path = schema2;

INSERT INTO schema1.mytable(id, header_id, value)
VALUES (1,1,'a');  -- Should be ok

INSERT INTO schema1.mytable(id, header_id, value)
VALUES (1,2,'a');  -- Should violate error

考虑具有相同表结构的多个模式。

Edt: 2012.05.16-1533 例如考虑这个表: Header_table: id serial Snapshot_table: id serial, header_id int (FK to header table), value varchar

id               id | header_id | value
 1                1 |         1 | a
 2                2 |         1 | a
 3                3 |         2 | b
                  4 |         3 | b

等 header_id 为 3 的记录将违反 unique_key

这是指:对一列的唯一约束,不包括在另一列中具有相同值的行

Edt:20120516-2356 添加一些测试脚本(对不起,我不知道如何加入文件)

drop schema if exists s1 cascade;
drop schema if exists s2 cascade;
drop schema if exists tp cascade; 

create schema s1;
create schema s2;
create schema tp; -- Temporary schema to hold insert function

-- SCHEMA 1 --

set search_path = s1;

-- * * * header table * * * 

CREATE TABLE th1 (
    id SERIAL NOT NULL,
    constraint th1_pk_id PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

-- * * * snapshot table * * *

CREATE TABLE ts1 (
    id SERIAL NOT NULL,
    header_id INTEGER NOT NULL,
    version INTEGER NOT NULL,
    value VARCHAR NOT NULL,
    CONSTRAINT ts1_pk_id PRIMARY KEY (id),
    CONSTRAINT ts1_header_id_fk FOREIGN KEY (header_id) REFERENCES th1(id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
)
WITH (
  OIDS=FALSE
);

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
DECLARE
   b boolean;
BEGIN
   RAISE INFO ' ---- CHECK UNIQUE FUNCTION ----';
   RAISE INFO 'Search_path: %', current_schema();
   RAISE INFO 'Function caled schema: %', 's1';
   SELECT NOT EXISTS (
      SELECT 1
      FROM ts1
      WHERE value LIKE _value AND header_id != _header_id
      LIMIT 1) INTO b;
   RAISE INFO 'Result: %', b;
   RETURN b;
END;
$$ LANGUAGE plpgsql;

ALTER TABLE ts1
  ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value));

CREATE OR REPLACE FUNCTION logSchema() RETURNS TRIGGER AS $$
BEGIN
    RAISE INFO 'Call on schema: %', 's1';
    RETURN new;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER logSchemaTrigger
BEFORE INSERT OR UPDATE ON ts1
FOR EACH ROW EXECUTE PROCEDURE logSchema();


-- SCHEMA2 --

set search_path = s2;

-- * * * header table * * * 

CREATE TABLE th1 (
    id SERIAL NOT NULL,
    constraint th1_pk_id PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

-- * * * snapshot table * * *

CREATE TABLE ts1 (
    id SERIAL NOT NULL,
    header_id INTEGER NOT NULL,
    version INTEGER NOT NULL,
    value VARCHAR NOT NULL,
    CONSTRAINT ts1_pk_id PRIMARY KEY (id),
    CONSTRAINT ts1_header_id_fk FOREIGN KEY (header_id) REFERENCES th1(id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
)
WITH (
  OIDS=FALSE
);

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
DECLARE
   b boolean;
BEGIN
   RAISE INFO ' ---- CHECK UNIQUE FUNCTION ----';
   RAISE INFO 'Search_path: %', current_schema();
   RAISE INFO 'Function caled schema: %', 's2';
   SELECT NOT EXISTS (
      SELECT 1
      FROM ts1
      WHERE value LIKE _value AND header_id != _header_id
      LIMIT 1) INTO b;
   RAISE INFO 'Result: %', b;
   RETURN b;
END;
$$ LANGUAGE plpgsql;

ALTER TABLE ts1
  ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value));

CREATE OR REPLACE FUNCTION logSchema() RETURNS TRIGGER AS $$
BEGIN
    RAISE INFO 'Actual schema: %', 's2';
    RETURN new;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER logSchemaTrigger
BEFORE INSERT OR UPDATE ON ts1
FOR EACH ROW EXECUTE PROCEDURE logSchema();

-- INSERT PREPARE DATA --

-- insert function to handle error
CREATE OR REPLACE FUNCTION tp.insert_data_test(_schemaname name, _header_id integer, _version integer, _value varchar)
  RETURNS VOID AS
$$
DECLARE
   schemaname name;
   b boolean;
BEGIN
   RAISE INFO '==== NEW INSERT COMMAND ====';
   SELECT _schemaname into schemaname;
   IF schemaname IS NULL THEN
       SELECT current_schema() INTO schemaname;
   end if;

   RAISE INFO 'Search_path: %', schemaname;
   RAISE INFO 'Inserting data: %, %, %', _header_id, _version, _value;

   BEGIN
      EXECUTE 'SELECT NOT EXISTS ( SELECT 1 FROM ' ||(select case when schemaname is null then 'null' else schemaname end)|| '.th1 WHERE id = ' || quote_literal(_header_id) || ' LIMIT 1)' INTO b;
      IF (SELECT b) THEN      
          EXECUTE 'INSERT INTO ' || (select case when schemaname is null then 'null' else schemaname end) || '.th1 VALUES(' || quote_literal(_header_id) ||')';
      END IF;

      EXECUTE 'INSERT INTO ' || (select case when schemaname is null then 'null' else schemaname end) || '.ts1(header_id, version, value) VALUES( ' || quote_literal(_header_id) || ',' || quote_literal(_version) ||','|| quote_literal(_value) ||')';
   EXCEPTION WHEN others THEN
      RAISE INFO 'Insert error: % %', SQLERRM, SQLSTATE;
      RETURN;
   END;

   RAISE INFO 'Insert succesfull';
END;
$$ LANGUAGE plpgsql;

-- Wiht no current schema --
set search_path = default;

SELECT tp.insert_data_test('s1',1,0,'a');
SELECT tp.insert_data_test('s2',1,0,'b');

/* IF you do this above with before set search_path its go fine, but if you do insert from newone query thats drop:
ERROR:  relation "ts1" does not exist
LINE 3:       FROM ts1
                   ^
QUERY:  SELECT NOT EXISTS (
      SELECT 1
      FROM ts1
      WHERE value LIKE _value AND header_id != _header_id
      LIMIT 1)
CONTEXT:  PL/pgSQL function "is_value_free" line 3 at RETURN

********** Chyba **********

ERROR: relation "ts1" does not exist
Stav SQL: 42P01
Kontext:PL/pgSQL function "is_value_free" line 3 at RETURN
*/

-- Insert data with corect schema --
set search_path = s1;
SELECT tp.insert_data_test(null,1,1,'a2');

set search_path = s2;
SELECT tp.insert_data_test(null,1,1,'b2');

-- Insert data with incorect schema --
set search_path = s2;
SELECT tp.insert_data_test('s1',1,2,'a3');

set search_path = s1;
SELECT tp.insert_data_test('s2',1,2,'b3');

-- TEST DATA (Should for all violate unique_key)--
-- Wiht no current schema --
set search_path = default;

SELECT tp.insert_data_test('s1',2,0,'a');
SELECT tp.insert_data_test('s2',2,0,'b');

-- Wiht corect current schema --
set search_path = s1;
SELECT tp.insert_data_test(null,3,0,'a');

set search_path = s2;
SELECT tp.insert_data_test(null,3,0,'b');

-- Wiht incorect current schema --
set search_path = s2;
SELECT tp.insert_data_test('s1',4,0,'a');

set search_path = s1;
SELECT tp.insert_data_test('s2',4,0,'b');

drop schema if exists s1 cascade;
drop schema if exists s2 cascade;
drop schema if exists tp cascade; 

EDT:2012.05.28:1937 我托盘它并在数据库中找到了这个(我以前没有看到)

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
   RETURNS BOOLEAN AS
   $BODY$
   BEGIN
      RETURN SELECT  NOT EXISTS 
            (
            SELECT  NULL
            FROM    mytable
            WHERE   value LIKE $2
                    AND header_id != $1
            );
   END;
   $BODY$
   LANGUAGE plpgsql VOLATILE
   COST 100;

   ALTER FUNCTION schema1.is_value_free(integer, character varying) SET search_path=schema1, public;

但是在创建数据库函数并使用 postgre 函数从数据库中获取定义时我需要使用相同的代码(如备份 ...)

4

1 回答 1

4
CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
        SELECT  NOT EXISTS 
                (
                SELECT  NULL
                FROM    mytable
                WHERE   value LIKE $2
                        AND header_id != $1
                );
$$ LANGUAGE sql
SET search_path FROM CURRENT;

因为不需要EXISTS半连接,并且这些项目是不相关的。LIMIT 1SELECT

请注意,您的唯一函数不是可交换的,因此表内容将取决于插入记录的顺序。

更新:

DROP SCHEMA IF EXISTS s1 CASCADE;
DROP SCHEMA IF EXISTS s2 CASCADE;
CREATE SCHEMA s1;
CREATE SCHEMA s2;

SET search_path = s1;

DROP TABLE IF EXISTS mytable;

CREATE TABLE mytable (id bigserial primary key, header_id integer not null, value text);

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar)
  RETURNS BOOLEAN AS
$$
        SELECT  NOT EXISTS 
                (
                SELECT  NULL
                FROM    mytable
                WHERE   value LIKE $2
                        AND header_id != $1
                );
$$ LANGUAGE sql
-- Below is the most important clause
SET search_path FROM CURRENT;

ALTER TABLE mytable
  ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value));

SET search_path = s2;

DELETE
FROM    s1.mytable;

INSERT INTO s1.mytable(id, header_id, value)
VALUES (1,1,'a');  -- Should be ok

INSERT INTO s1.mytable(id, header_id, value)
VALUES (2,2,'a');  -- Should violate error
于 2012-05-16T12:13:02.617 回答