Запрос, возвращающий точное количество строк - PullRequest
0 голосов
/ 17 января 2012

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

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

Остальные записи могутбыть заполнены NULL значениями или чем-то еще.

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

Изменить: Пример

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 предметов), но субъекту назначено только 3 предмета.

Возврат будетбыть примерно таким:

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

Я использую postgresql-8.3

Ответы [ 3 ]

2 голосов
/ 17 января 2012

Может работать так ( решение чистого 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. Применить номер строки к существующим предметам по предмету.
    Руководство по оконным функциям .
  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() в этой статье Quassnoi .

2 голосов
/ 17 января 2012

Рассмотрим эту в значительной степени упрощенную версию вашей функции 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 в руководстве (ссылка на версию 8.3).

0 голосов
/ 17 января 2012

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

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;
...