2012-01-17 5 views
0

두 개의 외래 키를 저장하고 n : 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

+0

상황을 이해할 수 없습니다. 몇 가지 테이블 구조와 원하는 쿼리 출력을 제공 할 수 있습니까? – Acn

+0

질문 끝의 리턴 테이블은 'sal_subject'에 대한 모든 행에서 값'1 '을 유지해야하며 의미가 없으려면'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 : 8.3 페이지에서이 옵션을 선택했습니다. 이 경우에'row_number()'윈도우 함수가 없으면 순수 SQL은 다루기 힘들다. –

+0

또한 변수를 선언 할 때 선택하여 할당 할 수 있다는 것을 알지 못했습니다. 매우 편리합니다. – ertx

0

나는이 단순한 솔루션으로 올 수 있었다 사용하고 있습니다 : 먼저 난 후 루핑 반환을 선택할 수있는 모든 값을 반환 우리가 올바른 금액을 가지고있는 동안 null 값. 누군가가 같은 문제에 걸려 넘어지면 여기에 게시하십시오. 더 쉽고 빠른 솔루션이 있으면 계속 찾고 있습니다.

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

함수 본문의 8 번째 줄에있는 'WHERE num_id = sub_item' 절이 의미가 없습니다. –

+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로 채워집니다. , 에 대한

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

    쿼리 PostgreSQL의 8.3 :

  4. 당신이 질문에 개시된 테이블에 추가

, 나는 subject 테이블 항목의 최대 수를 보유하고 열을 가정 누락 된 윈도우 함수로 대체 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 

article by Quassnoirow_number()을 대체하는 방법에 대해 자세히 알아보십시오.

+0

당신의 솔루션은 정말 멋지지만 어쩌면 나는 8.3.4 버전을 사용하고 있고 윈도우 기능이 8.4+에만 적용되는 것을 볼 수있을 것입니다. – ertx

+0

@ertx : 네, 분명히 언급 했어야합니다. 윈도우 함수는 PostgreSQL 8.4+ 용입니다. 나는 8.3을위한 해결책을 추가했다. –