Query returning exact number of rows

前端 未结 3 411
长情又很酷
长情又很酷 2021-01-27 13:14

I have a table that stores two foreign keys, implementing a n:m relationship.

One of them points to a person (subject), the other one to a specific item.

相关标签:
3条回答
  • 2021-01-27 13:29

    Consider this largely simplified version of your plpgsql function. Should work in 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;
    

    Details about plpgsql in the manual (link to version 8.3).

    0 讨论(0)
  • 2021-01-27 13:35

    I was able to come up to this simplistic solution: First returning all the values i may select then looping returning null values while we have the right amount. Posting it here if someone would stumble on the same problem. Still looking for easier/faster solutions if they exist.

    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 讨论(0)
  • 2021-01-27 13:39

    Could work like this (pure SQL solution):

    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 the maximum rows per subject, let's call them theoretical items.
      See the manual for generate_series().
    2. Apply a row-number to existing items per subject.
      Manual about window functions.
    3. LEFT JOIN the existing items to the theoretical items per subject. Missing items are filled in with NULL.

    In addition to the table you disclosed in the question, I assume a column that holds the maximum number of items in the subject table:

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

    Query for PostgreSQL 8.3, substituting for the missing window function 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
    

    More about substituting row_number() in this article by Quassnoi.

    0 讨论(0)
提交回复
热议问题