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_id3のレコードはunique_keyに違反します

これは、次のことを指します。他の同じ値を持つ行を除外した1つの列の一意性制約

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 1は必要なく、アイテムSELECTは無関係です。

一意の関数は可換ではないため、テーブルの内容はレコードが挿入される順序に依存することに注意してください。

アップデート:

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 に答える