2012-01-17 5 views
0

У меня есть таблица, в которой хранятся два внешних ключа, реализующих связь n: m.Запрос, возвращающий точное количество строк

Один из них указывает на человека (subject), а другой - на определенный предмет.
Теперь количество предметов, которые может быть у человека, указано в другой таблице, и мне нужен запрос, который возвращает столько же строк, сколько и количество предметов, которое может иметь человек.

Остальные записи могут быть заполнены знаками NULL или любым другим.

Это оказалось проблемой для решения этой проблемы со стороны приложения, поэтому я решил попробовать другой подход.

Edit: Примера

CREATE TABLE subject_items 
(
    sub_item integer NOT NULL, 
    sal_subject integer NOT NULL, 
    CONSTRAINT pkey PRIMARY KEY (sub_item, sal_subject), 
    CONSTRAINT fk1 FOREIGN KEY (sal_subject) 
     REFERENCES subject (sub_id) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE CASCADE, 
    CONSTRAINT fk2 FOREIGN KEY (sub_item) 
     REFERENCES item (item_id) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE CASCADE 
) 

мне нужен запрос/функция, которая будет возвращать все элементы объекта (объект может иметь 5 пунктов) но есть только три вещи, отнесенная к предмету.

Возврата будет несколько, как:

sub_item | sal_subject 
2   | 1 
3   | 1 
4   | 1 
NULL  | 1 
NULL  | 1 

Я использую PostgreSQL-8,3

+0

Не удалось разобраться в ситуации. Можете ли вы дать некоторые структуры таблиц и желаемый результат запроса? – Acn

+0

Ваша таблица возврата в конце вопроса должна содержать значение '1' во всех строках для' sal_subject', never 'NULL', чтобы иметь смысл. –

ответ

2

Рассмотрите это в значительной степени упрощенная версия вашего plpgsql функции. Должен работать в PostgreSQL 8.3 :

CREATE OR REPLACE FUNCTION x.fnk_abonemento_nariai(_prm_item integer) 
    RETURNS SETOF subject_items AS 
$BODY$ 
DECLARE 
    _kiek integer := num_records -- get number at declaration time 
         FROM subjekto_abonementai WHERE num_id = _prm_item; 
    _counter integer; 
BEGIN 

RETURN QUERY       -- get the records that actualy exist 
SELECT sub_item, sal_subject 
FROM sal_subject 
WHERE sub_item = prm_item; 

GET DIAGNOSTICS _counter = ROW_COUNT; -- save number of returned rows. 

RETURN QUERY 
SELECT NULL, NULL      -- fill the rest with null values 
FROM generate_series(_counter + 1, _kiek); 

END; 
$BODY$ LANGUAGE plpgsql VOLATILE STRICT; 

Подробнее о plpgsql in the manual (ссылка на версию 8.3).

+0

Спасибо за ваши ответы. Насколько мне понравился другой метод, я принимаю это только потому, что его легче читать/модифицировать. – ertx

+0

@ertx: С pg 8.3 я тоже выбрал бы этот. Чистый SQL является громоздким без функции окна 'row_number()' в этом случае. –

+0

Кроме того, я не знал, что переменные могут быть назначены после объявления путем выбора. Очень удобно. – ertx

0

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

CREATE OR REPLACE FUNCTION fnk_abonemento_nariai(prm_item integer) 
    RETURNS SETOF subject_items AS 
$BODY$DECLARE _kiek integer; 
DECLARE _rec subject_items; 
DECLARE _counter integer; 
BEGIN 
    /*get the number of records we need*/ 
    SELECT INTO _kiek num_records 
    FROM subjekto_abonementai 
    WHERE num_id = prm_item; 

    /*get the records that actualy exist */ 

    FOR _rec IN SELECT sub_item, sal_subject 
     FROM sal_subject 
     WHERE sub_item = prm_item LOOP 
    return 
     next _rec; 
    _counter := COALESCE(_counter, 0) + 1; 
    END LOOP; 

    /*fill the rest with null values*/ 

    While _kiek > _counter loop 
    _rec.sub_item := NULL; 
    _rec.sal_subject := NULL; 
    Return next _rec; 
    _counter := COALESCE(_counter, 0) + 1; 
    end loop; 

END;$BODY$ 
    LANGUAGE plpgsql VOLATILE; 
+0

Предложение 'WHERE num_id = sub_item' в строке 8 тела функции не имеет смысла. –

+0

Исправлено: я изменил несколько вещей перед публикацией здесь, так как это всего лишь часть большей структуры. – ertx

2

Может работать как это (чистого SQL решения):

SELECT a.sal_subject 
    , b.sub_item 
FROM (
    SELECT generate_series(1, max_items) AS rn 
     , sal_subject 
    FROM subject 
    ) a 
LEFT JOIN (
    SELECT row_number() OVER (PARTITION BY sal_subject ORDER BY sub_item) AS rn 
     , sal_subject 
     , sub_item 
    FROM subject_items 
    ) b USING (sal_subject, rn) 
ORDER BY sal_subject, rn 
  1. Генерация максимальных строк на тему, давайте называть их теоретические предметы.
    См. Руководство пользователя для generate_series().
  2. Применить номер строки к существующим элементам на объект.
    Руководство по эксплуатации window functions.
  3. LEFT JOIN Существующие предметы для теоретических предметов по предмету. Пропущенные предметы заполняются с помощью NULL.

В дополнение к таблице вы раскрытой в вопросе, я полагаю, столбец, который содержит максимальное количество элементов в subject таблице:

CREATE temp TABLE subject 
(sal_subject integer,  -- primary key of subject 
    max_items int);   -- max. number of items 

запросов для PostgreSQL 8.3, заменяя недостающую функцию окна row_number():

SELECT a.sal_subject 
    , b.sub_item 
FROM (
    SELECT generate_series(1, max_items) AS rn 
     , sal_subject 
    FROM subject 
    ) a 
LEFT JOIN (
    SELECT rn, sal_subject, arr[rn] AS sub_item 
    FROM (
     SELECT generate_series(1, ct) rn, sal_subject, arr 
     FROM (
      SELECT s.sal_subject 
       , s.ct 
       , ARRAY(
         SELECT sub_item 
         FROM subject_items s0 
         WHERE s0.sal_subject = s.sal_subject 
         ORDER BY sub_item 
        ) AS arr 
      FROM (
       SELECT sal_subject 
        , count(*) AS ct 
       FROM subject_items 
       GROUP BY 1 
       ) s 
      ) x 
     ) y 
    ) b USING (sal_subject, rn) 
ORDER BY sal_subject, rn 

Более подробно о замене row_number() в этом документе article by Quassnoi.

+0

Ваше решение выглядит очень хорошо, но я, вероятно, должен был упомянуть, что я использую версию 8.3.4, и насколько я могу видеть, что функции окна применяются только к 8.4+. – ertx

+0

@ertx: Да, вы определенно должны были это упомянуть. Функции окна предназначены для PostgreSQL 8.4+. Я добавил решение для 8.3. –