פונקציות צבירה בהגדרת המשתמש
במאמר הזה מוסבר איך ליצור, להפעיל ולמחוק פונקציות מצטברות מוגדרות על ידי המשתמש (UDAFs) ב-BigQuery.
פונקציית UDAF מאפשרת ליצור פונקציית צבירה באמצעות ביטוי שמכיל קוד. פונקציית UDAF מקבלת עמודות קלט, מבצעת חישוב על קבוצה של שורות בכל פעם, ואז מחזירה את תוצאת החישוב כערך יחיד.
יצירת פונקציית UDAF של SQL
בקטע הזה מתוארות הדרכים השונות שבהן אפשר ליצור פונקציית UDAF של SQL ב-BigQuery, שהיא קבועה או זמנית.
יצירת פונקציית UDAF מתמשכת של SQL
אפשר ליצור פונקציית UDAF מתמידה ב-SQL, כלומר אפשר להשתמש בה שוב בכמה שאילתות. בטוח להפעיל פונקציות UDAF קבועות כשמשתפים אותן בין בעלים. פונקציות UDAF לא יכולות לשנות נתונים, לתקשר עם מערכות חיצוניות או לשלוח יומנים ל-Google Cloud Observability או לאפליקציות דומות.
כדי ליצור פונקציית UDAF קבועה, משתמשים בהצהרה CREATE AGGREGATE FUNCTION בלי מילות המפתח TEMP או TEMPORARY. חובה לכלול את מערך הנתונים בנתיב הפונקציה.
לדוגמה, השאילתה הבאה יוצרת UDAF מתמשכת בשם ScaledAverage:
CREATE AGGREGATE FUNCTION myproject.mydataset.ScaledAverage( dividend FLOAT64, divisor FLOAT64) RETURNS FLOAT64 AS ( AVG(dividend / divisor) );
יצירת פונקציית UDAF זמנית ב-SQL
אפשר ליצור פונקציית UDAF זמנית ב-SQL, כלומר פונקציית UDAF שקיימת רק בהיקף של שאילתה, סקריפט, סשן או פרוצדורה יחידים.
כדי ליצור פונקציית UDAF זמנית, משתמשים בהצהרה CREATE AGGREGATE FUNCTION עם מילת המפתח TEMP או TEMPORARY.
לדוגמה, השאילתה הבאה יוצרת UDAF זמני בשם ScaledAverage:
CREATE TEMP AGGREGATE FUNCTION ScaledAverage( dividend FLOAT64, divisor FLOAT64) RETURNS FLOAT64 AS ( AVG(dividend / divisor) );
שימוש בפרמטרים מצטברים ולא מצטברים
אפשר ליצור פונקציית UDAF ב-SQL עם פרמטרים מצטברים ופרמטרים לא מצטברים.
בדרך כלל, פונקציות UDAF צוברות פרמטרים של פונקציות בכל השורות בקבוצה.
עם זאת, אפשר לציין פרמטר של פונקציה כפרמטר שאינו מצטבר באמצעות מילת המפתח NOT AGGREGATE.
פרמטר של פונקציה לא מצטברת הוא פרמטר של פונקציה סקלרית עם ערך קבוע לכל השורות בקבוצה. פרמטר חוקי של פונקציה שאינה מצטברת חייב להיות ערך מילולי. בתוך ההגדרה של UDAF, פרמטרים של פונקציית צבירה יכולים להופיע רק כארגומנטים של פונקציה לקריאות של פונקציית צבירה. הפניות לפרמטרים של פונקציות שאינן מצטברות יכולות להופיע בכל מקום בהגדרה של UDAF.
לדוגמה, הפונקציה הבאה מכילה פרמטר מצטבר שנקרא dividend ופרמטר לא מצטבר שנקרא divisor:
-- Create the function. CREATE TEMP AGGREGATE FUNCTION ScaledSum( dividend FLOAT64, divisor FLOAT64 NOT AGGREGATE) RETURNS FLOAT64 AS ( SUM(dividend) / divisor );
שימוש בפרויקט שמוגדר כברירת מחדל בגוף הפונקציה
בגוף של פונקציית UDAF ב-SQL, כל ההפניות לישויות ב-BigQuery, כמו טבלאות או תצוגות, חייבות לכלול את מזהה הפרויקט, אלא אם הישות נמצאת באותו פרויקט שמכיל את פונקציית ה-UDAF.
לדוגמה, נניח את ההצהרה הבאה:
CREATE AGGREGATE FUNCTION project1.dataset_a.ScaledAverage( dividend FLOAT64, divisor FLOAT64) RETURNS FLOAT64 AS ( ( SELECT AVG(dividend / divisor) FROM dataset_a.my_table ) );
אם מריצים את ההצהרה הקודמת בפרויקט project1, ההצהרה מצליחה כי my_table קיים ב-project1. עם זאת, אם מריצים את ההצהרה הקודמת מפרויקט אחר, ההצהרה נכשלת.
כדי לתקן את השגיאה, צריך לכלול את מזהה הפרויקט בהפניה לטבלה:
CREATE AGGREGATE FUNCTION project1.dataset_a.ScaledAverage( dividend FLOAT64, divisor FLOAT64) RETURNS FLOAT64 AS ( ( SELECT AVG(dividend / divisor) FROM project1.dataset_a.my_table ) );
אפשר גם להפנות לישות בפרויקט או במערך נתונים אחרים מאלה שבהם יוצרים את הפונקציה:
CREATE AGGREGATE FUNCTION project1.dataset_a.ScaledAverage( dividend FLOAT64, divisor FLOAT64) RETURNS FLOAT64 AS ( ( SELECT AVG(dividend / divisor) FROM project2.dataset_c.my_table ) );
יצירת UDAF של JavaScript
בקטע הזה מתוארות הדרכים השונות ליצירת פונקציית UDAF ב-JavaScript ב-BigQuery. יש כמה כללים שצריך להקפיד עליהם כשיוצרים פונקציית UDAF ב-JavaScript:
הגוף של פונקציית UDAF ב-JavaScript חייב להיות מחרוזת ליטרלית עם מרכאות שמייצגת את קוד ה-JavaScript. מידע נוסף על הסוגים השונים של מחרוזות מילוליות שמוקפות במירכאות זמין במאמר פורמטים של מחרוזות מילוליות שמוקפות במירכאות.
מותר להשתמש רק בקידודים מסוגים מסוימים. מידע נוסף זמין במאמר בנושא קידודים מותרים של סוגי SQL ב-UDAF של JavaScript.
גוף הפונקציה ב-JavaScript צריך לכלול ארבע פונקציות JavaScript שמבצעות אתחול, צבירה, מיזוג וסיום של התוצאות עבור פונקציית ה-UDAF ב-JavaScript (
initialState,aggregate,mergeו-finalize). מידע נוסף זמין במאמר בנושא קידודים מותרים של סוגי SQL בפונקציית UDAF ב-JavaScript.כל ערך שמוחזר על ידי הפונקציה
initialStateאו שנותר בארגומנטstateאחרי הקריאה לפונקציהaggregateאוmerge, חייב להיות ניתן לסריאליזציה. אם רוצים לעבוד עם נתוני צבירה שלא ניתן לסדר בסדרות, כמו פונקציות או שדות סמלים, צריך להשתמש בפונקציותserializeו-deserializeשכלולות. מידע נוסף על סדרת נתונים וביטול סדרת נתונים ב-UDAF של JavaScript
יצירת פונקציית UDAF של JavaScript לאחסון מתמיד
אפשר ליצור פונקציית UDAF ב-JavaScript שהיא מתמידה, כלומר אפשר להשתמש בה שוב בכמה שאילתות. בטוח להפעיל פונקציות UDAF קבועות כשמשתפים אותן בין בעלים. פונקציות UDAF לא יכולות לשנות נתונים, לתקשר עם מערכות חיצוניות או לשלוח יומנים ל-Google Cloud Observability או לאפליקציות דומות.
כדי ליצור פונקציית UDAF מתמידה, משתמשים בהצהרה CREATE AGGREGATE FUNCTION בלי מילות המפתח TEMP או TEMPORARY. חובה לכלול את מערך הנתונים בנתיב הפונקציה.
השאילתה הבאה יוצרת UDAF מתמשך של JavaScript שנקרא SumPositive:
CREATE OR REPLACE AGGREGATE FUNCTION my_project.my_dataset.SumPositive(x FLOAT64) RETURNS FLOAT64 LANGUAGE js AS r''' export function initialState() { return {sum: 0} } export function aggregate(state, x) { if (x > 0) { state.sum += x; } } export function merge(state, partialState) { state.sum += partialState.sum; } export function finalize(state) { return state.sum; } '''; -- Call the JavaScript UDAF. WITH numbers AS ( SELECT * FROM UNNEST([1.0, -1.0, 3.0, -3.0, 5.0, -5.0]) AS x) SELECT my_project.my_dataset.SumPositive(x) AS sum FROM numbers; /*-----* | sum | +-----+ | 9.0 | *-----*/
יצירת פונקציית UDAF זמנית ב-JavaScript
אפשר ליצור UDAF זמנית ב-JavaScript, כלומר UDAF שקיימת רק בהיקף של שאילתה, סקריפט, סשן או פרוצדורה יחידים.
כדי ליצור פונקציית UDAF זמנית, משתמשים בהצהרה CREATE AGGREGATE FUNCTION עם מילת המפתח TEMP או TEMPORARY.
השאילתה הבאה יוצרת פונקציית UDAF זמנית ב-JavaScript שנקראת
SumPositive:
CREATE TEMP AGGREGATE FUNCTION SumPositive(x FLOAT64) RETURNS FLOAT64 LANGUAGE js AS r''' export function initialState() { return {sum: 0} } export function aggregate(state, x) { if (x > 0) { state.sum += x; } } export function merge(state, partialState) { state.sum += partialState.sum; } export function finalize(state) { return state.sum; } '''; -- Call the JavaScript UDAF. WITH numbers AS ( SELECT * FROM UNNEST([1.0, -1.0, 3.0, -3.0, 5.0, -5.0]) AS x) SELECT SumPositive(x) AS sum FROM numbers; /*-----* | sum | +-----+ | 9.0 | *-----*/
הכללת פרמטרים לא מצטברים ב-UDAF של JavaScript
אפשר ליצור פונקציית UDAF ב-JavaScript עם פרמטרים מצטברים ופרמטרים לא מצטברים.
בדרך כלל, פונקציות UDAF צוברות פרמטרים של פונקציות בכל השורות בקבוצה.
עם זאת, אפשר לציין פרמטר של פונקציה כפרמטר שאינו מצטבר באמצעות מילת המפתח NOT AGGREGATE.
פרמטר של פונקציה לא מצטברת הוא פרמטר של פונקציה סקלרית עם ערך קבוע לכל השורות בקבוצה. פרמטר חוקי של פונקציה שאינה מצטברת חייב להיות ערך מילולי. בתוך ההגדרה של UDAF, פרמטרים של פונקציית צבירה יכולים להופיע רק כארגומנטים של פונקציה לקריאות של פונקציית צבירה. הפניות לפרמטרים של פונקציות שאינן מצטברות יכולות להופיע בכל מקום בהגדרה של UDAF.
בדוגמה הבאה, פונקציית ה-UDAF של JavaScript מכילה פרמטר מצטבר בשם s ופרמטר לא מצטבר בשם delimiter:
CREATE TEMP AGGREGATE FUNCTION JsStringAgg( s STRING, delimiter STRING NOT AGGREGATE) RETURNS STRING LANGUAGE js AS r''' export function initialState() { return {strings: []} } export function aggregate(state, s) { state.strings.push(s); } export function merge(state, partialState) { state.strings = state.strings.concat(partialState.strings); } export function finalize(state, delimiter) { return state.strings.join(delimiter); } '''; -- Call the JavaScript UDAF. WITH strings AS ( SELECT * FROM UNNEST(["aaa", "bbb", "ccc", "ddd"]) AS values) SELECT JsStringAgg(values, '.') AS result FROM strings; /*-----------------* | result | +-----------------+ | aaa.bbb.ccc.ddd | *-----------------*/
סדרת נתונים וביטול הסדרה שלהם ב-UDAF של JavaScript
מערכת BigQuery צריכה לבצע סריאליזציה של כל אובייקט שמוחזר על ידי הפונקציה initialState או שנשאר בארגומנט state אחרי הקריאה לפונקציה aggregate או merge.
BigQuery תומך בסריאליזציה של אובייקט אם כל השדות הם אחד מהסוגים הבאים:
- ערך פרימיטיבי של JavaScript (לדוגמה:
2,"abc",null,undefined). - אובייקט JavaScript ש-BigQuery תומך בסריאליזציה של כל ערכי השדות שלו.
- מערך JavaScript ש-BigQuery תומך בסריאליזציה של כל הרכיבים שלו.
אפשר לבצע סריאליזציה של ערכי ההחזרה הבאים:
export function initialState() {
return {a: "", b: 3, c: null, d: {x: 23} }
}
export function initialState() {
return {value: 2.3};
}
אי אפשר לבצע סריאליזציה של ערכי ההחזרה הבאים:
export function initialState() {
return {
value: function() {return 6;}
}
}
export function initialState() {
return 2.3;
}
אם רוצים לעבוד עם מצבי צבירה שלא ניתן לסדר אותם, פונקציית ה-UDAF של JavaScript צריכה לכלול את הפונקציות serialize ו-deserialize.
הפונקציה serialize ממירה את מצב האגרגציה לאובייקט שניתן לסדר אותו, והפונקציה deserialize ממירה את האובייקט שניתן לסדר בחזרה למצב אגרגציה.
בדוגמה הבאה, ספרייה חיצונית מחשבת סכומים באמצעות ממשק:
export class SumAggregator { constructor() { this.sum = 0; } update(value) { this.sum += value; } getSum() { return this.sum; } }
השאילתה הבאה לא מופעלת כי אובייקט המחלקה SumAggregator לא ניתן לסריאליזציה ב-BigQuery, בגלל הפונקציות שנמצאות בתוך המחלקה.
CREATE TEMP AGGREGATE FUNCTION F(x FLOAT64) RETURNS FLOAT64 LANGUAGE js AS r''' class SumAggregator { constructor() { this.sum = 0; } update(value) { this.sum += value; } getSum() { return this.sum; } } export function initialState() { return new SumAggregator(); } export function aggregate(agg, value) { agg.update(value); } export function merge(agg1, agg2) { agg1.update(agg2.getSum()); } export function finalize(agg) { return agg.getSum(); } '''; --Error: getSum is not a function SELECT F(x) AS results FROM UNNEST([1,2,3,4]) AS x;
אם מוסיפים את הפונקציות serialize ו-deserialize לשאילתה הקודמת, השאילתה מופעלת כי אובייקט המחלקה SumAggregator מומר לאובייקט שניתן לסריאליזציה ב-BigQuery, ואז בחזרה לאובייקט המחלקה SumAggregator.
CREATE TEMP AGGREGATE FUNCTION F(x FLOAT64) RETURNS FLOAT64 LANGUAGE js AS r''' class SumAggregator { constructor() { this.sum = 0; } update(value) { this.sum += value; } getSum() { return this.sum; } } export function initialState() { return new SumAggregator(); } export function aggregate(agg, value) { agg.update(value); } export function merge(agg1, agg2) { agg1.update(agg2.getSum()); } export function finalize(agg) { return agg.getSum(); } export function serialize(agg) { return {sum: agg.getSum()}; } export function deserialize(serialized) { var agg = new SumAggregator(); agg.update(serialized.sum); return agg; } '''; SELECT F(x) AS results FROM UNNEST([1,2,3,4]) AS x; /*-----------------* | results | +-----------------+ | 10.0 | *-----------------*/
מידע נוסף על פונקציות הסריאליזציה זמין במאמר פונקציות אופציונליות של סריאליזציה ב-JavaScript.
הכללת משתנים גלובליים ופונקציות בהתאמה אישית ב-UDAF של JavaScript
גוף הפונקציה של JavaScript יכול לכלול קוד JavaScript בהתאמה אישית, כמו משתנים גלובליים של JavaScript ופונקציות בהתאמה אישית.
משתנים גלובליים מופעלים כשקוד ה-JavaScript נטען ל-BigQuery ולפני שהפונקציה initialState מופעלת.
משתנים גלובליים יכולים להיות שימושיים אם צריך לבצע עבודת אתחול חד-פעמית שלא צריכה לחזור על עצמה עבור כל קבוצת צבירה, כמו במקרה של הפונקציות initialState, aggregate, merge ו-finalize.
אל תשתמשו במשתנים גלובליים כדי לאחסן את מצב הצבירה. במקום זאת, מגבילים את מצב הצבירה לאובייקטים שמועברים לפונקציות מיוצאות. משתמשים במשתנים גלובליים רק כדי לשמור במטמון פעולות יקרות שלא ספציפיות לפעולת צבירה מסוימת.
בשאילתה הבאה, הפונקציה SumOfPrimes מחשבת סכום, אבל רק מספרים ראשוניים נכללים בחישוב. בגוף הפונקציה של JavaScript יש שני משתנים גלובליים, primes ו-maxTested, שמופעלים קודם. בנוסף, יש פונקציה מותאמת אישית בשם isPrime שבודקת אם מספר הוא ראשוני.
CREATE TEMP AGGREGATE FUNCTION SumOfPrimes(x INT64) RETURNS INT64 LANGUAGE js AS r''' var primes = new Set([2]); var maxTested = 2; function isPrime(n) { if (primes.has(n)) { return true; } if (n <= maxTested) { return false; } for (var k = 2; k < n; ++k) { if (!isPrime(k)) { continue; } if ((n % k) == 0) { maxTested = n; return false; } } maxTested = n; primes.add(n); return true; } export function initialState() { return {sum: 0}; } export function aggregate(state, x) { x = Number(x); if (isPrime(x)) { state.sum += x; } } export function merge(state, partialState) { state.sum += partialState.sum; } export function finalize(state) { return state.sum; } '''; -- Call the JavaScript UDAF. WITH numbers AS ( SELECT * FROM UNNEST([10, 11, 13, 17, 19, 20]) AS x) SELECT SumOfPrimes(x) AS sum FROM numbers; /*-----* | sum | +-----+ | 60 | *-----*/
הכללה של ספריות JavaScript
אפשר להרחיב את הפונקציות המצטברות המוגדרות על ידי המשתמש ב-JavaScript באמצעות האפשרות library בסעיף OPTIONS. האפשרות הזו מאפשרת לכם לציין ספריות קוד חיצוניות עבור פונקציית ה-UDAF של JavaScript, ואז לייבא את הספריות האלה באמצעות ההצהרה import.
בדוגמה הבאה, קוד ב-bar.js זמין לכל קוד בגוף הפונקציה של UDAF ב-JavaScript:
CREATE TEMP AGGREGATE FUNCTION JsAggFn(x FLOAT64) RETURNS FLOAT64 LANGUAGE js OPTIONS (library = ['gs://foo/bar.js']) AS r''' import doInterestingStuff from 'bar.js'; export function initialState() { return ... } export function aggregate(state, x) { var result = doInterestingStuff(x); ... } export function merge(state, partial_state) { ... } export function finalize(state) { return ...; } ''';
מבנה JavaScript נדרש
בניגוד לפונקציה בהגדרת המשתמש (UDF) ב-JavaScript, שבה גוף הפונקציה הוא JavaScript חופשי שמופעל לכל שורה, גוף הפונקציה של פונקציה מצטברת בהגדרת המשתמש (UDAF) ב-JavaScript הוא מודול JavaScript שמכיל כמה פונקציות מובנות שמיוצאות, שמופעלות בשלבים שונים בתהליך הצבירה. חלק מהפונקציות המובנות האלה הן חובה, וחלקן אופציונליות. אפשר גם להוסיף פונקציות JavaScript.
פונקציות אגרגציה נדרשות של JavaScript
אפשר לכלול את פונקציות ה-JavaScript, אבל גוף הפונקציה של ה-JavaScript חייב לכלול את פונקציות ה-JavaScript הבאות שאפשר לייצא:
initialState([nonAggregateParam]): מחזירה אובייקט JavaScript שמייצג מצב צבירה שבו עדיין לא בוצעה צבירה של שורות.
aggregate(state, aggregateParam[, ...][, nonAggregateParam]): מצטבר בשורה אחת של נתונים, ומעדכן את המצב כדי לאחסן את תוצאת הצבירה. הפונקציה לא מחזירה ערך.
merge(state, partialState, [nonAggregateParam]): מיזוג של מצב הצבירהpartialStateעם מצב הצבירהstate. הפונקציה הזו משמשת כשהמנוע צובר נתונים מקטעים שונים במקביל וצריך לשלב את התוצאות. הפונקציה לא מחזירה ערך.
finalize(finalState, [nonAggregateParam]): מחזירה את התוצאה הסופית של פונקציית הצבירה, בהינתן מצב צבירה סופיfinalState.
מידע נוסף על הפונקציות הנדרשות זמין במאמר פונקציות נדרשות ב-UDAF של JavaScript.
פונקציות אופציונליות של סריאליזציה ב-JavaScript
אם רוצים לעבוד עם מצבי צבירה שלא ניתן לסדר, פונקציית ה-UDAF ב-JavaScript צריכה לספק את הפונקציות serialize ו-deserialize.
הפונקציה serialize ממירה את מצב הצבירה לאובייקט שניתן לסדר אותו ב-BigQuery, והפונקציה deserialize ממירה את האובייקט שניתן לסדר אותו ב-BigQuery בחזרה למצב צבירה.
serialize(state): מחזירה אובייקט שניתן לסדר אותו, שמכיל את המידע במצב צבירה, כדי לבטל את הסדר שלו באמצעות הפונקציהdeserialize.
deserialize(serializedState): מבצעת דה-סריאליזציה שלserializedState(שעברה סריאליזציה קודמת באמצעות הפונקציהserialize) למצב צבירה שאפשר להעביר לפונקציותserialize, aggregate, mergeאוfinalize.
מידע נוסף על פונקציות הסריאליזציה המובנות של JavaScript זמין במאמר פונקציות סריאליזציה עבור UDAF של JavaScript.
כדי ללמוד איך לבצע סריאליזציה ודה-סריאליזציה של נתונים באמצעות UDAF של JavaScript, אפשר לעיין במאמר בנושא סריאליזציה ודה-סריאליזציה של נתונים ב-UDAF של JavaScript.
קידודים מותרים של סוגי SQL בפונקציות מצטברות בהגדרת המשתמש (UDAF) ב-JavaScript
ב-UDAFs של JavaScript, סוגי הנתונים של GoogleSQL שנתמכים מייצגים סוגי נתונים של JavaScript באופן הבא:
| סוג הנתונים של GoogleSQL |
סוג נתונים של JavaScript |
הערות |
|---|---|---|
ARRAY |
Array |
אין תמיכה במערך של מערכים. כדי לעקוף את המגבלה הזו, צריך להשתמש בסוגי הנתונים Array<Object<Array>> (JavaScript) ו-ARRAY<STRUCT<ARRAY>> (GoogleSQL).
|
BIGNUMERIC
|
Number או String
|
בדיוק כמו NUMERIC.
|
BOOL |
Boolean |
|
BYTES |
Uint8Array |
|
DATE |
Date |
|
FLOAT64 |
Number |
|
INT64 |
BigInt |
|
JSON |
סוגים שונים |
אפשר להמיר את סוג הנתונים JSON ב-GoogleSQL לסוג הנתונים Object, Array או לסוג נתונים אחר ב-JavaScript שנתמך ב-GoogleSQL.
|
NUMERIC
|
Number או String
|
אם אפשר לייצג ערך NUMERIC בדיוק כערך IEEE 754 floating-point (בטווח [-253, 253]), ואין לו חלק שברי, הוא מקודד כסוג נתונים Number, אחרת הוא מקודד כסוג נתונים String.
|
STRING |
String |
|
STRUCT |
Object |
כל שדה STRUCT הוא מאפיין עם שם בסוג הנתונים Object. אין תמיכה בשדה STRUCT ללא שם.
|
TIMESTAMP |
Date |
Date מכיל שדה של מיקרו-שנייה עם השבר של המיקרו-שנייה TIMESTAMP.
|
התקשרות ל-UDAF
בקטע הזה מתוארות הדרכים השונות שבהן אפשר לקרוא לפונקציית UDAF קבועה או זמנית אחרי שיוצרים אותה ב-BigQuery.
קריאה לפונקציית UDAF מתמידה
אפשר לקרוא לפונקציית UDAF מתמידה באותו אופן שבו קוראים לפונקציית צבירה מובנית. מידע נוסף זמין במאמר בנושא קריאות לפונקציות מצטברות. צריך לכלול את קבוצת הנתונים בנתיב הפונקציה.
בדוגמה הבאה, השאילתה קוראת ל-UDAF מתמשך שנקרא WeightedAverage:
SELECT my_project.my_dataset.WeightedAverage(item, weight, 2) AS weighted_average FROM ( SELECT 1 AS item, 2.45 AS weight UNION ALL SELECT 3 AS item, 0.11 AS weight UNION ALL SELECT 5 AS item, 7.02 AS weight );
נוצרת טבלה עם התוצאות הבאות:
/*------------------*
| weighted_average |
+------------------+
| 4.5 |
*------------------*/
התקשרות לפונקציית UDAF זמנית
אפשר לקרוא לפונקציית UDAF זמנית באותו אופן שבו קוראים לפונקציית צבירה מובנית. מידע נוסף זמין במאמר בנושא קריאות לפונקציות מצטברות.
הפונקציה הזמנית צריכה להיכלל בשאילתה עם כמה הצהרות או בפרוצדורה שמכילה את בקשת הפעלת פונקציית ה-UDAF.
בדוגמה הבאה, השאילתה קוראת ל-UDAF זמני שנקרא WeightedAverage:
CREATE TEMP AGGREGATE FUNCTION WeightedAverage(...) -- Temporary UDAF function call SELECT WeightedAverage(item, weight, 2) AS weighted_average FROM ( SELECT 1 AS item, 2.45 AS weight UNION ALL SELECT 3 AS item, 0.11 AS weight UNION ALL SELECT 5 AS item, 7.02 AS weight );
נוצרת טבלה עם התוצאות הבאות:
/*------------------*
| weighted_average |
+------------------+
| 4.5 |
*------------------*/
התעלמות משורות עם ערכים של NULL או הכללה שלהן
כשקוראים לפונקציית UDAF ב-JavaScript עם הארגומנט IGNORE NULLS, BigQuery מדלג אוטומטית על שורות שבהן כל ארגומנט מצטבר מקבל את הערך NULL. השורות האלה מוחרגות לחלוטין מהצבירה ולא מועברות לפונקציית ה-JavaScript aggregate. כשמספקים את הארגומנט RESPECT NULLS, הסינון NULL מושבת וכל שורה מועברת אל UDAF של JavaScript, ללא קשר לערכים של NULL.
אם לא מציינים את הארגומנטים IGNORE NULLS וגם לא RESPECT NULLS, הארגומנט שמוגדר כברירת מחדל הוא IGNORE NULLS.
בדוגמה הבאה אפשר לראות את ההתנהגות של NULL כברירת מחדל, את ההתנהגות של IGNORE NULLS ואת ההתנהגות של RESPECT NULLS:
CREATE TEMP AGGREGATE FUNCTION SumPositive(x FLOAT64) RETURNS FLOAT64 LANGUAGE js AS r''' export function initialState() { return {sum: 0} } export function aggregate(state, x) { if (x == null) { // Use 1000 instead of 0 as placeholder for null so // that NULL values passed are visible in the result. state.sum += 1000; return; } if (x > 0) { state.sum += x; } } export function merge(state, partialState) { state.sum += partialState.sum; } export function finalize(state) { return state.sum; } '''; -- Call the JavaScript UDAF. WITH numbers AS ( SELECT * FROM UNNEST([1.0, 2.0, NULL]) AS x) SELECT SumPositive(x) AS sum, SumPositive(x IGNORE NULLS) AS sum_ignore_nulls, SumPositive(x RESPECT NULLS) AS sum_respect_nulls FROM numbers; /*-----+------------------+-------------------* | sum | sum_ignore_nulls | sum_respect_nulls | +-----+------------------+-------------------+ | 3.0 | 3.0 | 1003.0 | *-----+------------------+-------------------*/
מחיקת פונקציית UDAF
בקטע הזה מתוארות הדרכים השונות למחיקת פונקציית UDAF קבועה או זמנית אחרי שיוצרים אותה ב-BigQuery.
מחיקה של פונקציית UDAF מתמידה
כדי למחוק פונקציית UDAF מתמידה, משתמשים בהצהרה DROP FUNCTION.
צריך לכלול את קבוצת הנתונים בנתיב הפונקציה.
בדוגמה הבאה, השאילתה מוחקת פונקציית UDAF מתמשכת בשם WeightedAverage:
DROP FUNCTION IF EXISTS my_project.my_dataset.WeightedAverage;
מחיקה של פונקציית UDAF זמנית
כדי למחוק פונקציית UDAF זמנית, משתמשים בהצהרה DROP FUNCTION.
בדוגמה הבאה, השאילתה מוחקת UDAF זמני בשם WeightedAverage:
DROP FUNCTION IF EXISTS WeightedAverage;
התוקף של פונקציות UDAF זמניות פג כשהשאילתה מסתיימת. אין צורך למחוק את הפונקציה UDAF, אלא אם רוצים להסיר אותה מוקדם משאילתה עם כמה הצהרות או מפרוצדורה.
הצגת רשימה של פונקציות UDAFs
פונקציות UDAF הן סוג של שגרה. כדי לראות את כל התרחישים בקבוצת נתונים, אפשר לעיין במאמר בנושא רשימת תרחישים.
טיפים לשיפור הביצועים
כדי לשפר את הביצועים של השאילתות, כדאי לשקול את האפשרויות הבאות:
מסננים מראש את הקלט. עיבוד נתונים ב-JavaScript יקר יותר מאשר ב-SQL, ולכן מומלץ לסנן את הקלט ככל האפשר קודם ב-SQL.
השאילתה הבאה פחות יעילה כי היא מסננת את הקלט באמצעות
x > 0בקריאה ל-UDAF:SELECT JsFunc(x) FROM t;השאילתה הבאה יעילה יותר כי היא מסננת מראש את הקלט באמצעות
WHERE x > 0לפני הקריאה ל-UDAF:SELECT JsFunc(x) FROM t WHERE x > 0;כשזה אפשרי, כדאי להשתמש בפונקציות צבירה מובנות במקום ב-JavaScript. הטמעה מחדש של פונקציית צבירה מובנית ב-JavaScript איטית יותר מקריאה לפונקציית צבירה מובנית שעושה את אותו הדבר.
השאילתה הבאה פחות יעילה כי היא מיישמת UDAF:
SELECT SumSquare(x) FROM t;השאילתה הבאה יעילה יותר כי היא מיישמת פונקציה מובנית שמפיקה את אותן תוצאות כמו השאילתה הקודמת:
SELECT SUM(x*x) FROM t;פונקציות UDAFs ב-JavaScript מתאימות לפעולות צבירה מורכבות יותר, שלא ניתן לבטא באמצעות פונקציות מובנות.
שימוש יעיל בזיכרון. לסביבת העיבוד של JavaScript יש זיכרון מוגבל לכל שאילתה. יכול להיות ששאילתות JavaScript UDAF שמצטבר בהן יותר מדי מצב מקומי ייכשלו בגלל חוסר זיכרון. חשוב במיוחד לצמצם את הגודל של אובייקטים של מצבי צבירה ולהימנע ממצבי צבירה שצוברים מספר גדול של שורות.
השאילתה הבאה לא יעילה כי הפונקציה
aggregateמשתמשת בכמות בלתי מוגבלת של זיכרון כשמספר השורות שעוברות עיבוד גדול.export function initialState() { return {rows: []}; } export function aggregate(state, x) { state.rows.push(x); } ...מומלץ להשתמש בטבלאות מחולקות למחיצות (partitioned) כשזה אפשרי. בדרך כלל, פונקציות UDAF ב-JavaScript פועלות בצורה יעילה יותר כשמריצים שאילתה על טבלה עם חלוקה למחיצות בהשוואה לטבלה ללא חלוקה למחיצות, כי בטבלה עם חלוקה למחיצות הנתונים מאוחסנים בקבצים קטנים רבים בהשוואה לטבלה ללא חלוקה למחיצות, ולכן אפשר להשיג מקביליות גבוהה יותר.
מגבלות
ההגבלות שחלות על UDF חלות גם על UDAF. פרטים נוספים זמינים במאמר בנושא מגבלות על פונקציות מוגדרות על ידי המשתמש.
אפשר להעביר כארגומנטים לא מצטברים לפונקציית UDAF רק ליטרלים, פרמטרים של שאילתות ומשתני תסריט.
השימוש בפסקה
ORDER BYבקריאה לפונקציית UDAF של JavaScript לא נתמך.SELECT MyUdaf(x ORDER BY y) FROM t; -- Error: ORDER BY is unsupported.
תמחור
החיוב על UDAFs מתבצע לפי מודל התמחור הרגיל של BigQuery.
מכסות ומגבלות
ל-UDAFs יש את אותן המכסות והמגבלות שחלות על UDFs. מידע על מכסות של פונקציות UDF זמין במאמר מכסות ומגבלות.