פונקציות צבירה מוגדרות על ידי משתמש
פונקציות צבירה בהגדרת המשתמש (UDAF) ב-BigQuery מאפשרות לכם ליצור לוגיקה של צבירה בהתאמה אישית באמצעות קוד SQL או JavaScript. אתם יכולים לעבד את עמודות הקלט כקבוצה כדי לבצע חישובים מיוחדים בכמה שורות בו-זמנית, ולהחזיר את התוצאה כערך יחיד.
יצירת פונקציית SQL UDAF
בקטע הזה מתוארות הדרכים השונות שבהן אפשר ליצור פונקציית SQL UDAF זמנית או קבועה ב-BigQuery.
יצירת פונקציית SQL UDAF קבועה
אפשר ליצור פונקציית SQL UDAF מתמידה, כלומר אפשר להשתמש בה שוב בכמה שאילתות. אפשר להפעיל פונקציות 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) );
יצירת פונקציית SQL UDAF זמנית
אפשר ליצור פונקציית SQL UDAF זמנית, כלומר פונקציית UDAF שקיימת רק בהיקף של שאילתה, סקריפט, סשן או פרוצדורה אחת.
כדי ליצור UDAF זמנית, משתמשים בהצהרה CREATE AGGREGATE FUNCTION עם מילת המפתח TEMP או TEMPORARY.
לדוגמה, השאילתה הבאה יוצרת פונקציית UDAF זמנית בשם ScaledAverage:
CREATE TEMP AGGREGATE FUNCTION ScaledAverage( dividend FLOAT64, divisor FLOAT64) RETURNS FLOAT64 AS ( AVG(dividend / divisor) );
שימוש בפרמטרים מצטברים ולא מצטברים
אפשר ליצור פונקציית SQL UDAF שיש לה גם פרמטרים מצטברים וגם פרמטרים לא מצטברים.
פונקציות 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 ) );
יצירת פונקציית JavaScript UDAF
בקטע הזה מתוארות הדרכים השונות שבהן אפשר ליצור פונקציית UDAF ב-JavaScript ב-BigQuery. יש כמה כללים שצריך לפעול לפיהם כשיוצרים פונקציית UDAF ב-JavaScript:
גוף ה-UDAF ב-JavaScript חייב להיות מחרוזת מילולית במירכאות שמייצגת את קוד ה-JavaScript. במאמר פורמטים של ליטרלים במירכאות מוסבר על הסוגים השונים של ליטרלים של מחרוזות במירכאות שאפשר להשתמש בהם.
מותר להשתמש רק בקידודים מסוגים מסוימים. למידע נוסף, קראו את המאמר קידודים מותרים של סוגי SQL ב-UDAF של JavaScript.
גוף הפונקציה של JavaScript חייב לכלול ארבע פונקציות JavaScript שמאתחלות, צוברות, ממזגות ומסיימות את התוצאות של ה-UDAF של JavaScript (
initialState,aggregate,mergeו-finalize). מידע נוסף זמין בקטע פונקציות צבירה נדרשות של 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
אפשר ליצור פונקציית JavaScript UDAF זמנית, כלומר פונקציית 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
אפשר ליצור פונקציית JavaScript UDAF שיש לה גם פרמטרים מצטברים וגם פרמטרים לא מצטברים.
פונקציות 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
אפשר להרחיב את פונקציות ה-UDAF ב-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
בפונקציות UDAF ב-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. שורות כאלה מוחרגות מהצבירה לחלוטין ולא מועברות לפונקציית aggregate של JavaScript. כשמציינים את הארגומנט 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, אלא אם רוצים להסיר אותו מוקדם משאילתה עם כמה הצהרות או מפרוצדורה.
רשימת פונקציות UDAF
פונקציות 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;פונקציות UDAF ב-JavaScript מתאימות לפעולות צבירה מורכבות יותר, שלא ניתן לבטא באמצעות פונקציות מובנות.
שימוש יעיל בזיכרון. בסביבת העיבוד של JavaScript יש זיכרון מוגבל לכל שאילתה. שאילתות JavaScript UDAF שצוברות יותר מדי מצב מקומי עלולות להיכשל בגלל ניצול יתר של הזיכרון. חשוב במיוחד לצמצם את הגודל של אובייקטים של מצב צבירה, ולהימנע ממצבי צבירה שצוברים מספר גדול של שורות.
השאילתה הבאה לא יעילה כי הפונקציה
aggregateמשתמשת בכמות זיכרון לא מוגבלת כשמספר השורות שמעובדות גדול.export function initialState() { return {rows: []}; } export function aggregate(state, x) { state.rows.push(x); } ...אם אפשר, השתמשו בטבלאות מחולקות למחיצות. בדרך כלל, פונקציות UDAF ב-JavaScript פועלות בצורה יעילה יותר כשמריצים שאילתות על טבלה מחולקת למחיצות בהשוואה לטבלה לא מחולקת למחיצות, כי בטבלה מחולקת למחיצות הנתונים מאוחסנים בהרבה קבצים קטנים יותר בהשוואה לטבלה לא מחולקת למחיצות, וכך מתאפשרת מקביליות גבוהה יותר.
מגבלות
לפונקציות UDAF יש את אותן המגבלות שחלות על פונקציות UDF. לפרטים נוספים, ראו מגבלות של פונקציות UDF.
אפשר להעביר רק ערכים מילוליים, פרמטרים של שאילתות ומשתני סקריפטים כארגומנטים שאינם צבירה ל-UDAF.
אי אפשר להשתמש בסעיף
ORDER BYבקריאה לפונקציית JavaScript UDAF.SELECT MyUdaf(x ORDER BY y) FROM t; -- Error: ORDER BY is unsupported.
תמחור
החיוב על פונקציות UDAF מתבצע לפי מודל התמחור הסטנדרטי של BigQuery.
מכסות ומגבלות
המכסות והמגבלות של פונקציות UDAF זהות לאלה של פונקציות UDF. למידע על מכסות של פונקציות UDF, ראו מכסות ומגבלות.