Вставка PostgreSQL, которая зависит от данных в другой таблице, наилучшая практика? - PullRequest
3 голосов
/ 20 января 2010

Я недавно начал использовать PostgreSQL, и я стараюсь делать вещи «правильно», как я понял. Это означает, что на сервере базы данных должно быть как можно больше работы, а не на клиенте.

Итак, я создал функцию с PL / pgSQL, которая будет добавлять данные в таблицу. Поскольку для этой таблицы установлено ограничение первичного ключа, а указанный ключ может не существовать в то время, когда я пытаюсь добавить новые данные, я добавил перехват исключений, который создаст ключ, а затем попытается вставить снова новый ряд.

Это работает удовлетворительно для меня, но мне любопытно, справляюсь ли я с этим "правильным способом". Я пытался найти какое-то руководство по разработке этих пользовательских функций, но на самом деле я не нашел ничего полезного.

CREATE OR REPLACE FUNCTION add_product_price_promo_xml(v_product_code varchar, v_description varchar, v_product_group varchar,
                                                       v_mixmatch_id integer, v_price_at date, v_cost_price numeric, v_sales_price numeric,
                                                       v_tax_rate integer) RETURNS void AS $$
BEGIN
   INSERT INTO product_prices (product_code  , mixmatch_id  , price_at  , cost_price  , sales_price  , tax_rate) VALUES
                              (v_product_code, v_mixmatch_id, v_price_at, v_cost_price, v_sales_price, v_tax_rate);
EXCEPTION WHEN foreign_key_violation THEN
   INSERT INTO products (code, description, product_group) VALUES (v_product_code, v_description, v_product_group);
   PERFORM add_product_price_promo_xml($1, $2, $3, $4, $5, $6, $7, $8);
END;
$$ LANGUAGE plpgsql;

База данных, о которой идет речь, будет использоваться для составления отчетов и будет ежедневно импортировать полный реестр товаров с обновлениями цен и новых товаров, но я не буду знать, какие товары новые, а какие старые. *

Ответы [ 3 ]

4 голосов
/ 20 января 2010

Нет !!! WRONG WAY Извините, я использую postgresql в течение многих лет, и это плохая идея. Правильный способ - это (1) создать временную таблицу и (2) обновить там, где есть нарушение, (3) вставить там, где нет нарушения. Я покажу вам фрагмент, используя pg 8.4:

CREATE TEMP TABLE temp_table (
  LIKE table INCLUDING INDEXES INCLUDING CONSTRAINTS
);

Затем вы хотите вставить все свои вещи в temp_table и запустить эти две команды.

UPDATE table
SET a = t.a
FROM temp_table AS t
WHERE join-constraints;

INSERT INTO table
SELECT * FROM temp_table AS t
WHERE NOT EXISTS (
    SELECT * FROM table AS v
    WHERE ( join-constraints )
);

Вы можете сделать это в транзакции, если вам это нужно, и у вас достаточно памяти. Этот метод масштабируется, потому что внутренне он не создает огромное количество контрольных точек . Это также значительно быстрее. Ваш текущий путь был опубликован как подпрограмма псевдо-слияния на Varlena в 2006 , он укусил меня, и он укусил многих людей. Я не вижу в этом необходимости, поэтому предлагаю вам избежать этого.

0 голосов
/ 22 января 2010

Я думаю, что лучше написать код, который должен работать без исключений в обычном случае. Так как вы в любом случае находитесь в pl / pgSQL, почему бы не написать это как:

CREATE OR REPLACE FUNCTION add_product_price_promo_xml(v_product_code  varchar,
                                                       v_description   varchar,
                                                       v_product_group varchar, 
                                                       v_mixmatch_id integer,
                                                       v_price_at date,
                                                       v_cost_price numeric, 
                                                       v_sales_price numeric, 
                                                       v_tax_rate integer)
  RETURNS void AS $$ 
DECLARE
  n_count  numeric;
BEGIN
  SELECT COUNT(*)
    FROM products
    INTO n_count
    WHERE code = v_product_code;  -- or whatever the join criteria should be

  IF n_count = 0 THEN
    INSERT INTO products
      (code, description, product_group)
    VALUES
      (v_product_code, v_description, v_product_group);
  END IF;

  INSERT INTO product_prices
    (product_code, mixmatch_id, price_at,
     cost_price, sales_price, tax_rate)
  VALUES 
    (v_product_code, v_mixmatch_id, v_price_at,
     v_cost_price, v_sales_price, v_tax_rate); 
END; 
$$ LANGUAGE plpgsql; 

Согласно документации по pl / pgSQL, с блоком, у которого есть обработчик исключений, связано больше служебных данных, чем с блоком без обработчика, поэтому это может сэкономить незначительное количество служебных данных.

Делись и наслаждайся.

0 голосов
/ 20 января 2010

Вы на самом деле все в порядке.Я бы порекомендовал предоставить логическое возвращаемое значение для вашей функции для дополнительного спокойствия при работе с хранимой процедурой и ссылками на аргументы по имени, если только вы не планируете ориентироваться на более старые версии PostgreSQL (в этом случае вам понадобитсядобавить раздел DECLARE).

Я настоятельно рекомендую книгу Корри Дугласа PostgreSQL (Библиотека разработчика), в которой много материала о написании хранимых процедур в Pl / PgSQL.Я также рекомендую следовать порталу Planet PostgreSQL , который собирает сообщения в блогах от известных членов сообщества PostgreSQL, поскольку они очень часто обсуждают лучшие практики и хитрые приемы для решения сложных проблем с использованием Postgres.Удачи!

...