จะส่งคืนผลลัพธ์ของ SELECT ภายในฟังก์ชันใน PostgreSQL ได้อย่างไร?


106

ฉันมีฟังก์ชันนี้ใน PostgreSQL แต่ฉันไม่รู้ว่าจะส่งคืนผลลัพธ์ของแบบสอบถามอย่างไร:

CREATE OR REPLACE FUNCTION wordFrequency(maxTokens INTEGER)
  RETURNS SETOF RECORD AS
$$
BEGIN
    SELECT text, count(*), 100 / maxTokens * count(*)
    FROM (
        SELECT text
    FROM token
    WHERE chartype = 'ALPHABETIC'
    LIMIT maxTokens
    ) as tokens
    GROUP BY text
    ORDER BY count DESC
END
$$
LANGUAGE plpgsql;

แต่ฉันไม่รู้ว่าจะส่งคืนผลลัพธ์ของแบบสอบถามภายในฟังก์ชัน PostgreSQL ได้อย่างไร

ฉันพบว่าประเภทการคืนสินค้าควรเป็นSETOF RECORDใช่ไหม? แต่คำสั่ง return ไม่ถูกต้อง

วิธีที่ถูกต้องในการทำเช่นนี้คืออะไร?


ทำไมคุณนับพวกเขา; คุณมีโทเค็นที่ซ้ำกันในตารางโทเค็นของคุณหรือไม่? นอกจากนี้: โปรดเพิ่มคำจำกัดความของตารางในคำถามของคุณ
wildplasser

1
นี่คือหน้าที่ทั้งหมดของคุณหรือไม่? หากคุณไม่มีคำสั่งอื่น ๆ ในฟังก์ชันคุณควรสร้างมันLANGUAGE SQLขึ้นมา
jpmc26

คำตอบ:


138

ใช้RETURN QUERY:

CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
  RETURNS TABLE (txt   text   -- also visible as OUT parameter inside function
               , cnt   bigint
               , ratio bigint) AS
$func$
BEGIN
   RETURN QUERY
   SELECT t.txt
        , count(*) AS cnt                 -- column alias only visible inside
        , (count(*) * 100) / _max_tokens  -- I added brackets
   FROM  (
      SELECT t.txt
      FROM   token t
      WHERE  t.chartype = 'ALPHABETIC'
      LIMIT  _max_tokens
      ) t
   GROUP  BY t.txt
   ORDER  BY cnt DESC;                    -- potential ambiguity 
END
$func$  LANGUAGE plpgsql;

โทร:

SELECT * FROM word_frequency(123);

คำอธิบาย:

  • มันเป็นมากในทางปฏิบัติมากขึ้นอย่างชัดเจนกำหนดประเภทผลตอบแทนมากกว่าเพียงแค่การประกาศว่าเป็นบันทึก วิธีนี้ทำให้คุณไม่ต้องระบุรายการนิยามคอลัมน์ในทุกการเรียกใช้ฟังก์ชัน RETURNS TABLEเป็นวิธีหนึ่งในการทำเช่นนั้น ยังมีคนอื่นอีก ประเภทข้อมูลของOUTพารามิเตอร์ต้องตรงกับสิ่งที่ส่งกลับมาจากการสืบค้น

  • เลือกชื่อสำหรับOUTพารามิเตอร์อย่างระมัดระวัง สามารถมองเห็นได้ในส่วนของฟังก์ชันเกือบทุกที่ คอลัมน์คุณสมบัติตารางที่มีชื่อเดียวกันเพื่อหลีกเลี่ยงความขัดแย้งหรือผลลัพธ์ที่ไม่คาดคิด ฉันทำเช่นนั้นสำหรับคอลัมน์ทั้งหมดในตัวอย่างของฉัน

    แต่สังเกตความขัดแย้งในการตั้งชื่อที่อาจเกิดขึ้นระหว่างOUTพารามิเตอร์cntและนามแฝงคอลัมน์ที่มีชื่อเดียวกัน ในกรณีนี้โดยเฉพาะ ( RETURN QUERY SELECT ...) Postgres ใช้นามแฝงของคอลัมน์เหนือOUTพารามิเตอร์ทางใดทางหนึ่ง สิ่งนี้อาจคลุมเครือในบริบทอื่น ๆ มีหลายวิธีในการหลีกเลี่ยงความสับสน:

    1. ใช้ตำแหน่งลำดับของรายการในรายการ SELECT ORDER BY 2 DESCนี้: ตัวอย่าง:
    2. ORDER BY count(*)ทำซ้ำการแสดงออก
    3. (ไม่สามารถใช้ได้ที่นี่) ตั้งค่าพารามิเตอร์การกำหนดค่าplpgsql.variable_conflictหรือใช้คำสั่งพิเศษ#variable_conflict error | use_variable | use_columnในฟังก์ชัน ดู:
  • อย่าใช้ "text" หรือ "count" เป็นชื่อคอลัมน์ ทั้งสองอย่างถูกกฎหมายที่จะใช้ใน Postgres แต่ "count" เป็นคำสงวนใน SQL มาตรฐานและชื่อฟังก์ชันพื้นฐานและ "ข้อความ" เป็นชนิดข้อมูลพื้นฐาน อาจทำให้เกิดข้อผิดพลาดที่สับสน ฉันใช้txtและcntในตัวอย่างของฉัน

  • เพิ่ม;ข้อผิดพลาดทางไวยากรณ์ที่ขาดหายไปและแก้ไขในส่วนหัว (_max_tokens int)ไม่(int maxTokens)- ประเภทหลังชื่อ

  • ในขณะที่ทำงานกับการหารจำนวนเต็มควรคูณก่อนแล้วหารในภายหลังเพื่อลดข้อผิดพลาดในการปัดเศษให้น้อยที่สุด ดียิ่งขึ้น: ทำงานกับnumeric(หรือประเภทจุดลอยตัว) ดูด้านล่าง

ทางเลือก

นี่คือสิ่งที่ฉันคิดว่าข้อความค้นหาของคุณควรมีลักษณะดังนี้ (การคำนวณส่วนแบ่งสัมพัทธ์ต่อโทเค็น ):

CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
  RETURNS TABLE (txt            text
               , abs_cnt        bigint
               , relative_share numeric) AS
$func$
BEGIN
   RETURN QUERY
   SELECT t.txt, t.cnt
        , round((t.cnt * 100) / (sum(t.cnt) OVER ()), 2)  -- AS relative_share
   FROM  (
      SELECT t.txt, count(*) AS cnt
      FROM   token t
      WHERE  t.chartype = 'ALPHABETIC'
      GROUP  BY t.txt
      ORDER  BY cnt DESC
      LIMIT  _max_tokens
      ) t
   ORDER  BY t.cnt DESC;
END
$func$  LANGUAGE plpgsql;

การแสดงออกsum(t.cnt) OVER ()เป็นฟังก์ชั่นหน้าต่าง คุณสามารถใช้CTEแทนเคียวรีย่อยได้ แต่โดยทั่วไปแล้วคิวรีย่อยจะมีราคาถูกกว่าในกรณีง่ายๆเช่นนี้

ไม่จำเป็นต้องมีคำสั่งที่ชัดเจนRETURNขั้นสุดท้าย(แต่อนุญาต) เมื่อทำงานกับOUTพารามิเตอร์หรือRETURNS TABLE(ซึ่งใช้OUTพารามิเตอร์โดยปริยาย)

round()ด้วยพารามิเตอร์สองตัวที่ใช้ได้กับnumericประเภทเท่านั้น count()ในการสืบค้นย่อยจะสร้างbigintผลลัพธ์และสิ่งsum()นี้bigintจะสร้างnumericผลลัพธ์ดังนั้นเราจึงจัดการกับnumericตัวเลขโดยอัตโนมัติและทุกอย่างก็เข้าที่


ขอบคุณมากสำหรับคำตอบและการแก้ไขของคุณ ทำงานได้ดีในขณะนี้ (ฉันเปลี่ยนประเภทอัตราส่วนเป็นตัวเลขเท่านั้น)
Renato Dinhani

@ RenatoDinhaniConceiçãoเจ๋ง! ฉันได้เพิ่มเวอร์ชันที่อาจตอบหรือไม่ตอบคำถามเพิ่มเติมที่คุณไม่ได้ถามจริง ;)
Erwin Brandstetter

ดีสิ่งเดียวคือฉันคิดว่าคุณต้องการRETURN;ก่อนหน้านั้นEND;อย่างน้อยฉันก็ทำ - แต่ฉันกำลังทำ UNION ดังนั้นฉันจึงไม่แน่ใจว่าจะทำให้แตกต่างกันหรือไม่
yekta

@yekta: ฉันได้เพิ่มข้อมูลเกี่ยวกับบทบาทของRETURN. แก้ไขข้อผิดพลาดที่ไม่เกี่ยวข้องและเพิ่มการปรับปรุงบางอย่างในขณะที่ดำเนินการอยู่
Erwin Brandstetter

1
จะทำอย่างไรเมื่อคุณไม่ต้องการ จำกัด สิ่งที่อยู่ใน Return TABLE () IE ส่งคืนตาราง (*)?
นิค

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.