טרנספורמציה של נתונים באמצעות שפת טיפול בנתונים (DML)

שפת הטיפול בנתונים (DML) ב-BigQuery מאפשרת לעדכן, להוסיף ולמחוק נתונים מטבלאות BigQuery.

אפשר להריץ פקודות DML בדיוק כמו שמריצים פקודות SELECT, בתנאים הבאים:

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

מגבלות

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

  • אי אפשר לשנות שורות שנכתבו לאחרונה באמצעות שיטת הסטרימינג tabledata.insertall באמצעות שפת מניפולציה של נתונים (DML), כמו פקודות UPDATE,‏ DELETE,‏ MERGE או TRUNCATE. הפעולות האחרונות של כתיבה הן אלה שהתרחשו ב-30 הדקות האחרונות. אפשר לשנות את כל השורות האחרות בטבלה באמצעות פקודות UPDATE,‏ DELETE,‏ MERGE או TRUNCATE. יכול להיות שיחלפו עד 90 דקות עד שהנתונים שמוזרמים יהיו זמינים לפעולות העתקה.

    לחלופין, אפשר לשנות שורות שנכתבו לאחרונה באמצעות Storage Write API באמצעות פקודות UPDATE,‏ DELETE או MERGE. מידע נוסף זמין במאמר שימוש בשפת טיפול בנתונים (DML) עם נתונים שהועברו לאחרונה בסטרימינג.

  • אין תמיכה בשאילתות משנה קשורות בתוך משפטי when_clause, ‏ search_condition,‏ merge_update_clause או merge_insert_clause עבור משפטי MERGE.

  • בשאילתות שמכילות הצהרות DML אי אפשר להשתמש בטבלת תווים כלליים לחיפוש כיעד של השאילתה. לדוגמה, אפשר להשתמש בטבלת תו כללי לחיפוש בסעיף FROM של שאילתת UPDATE, אבל אי אפשר להשתמש בטבלת תו כללי לחיפוש כיעד של פעולת UPDATE.

פקודות DML

בקטעים הבאים מתוארים הסוגים השונים של הצהרות DML ומוסבר איך אפשר להשתמש בהן.

INSERT דוחות

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

INSERT dataset.Inventory (product, quantity)
VALUES('whole milk', 10),
      ('almond milk', 20),
      ('coffee beans', 30),
      ('sugar', 0),
      ('matcha', 20),
      ('oat milk', 30),
      ('chai', 5)

/+-------------------+----------+
 |      product      | quantity |
 +-------------------+----------+
 | almond milk       |       20 |
 | chai              |        5 |
 | coffee beans      |       30 |
 | matcha            |       20 |
 | oat milk          |       30 |
 | sugar             |        0 |
 | whole milk        |       10 |
 +-------------------+----------+/

מידע נוסף על פקודות INSERT זמין במאמר בנושא פקודת INSERT.

DELETE דוחות

כדי למחוק שורות בטבלה, משתמשים בהצהרה DELETE. בדוגמה הבאה נמחקו כל השורות בטבלה dataset.Inventory עם הערך quantity 0.

DELETE dataset.Inventory
WHERE quantity = 0

/+-------------------+----------+
 |      product      | quantity |
 +-------------------+----------+
 | almond milk       |       20 |
 | chai              |        5 |
 | coffee beans      |       30 |
 | matcha            |       20 |
 | oat milk          |       30 |
 | whole milk        |       10 |
 +-------------------+----------+/

כדי למחוק את כל השורות בטבלה, משתמשים במקום זאת בהצהרת TRUNCATE TABLE. מידע נוסף על DELETE דוחות זמין במאמר בנושא DELETE דוחות.

TRUNCATE דוחות

משתמשים בהצהרת TRUNCATE כדי להסיר את כל השורות מטבלה, אבל משאירים את המטא-נתונים של הטבלה ללא שינוי, כולל סכימת הטבלה, התיאור והתוויות. בדוגמה הבאה מוסרות כל השורות מהטבלה dataset.Inventory.

TRUNCATE dataset.Inventory

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

UPDATE דוחות

משתמשים בהצהרת UPDATE כדי לעדכן שורות קיימות בטבלה. UPDATE ההצהרה חייבת לכלול גם את מילת המפתח WHERE כדי לציין תנאי. בדוגמה הבאה, הערך quantity של השורות יופחת ב-10 עבור מוצרים שמכילים את המחרוזת milk.

UPDATE dataset.Inventory
SET quantity = quantity - 10,
WHERE product LIKE '%milk%'

/+-------------------+----------+
 |      product      | quantity |
 +-------------------+----------+
 | almond milk       |       10 |
 | chai              |        5 |
 | coffee beans      |       30 |
 | matcha            |       20 |
 | oat milk          |       20 |
 | whole milk        |        0 |
 +-------------------+----------+/

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

MERGE דוחות

ההצהרה MERGE משלבת את הפעולות INSERT, UPDATE ו-DELETE להצהרה אחת, ומבצעת את הפעולות באופן אטומי כדי למזג נתונים מטבלה אחת לטבלה אחרת. מידע נוסף ודוגמאות לגבי הצהרת MERGE זמינים במאמר בנושא הצהרת MERGE.

משימות מקבילות

מערכת BigQuery מנהלת את המקביליות של הצהרות DML שמוסיפות, משנות או מוחקות שורות בטבלה.

נסו שוב לבצע את הפעולה באמצעות השהיה מעריכית לפני ניסיון חוזר (exponential backoff) בין הניסיונות.

מקביליות של פקודות INSERT DML

במהלך כל תקופה של 24 שעות, 1,500 ההצהרות הראשונות של INSERT מופעלות מיד אחרי שהן נשלחות. אחרי שמגיעים למגבלה הזו, מספר ההצהרות בו-זמנית של INSERT שכותבות לטבלה מוגבל ל-10. הצהרות נוספות של INSERT מתווספות לתור של PENDING. אפשר להוסיף לתור עד 100 הצהרות לכל טבלה בכל זמן נתון.INSERT כשמסתיימת הפעלה של הצהרה, ההצהרה הבאה מוסרת מהתור ומופעלת.INSERTINSERT

אם אתם צריכים להריץ הצהרות DML INSERT בתדירות גבוהה יותר, כדאי להשתמש ב-Storage Write API כדי להזרים נתונים לטבלה.

מקבילות של פקודות DML מסוג UPDATE,‏ DELETE ו-MERGE

פקודות ה-DML‏ UPDATE,‏ DELETE ו-MERGE נקראות פקודות DML משתנות. אם שולחים הצהרת DML אחת או יותר לשינוי טבלה בזמן שמשימות DML אחרות לשינוי הטבלה עדיין פועלות (או בהמתנה), מערכת BigQuery מריצה עד 2 מהן בו-זמנית, ואז עד 20 משימות נוספות מוכנסות לתור כPENDING. כשמשימה שהייתה בהרצה מסתיימת, המשימה הבאה שממתינה בתור מוצאת מהתור ומופעלת. הצהרות DML לשינוי בתור משתפות תור לכל טבלה באורך מקסימלי של 20. הצהרות נוספות מעבר לאורך התור המקסימלי של כל טבלה נכשלות עם הודעת השגיאה: Resources exceeded during query execution: Too many DML statements outstanding against table PROJECT_ID:DATASET.TABLE, limit is 20.

משימות DML אינטראקטיביות בעדיפות גבוהה שנמצאות בתור יותר מ-7 שעות נכשלות עם הודעת השגיאה הבאה:

DML statement has been queued for too long

התנגשויות בפקודות DML

שינוי של פקודות DML שמופעלות בו-זמנית בטבלה גורם להתנגשויות בפקודות DML כשהפקודות מנסות לשנות את אותה המחיצה. ההצהרות יצליחו כל עוד הן לא ישנו את אותה מחיצה. ‫BigQuery מנסה להפעיל מחדש הצהרות שנכשלו עד שלוש פעמים.

  • פקודת INSERT DML שמוסיפה שורות לטבלה לא מתנגשת עם אף פקודת DML אחרת שפועלת במקביל.

  • פקודת MERGE DML לא מתנגשת עם פקודות DML אחרות שפועלות במקביל, כל עוד הפקודה רק מוסיפה שורות ולא מוחקת או מעדכנת שורות קיימות. זה יכול לכלול הצהרות MERGE עם סעיפים UPDATE או DELETE, כל עוד הסעיפים האלה לא מופעלים כשהשאילתה מורצת.

DML פרטני

DML ברמת גרנולריות גבוהה הוא שיפור ביצועים שנועד לייעל את הביצוע של פקודות UPDATE, DELETE ו-MERGE (שנקראות גם פקודות DML משתנות).

שיקולי ביצועים

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

שינוי נתונים ברמה מפורטת הוא שיפור ביצועים שנועד לבצע אופטימיזציה של הצהרות DML משתנות. השיפור הזה מציג גישה מפורטת יותר שמטרתה לצמצם את כמות הנתונים שצריך לשכתב ברמת קבוצת הקבצים. הגישה הזו יכולה להפחית באופן משמעותי את זמן העיבוד, את קלט/פלט ואת זמן המשבצת שנדרשים לעיבוד של משימות DML לשינוי נתונים.

יש כמה שיקולים שקשורים לביצועים שכדאי להביא בחשבון כשמשתמשים בפקודות DML עם גרנולריות גבוהה:

  • פעולות DML ברמת גרנולריות גבוהה מעבדות נתונים שנמחקו בגישה היברידית שמפיצה את עלויות השכתוב על פני מוטציות רבות בטבלה. כל פעולת DML עשויה לעבד חלק מהנתונים שנמחקו, ואז להעביר את עיבוד הנתונים שנמחקו שנותרו לתהליך garbage collection ברקע. מידע נוסף זמין במאמר בנושא שיקולים לגבי נתונים שנמחקו.
  • בטבלאות עם פעולות DML תכופות של שינוי, יכול להיות שתהיה עלייה בחביון של שאילתות SELECT ועבודות DML עוקבות. כדי להעריך את ההשפעה של הפעלת התכונה הזו, כדאי להשוות את הביצועים של רצף ריאליסטי של פעולות DML וקריאות עוקבות.
  • הפעלת DML ברמת דיוק גבוהה לא תפחית את כמות הבייטים שנסרקים בהצהרת ה-DML עצמה.

הפעלת DML עם גרנולריות גבוהה

כדי להפעיל DML ברמת גרנולריות גבוהה, מגדירים את האפשרות enable_fine_grained_mutations table לערך TRUE כשמריצים הצהרת DDL מסוג CREATE TABLE או ALTER TABLE.

כדי ליצור טבלה חדשה עם DML ברמת גרנולריות גבוהה, משתמשים בהצהרה CREATE TABLE:

CREATE TABLE mydataset.mytable (
  product STRING,
  inventory INT64)
OPTIONS(enable_fine_grained_mutations = TRUE);

כדי לשנות טבלה קיימת באמצעות DML ברמת גרנולריות גבוהה, משתמשים במשפט ALTER TABLE:

ALTER TABLE mydataset.mytable
SET OPTIONS(enable_fine_grained_mutations = TRUE);

כדי לשנות את כל הטבלאות הקיימות במערך נתונים באמצעות DML עם רמת פירוט גבוהה, משתמשים בהצהרת ALTER TABLE:

FOR record IN
 (SELECT CONCAT(table_schema, '.', table_name) AS table_path
 FROM mydataset.INFORMATION_SCHEMA.TABLES)
DO
 EXECUTE IMMEDIATE
   "ALTER TABLE " || record.table_path || " SET OPTIONS(enable_fine_grained_mutations = TRUE)";
END FOR;

אחרי שהאפשרות enable_fine_grained_mutations מוגדרת לערך TRUE, פקודות DML לשינוי נתונים מופעלות עם יכולות DML ברמת גרנולריות גבוהה, ומשתמשות בתחביר הקיים של פקודות DML.

כדי לדעת אם הופעלה בטבלה בקרת DML ברמת הגרנולריות, מריצים שאילתה בתצוגה INFORMATION_SCHEMA.TABLES. בדוגמה הבאה נבדק אילו טבלאות במערך נתונים הופעלו עם התכונה הזו:

SELECT
  table_schema AS datasetId,
  table_name AS tableId,
  is_fine_grained_mutations_enabled
FROM
  DATASET_NAME.INFORMATION_SCHEMA.TABLES;

מחליפים את DATASET_NAME בשם של מערך הנתונים שרוצים לבדוק אם יש בו טבלאות שמופעל בהן DML ברמת גרנולריות גבוהה.

השבתה של DML עם גרעין עדין

כדי להשבית DML ברמת גרנולריות גבוהה מטבלה קיימת, משתמשים בהצהרת ALTER TABLE.

ALTER TABLE mydataset.mytable
SET OPTIONS(enable_fine_grained_mutations = FALSE);

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

תמחור

הפעלת DML עם גרנולריות גבוהה בטבלה עלולה להיות כרוכה בעלויות נוספות. העלויות האלה כוללות:

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

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

משימות עיבוד של נתונים שנמחקו שהועברו ל-DML מפורט נחשבות למשימות ברקע, ולכן נדרש שימוש בBACKGROUND סוג הקצאת ההזמנה ולא בQUERY סוג הקצאת ההזמנה. בפרויקטים שבהם מתבצעות פעולות DML ברמת פירוט גבוהה ללא הקצאת BACKGROUND, המערכת משתמשת בתמחור לפי דרישה כדי לעבד את המשימות של הנתונים שנמחקו והועברו.

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

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

תופסים משבצות שהוקצו עם סוג QUERY בזמן ההרצה של ההצהרה.
העברה של משימות עיבוד נתונים שנמחקו משתמשים בהערכת גודל DML רגילה כדי לקבוע את החישובים של בייטים שנסרקו לפי דרישה כשמריצים משימות עיבוד של נתונים שנמחקו. צריכת משבצות שהוקצו עם סוג BACKGROUND כשמריצים משימות של עיבוד נתונים שנמחקו.

שיקולים לגבי נתונים שנמחקו

פעולות DML ברמת פירוט גבוהה משתמשות בגישה היברידית לניהול נתונים שנמחקו, שמשלבת עיבוד מוטמע עם garbage collection שהועבר כדי לחלק את עלויות השכתוב ולבצע אופטימיזציה של הביצועים בכמה פקודות DML משתנות שהונפקו לגבי טבלה.

במהלך הרצת פקודת DML לשינוי, מערכת BigQuery מנסה לבצע חלק מ-garbage collection הרלוונטי מפקודות DML קודמות בשורה. כל הנתונים שנמחקו ולא טופלו באופן מיידי מועברים לתהליך ברקע לניקוי מאוחר יותר.

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

בפרויקטים שבהם מתבצעות פעולות DML מדויקות באמצעות תמחור לפי דרישה, או ללא הקצאת BACKGROUND, מתבצע עיבוד של משימות של garbage collection באמצעות משאבים פנימיים של BigQuery, והחיוב מתבצע לפי תעריפי התמחור לפי דרישה. מידע נוסף מפורט במאמר בנושא תמחור.

התזמון של משימות ניקוי הזיכרון (garbage collection) שהועברו לביצוע במקום אחר נקבע לפי התדירות של פעילות DML בטבלה והזמינות של המשאבים, אם משתמשים בהקצאה BACKGROUND:

  • בטבלאות עם פעולות DML רציפות של שינוי, כל פעולת DML מעבדת חלק מעומס העבודה של איסוף האשפה כדי להבטיח ביצועים עקביים של קריאה וכתיבה. כתוצאה מכך, איסוף האשפה מעובד באופן קבוע בזמן שפעולות ה-DML הבאות מבוצעות.
  • אם לא מתבצעת פעילות DML נוספת בטבלה, garbage collection שהועבר לאחסון זמני מופעל באופן אוטומטי ברגע שהנתונים שנמחקו מגיעים לגיל 5 ימים.
  • במקרים נדירים, יכול להיות שיעבור יותר זמן עד שהעיבוד של הנתונים שנמחקו יושלם.

כדי לזהות משימות עיבוד של נתונים שנמחקו באמצעות DML ברמת גרנולריות גבוהה, שמועברות לעיבוד במקום אחר, מריצים שאילתה בתצוגה INFORMATION_SCHEMA.JOBS:

SELECT
  *
FROM
  region-us.INFORMATION_SCHEMA.JOBS
WHERE
  job_id LIKE "%fine_grained_mutation_garbage_collection%"

מגבלות

הטבלאות שמופעל בהן DML ברמת גרנולריות גבוהה כפופות למגבלות הבאות:

  • לא מומלץ להשתמש ב-DML ברמת גרנולריות גבוהה בטבלאות גדולות עם מחיצות שעוברות שינויים לעיתים קרובות, אם גודלן עולה על 2TB. יכול להיות שיהיה עומס זיכרון נוסף בטבלאות האלה בשאילתות הבאות, מה שעלול להוביל לזמן טעינה ארוך יותר או לשגיאות בשאילתות.
  • אפשר להריץ רק פקודת DML אחת שמשנה נתונים בכל פעם בטבלה שמופעל בה DML ברמת גרנולריות גבוהה. משימות עוקבות מתווספות לתור כ-PENDING. מידע נוסף על התנהגות בו-זמנית של DML עם שינויים זמין במאמר בנושא בו-זמניות של DML עם UPDATE,‏ DELETE ו-MERGE.
  • אי אפשר להשתמש בשיטה tabledata.list כדי לקרוא תוכן מטבלה שמופעל בה DML עם הרשאות גישה ברמת השורה. במקום זאת, שולחים שאילתה לטבלה באמצעות הצהרת SELECT כדי לקרוא רשומות בטבלה.
  • אי אפשר לראות תצוגה מקדימה של טבלה שמופעל בה DML ברמת גרנולריות גבוהה באמצעות מסוף BigQuery.
  • אי אפשר להעתיק טבלה עם DML ברמת דיוק גבוהה אחרי שמריצים את ההצהרות UPDATE, DELETE או MERGE.
  • אי אפשר ליצור תמונת מצב של טבלה או שיבוט של טבלה עם DML ברמת דיוק גבוהה שהופעל אחרי הפעלת פקודת UPDATE, DELETE או MERGE.
  • אי אפשר להפעיל DML ברמת גרנולריות גבוהה בטבלה במערך נתונים משוכפל, ואי אפשר לשכפל מערך נתונים שמכיל טבלה עם DML ברמת גרנולריות גבוהה.
  • פקודות DML שמופעלות בטרנזקציה עם כמה פקודות לא עוברות אופטימיזציה באמצעות DML מדויק.
  • אי אפשר להפעיל DML ברמת דיוק גבוהה בטבלאות זמניות שנוצרו באמצעות ההצהרה CREATE TEMP TABLE.
  • מטא-נתונים המשתקפים בתוך התצוגות והתצוגות והתצוגות יכולות לכלול באופן זמני נתונים שנמחקו לאחרונה באמצעות DML מפורט עד שמשימות איסוף האשפה ברקע יושלמו.

שיטות מומלצות

כדי להפיק את הביצועים הטובים ביותר, Google ממליצה להשתמש בדפוסים הבאים:

  • אל תשלחו מספרים גדולים של עדכונים או הוספות של שורות בודדות. במקום זאת, כדאי לקבץ פעולות DML כשאפשר. מידע נוסף זמין במאמר בנושא DML statements that update or insert single rows (הצהרות DML שמעדכנות או מוסיפות שורות בודדות).

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

  • לא מומלץ לחלק טבלאות למחיצות אם כמות הנתונים בכל מחיצה קטנה וכל עדכון משנה חלק גדול מהמחיצות.

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

    UPDATE mydataset.mytable
    SET string_col = 'some string'
    WHERE id BETWEEN 54 AND 75;

    הנה דוגמה דומה לסינון לפי רשימה קטנה של ערכי עמודות:

    UPDATE mydataset.mytable
    SET string_col = 'some string'
    WHERE id IN (54, 57, 60);

    במקרים כאלה, כדאי לשקול לבצע אשכול על העמודה id.

  • אם אתם צריכים פונקציונליות של OLTP, כדאי להשתמש בשאילתות מאוחדות של Cloud SQL, שמאפשרות ל-BigQuery לשלוח שאילתות לנתונים שמאוחסנים ב-Cloud SQL.

  • כדי לפתור את שגיאת המכסה Too many DML statements outstanding against table, ולמנוע אותה, צריך לפעול לפי ההנחיות לפתרון השגיאה בדף פתרון בעיות ב-BigQuery.

שיטות מומלצות לאופטימיזציה של ביצועי השאילתות מפורטות במאמר מבוא לאופטימיזציה של ביצועי השאילתות.

המאמרים הבאים