סבא שלי ז"ל היה חסכן גדול. לא מחמת חסרון כיס – הוא היה אדם עמיד – אלא מפני שזה היה אופיו, ומסורת זו עוברת במשפחתנו מדור לדור. "בזבז בגדול חסוך בקטן"- כך ניתן לתאר בדיעבד את השקפת עולמו של זה שהיה מנסה לנהוג במכונית עד טיפת הדלק האחרונה בטרם מילא את המיכל, וחשב שבכך הוא חוסך.. ברור שלא רק שהוא לא חסך, אלא שהרכב היה נתקע לפעמים ללא דלק, ואז היה צריך ללכת עם ג'ריקן ולחפש תחנת דלק בסביבה. שאר עלילותיו של הסבא ראויים לפוסט נפרד, ולכן לא אאריך כאן. גם אבי יבל"א חסכן מופלג, ובמקרה זה לא די בפוסט נפרד ויש צורך בבלוג נפרד, ולכן אגש לנושא שלשמו התכנסנו- שיטות מועילות לחסכון במשאבי SQL Server, חסכון שהתועלת בו בטלה בשישים לעומת הטרחה ומחיר הטעויות שעלולים לעשות, אבל זו מסורת אבות..
למלא טבלה בנתונים – כל אחד יכול, למשל במספרי object_id של האובייקטים השונים במערכת:
If Object_ID('tempdb..#T13') Is Not Null Drop Table #T1;
Create Table #T1(Object_ID Int);
Insert
Into #T1(Object_ID)
Select Object_ID
From sys.objects;
נניח שאני רוצה למלא שתי טבלאות בנתונים, כל זאת בפעולה – מבלי לבצע את הנ"ל פעמיים: ניתן לעשות זאת באמצעות האופרטור Output:
If Object_ID('tempdb..#T1') Is Not Null Drop Table #T1;
Create Table #T1(Object_ID Int);
If Object_ID('tempdb..#T2') Is Not Null Drop Table #T2;
Create Table #T2(Object_ID Int);
Insert
Into #T2(Object_ID)
Select Object_ID
From (Insert
Into #T1(Object_ID)
Output Inserted.Object_ID
Select Object_ID
From sys.objects) T;
כלומר- האופרטור מציג את הרשומות שנקלטו ל-T1# בעזרת Output (ב-Subquery), והן נקלטות על ידי השאילתה החיצונית לתוך #T2.
האם ניתן לקלוט את הנתונים לטבלה שלישית באותה שיטה על ידי רמת קינון נוספת? למרבה הצער לא:
במקרה זה רווח והצלה יעמדו לנו ממקום אחר, למשל- נריץ את השאילתה שמעדכנת שתי טבלאות (בצירוף Output לשאילתה החיצונית) בעזרת Exec,
ואת הפלט של ה-Exec נקלוט לטבלה שלישית:
If Object_ID('tempdb..#T1') Is Not Null Drop Table #T1;
Create Table #T1(Object_ID Int);
If Object_ID('tempdb..#T2') Is Not Null Drop Table #T2;
Create Table #T2(Object_ID Int);
If Object_ID('tempdb..#T3') Is Not Null Drop Table #T3;
Create Table #T3(Object_ID Int);
Insert
Into #T3
Exec('Insert
Into #T2(Object_ID)
Output Inserted.*
Select Object_ID
From (Insert
Into #T1(Object_ID)
Output Inserted.Object_ID
Select Object_ID
From sys.objects) T')
כאמור, יש רק פניה אחת לטבלה sys.objects, ושלוש פקודות Insert Into.
האם ניתן לקלוט את הנתונים לטבלה רביעית על ידי ביצוע Output לפעולת ה-Exec? לדאבוני לא:
כלומר- לא ניתן להשתמש ב-Output בהפעלה על ידי Exec, וממילא לא ניתן לקלוט זאת.
ניסיתי להיעזר ב-OpenRowset שהוא מכשיר השרצים של SQL Server ומאפשר לעשות הרבה דברים שאסור, אך משום מה התנהגותו תמוהה:
Use tempdb;
Go
If Object_ID('T1') Is Not Null Drop Table T1;
Create Table T1(Object_ID Int);
Select *
From OPENROWSET('SQLOLEDB','Server=.;Trusted_Connection=yes;',
'Insert
Into tempdb..T1(Object_ID)
Output Inserted.*
Select Object_ID
From sys.objects')
Select * From T1;
לכאורה הריצה מצליחה וה-Output מוצג כנדרש, אך הטבלה נותרת ריקה.
בהזדמנות אנסה לתרץ זאת.
ולסיום- כשאבי מכין פסטה הוא מנסה לשפוך את תכולת הסיר בו היא התבשלה לתוך מסננת, וחיש לקלוט את המים לתוך הסיר עצמו- גם כדי לחסוך בכלים וגם כדי לחסוך במים. קצת מסובך ומועד לפורענות, ומזכיר את צ'רנוחה מביתר ירושלים עליו מספרים שהיה מרים כדור קרן, ורץ לרחבה לנגוח אותו פנימה:
If Object_ID('#T1') Is Not Null Drop Table #T1;
Select Object_ID
Into #T1
From sys.objects;
Go
Delete #T1
Output Deleted.Object_ID
Into #T1(Object_ID);
Select * From #T1;
בשלב ראשון יצרנו טבלה עם נתונים,
ולאחר מכן מוחקים ממנה את הנתונים תוך הפנייתם חזרה אליה,
וכך כמו סיר הפסטה או פך השמן- מאליה היא מתמלאת.
לסיכום- הצלחנו למלא שלוש טבלאות במקביל בנתוני טבלה אחת, ומי שמצליח יותר- אשמח לשמוע כיצד.