אגריגציה של מחרוזות

היכולת לעשות אגריגציה של מחרוזות (ובעצם שירשור של ערכים משורות לשורה אחת) היא עניין דיי מורכב – ישנם כמה פתרונות שבהם היינו משתמשים בגרסאות הקודמות. בין הפתרונות שהיינו משתמשים בהם ניתן למנות את:

  • פונקציית STRAGG (של Tom Kyte) – שזמינה החל מגרסה 9i ומשתמשת בטכניקה של פונקציות משתמש סיכומיות. הפוקנציה הזו לא ידעה כל כך למיין את הרשומות בתוך הרשימה (בעיה שנפתרה לאחר מכן) והיו לה ביצועים לא כל טובים, אבל היא עשתה את העבודה. במאמר מוסגר נציין שהפונקציה הזו נוספה באופן מובנה לבסיס הנתונים בגרסה 11 אבל היא עדיין לא יודעת למיין או להשתמש במפרידים בין הערכים.
  • שימוש ב-CONNECT_BY_PATH ושליפות היררכיות שזמין מגרסה 9i – פתרון מורכב יחסית שלא היה טוב לכל שליפה.
  • שימוש בפונקציית model בעלת התחביר המזעזע והביצועים העוד יותר מזעזעים שזמינה מגרסה 10g.
  • שימוש ב-collect וקוד pl/sql שזמין מגרסה 10g גם כן.
  • עריכה: ידידי לאוניד הזכיר גם את הפונקציה wmsys.wm_concat. היא זמינה החל מגרסה 9i  אבל דורשת התקנה של ה-wmsys (workspace manager) – שמותקן כברירת מחדל רק בגרסאות מאוחרות יותר .

כמובן שיש עוד שיטות אבל בסופו של דבר אף אחד מהפתרונות לא נותן שילוב אמיתי של קלות בשימוש וביצועים טובים.

החל מגרסה 11gR2 קיימת פונקציה סיכומית בשם LISTAGG. הפונקציה החדשה יודעת לשרשר ערכים משורות ולהחזיר אותן בתור ערך בעמודה. יש לה מפריד שניתן לקבוע אותו והיא יודעת למיין את הערכים.

התחביר שלה פשוט יחסית וניתן להשתמש בה בקלות כמו שמשתמשים בכל פונקציה סיכומית אחרת. בנוסף, היא יודעת להתנהג גם כמו פונקציה אנליטית מה שמעלה את השימושיות שלה באופן משמעותי בעיני. התחביר שלה הוא:

LISTAGG(
 [,]) WITHIN GROUP (ORDER BY
)  [OVER (PARTITION BY
)]

כמו שכבר אמרתי קודם, אחד היתרונות הגדולים בפונקציה החדשה הוא שהיא יודעת למיין את הערכים בתוך הרשימה – בין אם על ידי שימוש בעמודות שאנחנו משרשרים או על ידי סדר אחר שנקבע על ידי עמודות אחרות בטבלה. אם החלטנו מאיזשהי סיבה שאנחנו לא רוצים למיין, ניתן לרשום ORDER BY NULL ולהתעלם לגמרי מהמיון – אבל לא ניתן לא לרשום order by מכוון שזה חלק מהתחביר של הפקודה. הפוקנציה יודעת לקבל מפריד (delimiter) כפרמטר. המפריד צריך להיות אחד מהבאים: קבוע (constant), עמודה מתוך הטבלה או פונקציה דטרמיניסטית שהופעלה על עמודה בטבלה.

דוגמה לשימוש בפונקציה:

SQL> l
  1  SELECT  emp_name,
  2           listagg (prod_name, ',')
  3           WITHIN GROUP (ORDER BY 1) list_of_prod
  4     FROM test_model_pivot a
  5*    group by emp_name
SQL>
SQL> /

EMP_NAME             LIST_OF_PROD
-------------------- ----------------------------------------
emp1                 prod1,prod2,prod3
emp2                 prod1,prod2,prod3
emp3                 prod1,prod2,prod3

דוגמה לשימוש בפונקציה כפונקציה אנליטית:

  1  SELECT emp_name,
  2                  listagg (prod_name, ',')
  3                          WITHIN GROUP (ORDER BY amount)
  4                          OVER (PARTITION BY emp_name) prod_list
  5*      FROM test_model_pivot a
18:09:32 SQL> /

EMP_NAME             PROD_LIST
-------------------- ----------------------------------------
emp1                 prod1,prod2,prod3
emp1                 prod1,prod2,prod3
emp1                 prod1,prod2,prod3
emp2                 prod1,prod2,prod3
emp2                 prod1,prod2,prod3
emp2                 prod1,prod2,prod3
emp3                 prod1,prod2,prod3
emp3                 prod1,prod2,prod3
emp3                 prod1,prod2,prod3

חשוב מאוד: יש לשים לב שהפונקציה הזו היא בסופו של דבר פונקציית SQL רגילה מה שאומר שיש לה אורך רשומה המוגבל לאורך המקסימלי של varchar2 – כלומר 4000 תווים בלבד.

ניתן לקרוא עוד על הפונקציה כאן – במיוחד אם רוצים לצלול קצת לענייני ביצועים והשוואות בין השיטות השונות.

1 תגובה

השאירו תגובה

Want to join the discussion?
Feel free to contribute!

השאר תגובה

This site uses Akismet to reduce spam. Learn how your comment data is processed.