2015-11-30 9 views
0

Я хотел бы создать ограничение, которое проверяет, является ли значение (из столбца «номинальная_значение»), классифицированное как «минимальное» в столбце «stats_type», равным или меньшим чем значение, классифицированное как «среднее», когда все значения в других столбцах равны. Другими словами, учитывая соответствующие кортежи, за исключением столбцов «oid», «stats_type» и «nominal_value», я хотел бы удостовериться, что значение «минимум» всегда равно или меньше значения, обозначенного как «среднее».Ограничение значений с разными метками, когда все остальные столбцы равны

Это трудно объяснить, так что я сделал пример ниже:

CREATE TABLE price (
    oid SERIAL NOT NULL, 
    product INTEGER NOT NULL, 
    territory INTEGER NOT NULL, 
    stats_type INTEGER NOT NULL, 
    year INTEGER NOT NULL, 
    nominal_value NUMERIC(6,2) NOT NULL, 
    data_source INTEGER NOT NULL, 
    CONSTRAINT pk_price PRIMARY KEY (oid), 
    CONSTRAINT price_1 UNIQUE (product, territory, stats_type, year, data_source), 
    CONSTRAINT price_2 CHECK (nominal_value > 0) 
); 

INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (1, 55, 5611, 1, 2014, 120, 3); 
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (2, 55, 5611, 2, 2014, 160, 3); 
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (3, 55, 5615, 1, 2014, 60, 3); 
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (4, 55, 5611, 3, 2014, 180, 3); 
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (5, 62, 5615, 1, 2013, 1500, 3); 
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (6, 62, 5615, 2, 2013, 1300, 3); 

В '' stats_type метки: 1 = минимальное, 2 = средний, 3 = максимум.

Глядя на первые две строки; они равны, за исключением «oid», «stats_type» и «номинальная_значение»; поэтому я хотел бы проверить: это 120 (что помечено как минимум в первой строке) меньше или равно 160 (значение, обозначенное как среднее)? В этом случае ответ «да», поэтому он прошел проверку.

Теперь последние две строки (5 и 6) также имеют соответствие по продуктам столбцов, 'территории', 'года' и 'data_source'. Однако бывает, что номинальное значение 1500 в строке 5 не должно быть меньше 1300 в строке 6 (поскольку 1500 должно было быть минимальным, а 1300 - средним значением).

Как я могу это сделать? Можно ли выполнить эту задачу, используя ограничение 'check'?

+0

_I можно ли выполнить эту задачу, используя ограничение 'check'? _ No –

ответ

2

Этого нельзя сделать, используя ограничение проверки из-за необходимости запроса таблицы. Вы можете использовать триггер:

create or replace function price_trigger() 
returns trigger language plpgsql as $$ 
begin 
    if exists (
     select 1 
     from price p 
     where (p.product, p.territory, p.year, p.data_source) = 
       (new.product, new.territory, new.year, new.data_source) 
     and (
      p.stats_type < new.stats_type and p.nominal_value > new.nominal_value 
      or 
      p.stats_type > new.stats_type and p.nominal_value < new.nominal_value 
      ) 
     ) 
    then 
     raise exception 'Nominal value error'; 
    end if; 
    return new; 
end $$; 

create trigger price_trigger 
before insert or update on price 
for each row 
execute procedure price_trigger(); 

спускового крючок функцию проверяет, все условия (мин < < среднего максимума).

+1

Эта функция выполняет эту работу. Но в его сценарии последние две вставки были проблематичными, вы не знаете, была ли проблема со средним или минимальным значением. Триггер будет препятствовать последней вставке. Если он делает ввод в одном заявлении, он может использовать триггер с модификатором «FOR STATEMENT» вместо модификатора ROW. – caiohamamura

+0

Легко сделать функцию более подробной, но я сомневаюсь, что это необходимо. Очевидно, что функция ** не может использоваться ** как триггер уровня инструкции, потому что вам нужен доступ к вставленным (обновленным) значениям. – klin

0

Рассмотрите возможность использования функции postgresql для всех вставок/обновлений (или upserts) в таблице. Затем вы можете использовать логику case, чтобы определить, что может войти, и указать реальные коды возврата статуса API для неправильных событий входных данных. Вы можете написать его в PL/pgSQL или python или любом другом расширяемом языке в postgres, который вам удобнее.

ex.

CREATE OR REPLACE FUNCTION band.usp_band_add_update(
i_sess TEXT, 
i_ban_name TEXT, 
i_ban_email TEXT, 
i_ban_description TEXT, 
i_geo_id BIGINT 
) 
RETURNS TABLE(
band_id BIGINT, 
status_id INT, 
status_desc TEXT 
) 
LANGUAGE plpgsql 
AS $$ 
DECLARE 
_user BIGINT; 
_ban_id BIGINT; 
_status_id INT; 
_status_desc TEXT; 
_pass TEXT; 
BEGIN 

SELECT asr_user 
    INTO _user 
    FROM sess.usp_sess_check(i_sess); 

IF _user IS NOT NULL THEN 
    SELECT ban_id 
     FROM band.band 
    WHERE ban_user = _user 
     INTO _ban_id; 
    IF _ban_id IS NOT NULL THEN 
     UPDATE band.band 
      SET ban_name = i_ban_name, 
       ban_description = i_ban_description, 
       ban_geo_id = i_geo_id, 
       ban_modified = now() 
     WHERE ban_user = _user; 
     _status_id = 200; 
     _status_desc = 'ban_id updated: ' || _ban_id::text; 
    ELSE 
     INSERT INTO band.band (ban_user, ban_name, ban_email, ban_description, ban_geo_id) 
     VALUES (_user, i_ban_name, i_ban_email, i_ban_description, i_geo_id) 
     RETURNING ban_id INTO _ban_id; 
     _status_id = 200; 
     _status_desc = 'ban_id inserted: ' || _ban_id::text; 
    END IF; 
ELSE 
    _status_id = 402; 
    _status_desc = 'User does not exist.'; 
END IF; 
RETURN QUERY SELECT _ban_id, _status_id, _status_desc; 
END; 
$$; 
+0

Я не уверен, последовал ли я за вашим ответом. Вы привели пример, основанный на другом наборе данных, который у вас был? –

+0

есть. Ответ не в вашем формате данных. Это просто пример синтаксических целей. – Caullyn

1

Поскольку ваш сценарий имеет сложность, что вставки могут быть сделаны в любом порядке, я предлагаю вам выполнить вставки в средней таблице заранее, например, price_check.

Тогда вы могли бы поставить триггер для выполнения SQL, таких как:

SELECT pmin.* 
FROM price_check AS pmin 
JOIN price_check AS pavg 
    ON (pmin.product, pmin.territory, pmin.year, pmin.data_source) = (pavg.product, pavg.territory, pavg.year, pavg.data_source) 
     AND pmin.stats_type = 1 
     AND pavg.stats_type = 2 
     AND pmin.nominal_value <= pavg.nominal_value 

который будет возвращать допустимый минимум только и использовать это, чтобы выполнить вставку на финальном столе. Имейте в виду, что я использую INNER JOIN, поэтому минимум без средней пары не вернется, вам придется использовать LEFT JOIN для этого.

Аналогичным образом вы можете запрограммировать запросы на price_check помещать недопустимые строки в таблицу price_log, чтобы вручную проверять их последним.

В качестве альтернативы вы можете разместить триггер INSTEAD OF INSERT на своем столе, не требуя дополнительной таблицы, но даже при этом рекомендуется создать таблицу LOG для ручной проверки и исправления недействительных строк.

+1

Поскольку ваши вставки поэтапно и одно вставленное вами значение может быть проблематичным, я предлагаю вам использовать подход вторичной таблицы. Триггер обнаружит проблему только после того, как вы уже вставили 1-е и 2-е значения, например. – caiohamamura

Смежные вопросы