הבלוג של גרי רשף

13/09/2010

אובייקטים זהים המתקיימים במקביל באותו Session

Filed under: Uncategorized — תגיות: , , , — גרי רשף @ 22:28

נתבונן בקטע הקוד הבא:

Declare @I Int;

Set @I=1;

Select @@SpID [@SpID],@I [@I];

יצרנו משתנה, הצבנו בו את הערך 1, והצגנו את מספר ה-Session שלנו ואת ערכו של המשתנה.

כל בר דעת מבין שהמשתנה I@ התקיים במהלך הרצת הקוד ונעלם עם סיומו, ושיכול להיות רק משתנה אחד כזה באותה נקודת זמן באותו Session. כלומר- אני יכול לפתוח חלון Query חדש ושם להגדיר במקביל עוד משתנה I@ או להריץ סקריפט נוסף לאחר שהנ"ל הסתיים ושגם בו יוגדר I@, אבל באותו Session ובאותה נקודת זמן זה לא יתכן, ומי שינסה זאת יקבל הודעת שגיאה:

Declare @I Int;

Set @I=1;

Declare @I Int;

Msg 134, Level 15, State 1, Line 3

The variable name '@I' has already been declared. Variable names must be unique within a query batch or stored procedure.

מותר לחילופין להריץ סקריפט כזה-

Declare @I Int;

Set @I=1;

Go

Declare @I Int;

פקודת Go מסיימת את ה- Batch ואת קיומו של I@, ומתחילה Batch חדש שבו ניתן להגדיר את I@ מחדש, כאשר ברור ששני ה-Batches לא התקיימו בו זמנית וכך גם לא שני המשתנים.

כעת נתבונן בקטע הקוד הבא:

Declare @I Int;

Set @I=1;

Exec('Declare @I Int;

Set @I=2;

Select @@SpID [@SpID],@I [@I];');

Select @@SpID [@SpID],@I [@I];

"דחפנו" לתוך הסקריפט פקודת SQL דינאמית, וכך באותו Session ובאותה נקודת זמן התקיימו שני משתנים בשם I@ – כל אחד בערך אחר:

clip_image002

לזכות ה-SQL Server יאמר שהם התקיימו באופן בלתי תלוי זה בזה- קטע הסקריפט שבתוך ה-SQL הדינאמי לא זיהה את המשתנה שמחוץ לו ולהיפך (נסיון לבטל את ההגדרה באחד הצדדים תוביל לשגיאה).

מה קורה עם טבלאות זמניות מקומיות? טבלה שכזו קיימת ברמת ה-Session, היא ממשיכה להתקיים גם לאחר סיום ה-Batch, אך היא בלתי נגישה מ-Sessions אחרים, והיא נעלמת כשה-Session נסגר או כשמתבצע לה Drop באופן יזום:

If Object_Id('tempdb..#T') Is Not Null Drop Table #T;

Create Table #T(I Int);

Insert Into #T Select 1;

Exec('Create Table #T(I Int);

Insert Into #T Select 2;

Select * From #T;')

Select * From #T;

clip_image004

שתי הטבלאות הזמניות התקיימו בו זמנית באותו Session!

ומה יקרה אם לא ניצור בקטע הדינאמי את הטבלה הזמנית?

If Object_Id('tempdb..#T') Is Not Null Drop Table #T;

Create Table #T(I Int);

Insert Into #T Select 1;

Exec('Insert Into #T Select 2;

Select * From #T;')

Select * From #T;

clip_image006

כעת הקטע הדינאמי השתמש בטבלה הזמנית שהוגדרה מחוץ לו, כלומר- בעדיפות ראשונה הוא בוחר בטבלה "שלו" ובעדיפות שניה בטבלה "החיצונית".

דוגמה נוספת לכך:

If Object_Id('tempdb..#T') Is Not Null Drop Table #T;

Create Table #T(I Int);

Insert Into #T Select 1;

Exec('Create Table #T(I Int);

Insert Into #T Select 2;

Select * From #T;

Drop Table #T;')

Select * From #T;

clip_image008

הקטע הדינאמי יצר לעצמו טבלה #T, הכניס לתוכה ערך, שלף אותו, וביטל אותה; ללא קשר לטבלה #T שהתקיימה במקביל מחוץ לו.

ומה יקרה במקרה משונה זה:

If Object_Id('tempdb..#T') Is Not Null Drop Table #T;

Create Table #T(I Int);

Insert Into #T Select 1;

Exec('Create Table #T(I Int);

Insert Into #T Select 2;

Select * From #T;

Drop Table #T;

Select * From #T;

Drop Table #T;')

Select * From #T;

clip_image010

הקטע הדינאמי שלף מהטבלה "שלו", ביצע לה Drop, שלף מהטבלה "החיצונית", ביצע לה Drop, ואז השליפה חיצונית נכשלה מכיוון ש-#T כבר לא היה קיים.

ולסיום- מה נאמר על זה:


If Object_Id('tempdb..#T') Is Not Null Drop Table #T;

Create Table #T(I Int);

Insert Into #T Select 1;

Exec('Create Table #T(I Int);

Insert Into #T Select 2;

    Exec(''Create Table #T(I Int);

     Insert Into #T Select 3;

    Select * From #T'')

Select * From #T')

Select * From #T;

clip_image012

בתוך ה-Exec קיננתי Exec פנימי, וכעת התקיימו במקביל שלוש טבלאות #T באותו Session.

התנהגות דומה ניתן לראות בפרוצדורות המשתמשות בטבלאות זמניות.

ניצור פרוצדורה רקורסיבית שיוצרת טבלה זמנית, מכניסה לתוכה את הפרמטר שאיתו היא הופעלה, ומפעילה את עצמה רקורסיבית עם פרמטר קטן ב-1 (כשהפרמטר מתאפס- הרקורסיה נעצרת):

If Object_Id('tempdb..#P1') Is Not Null Drop Proc #P1;

Go


If Object_Id('tempdb..#T1') Is Not Null Drop Table #T1;

Go


Create Proc #P1 @I Int As

Create Table #T1(I Int);

Insert Into #T1 Select @I;

Select * From #T1;

If @I>=1

    Begin

    Set    @I=@I-1;

    Exec #P1 @I;

    End

Go


Exec #P1 2;

Go

image

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

אם ננסה לשלוף Select * From #T1 נקבל הודעת שגיאה- הטבלאות התקיימו רק במהלך ריצת הפרוצדורות.

ננסה משהו אחר- את הטבלה הזמנית ניצור מחוץ לפרוצדורה ולא במהלכה:

If Object_Id('tempdb..#P2') Is Not Null Drop Proc #P2;

Go


Create Proc #P2 @I Int As

Insert Into #T2 Select @I;

Select * From #T2;

If @I>=1

    Begin

    Set    @I=@I-1;

    Exec #P2 @I;

    End

Go


If Object_Id('tempdb..#T2') Is Not Null Drop Table #T2;

Go


Create Table #T2(I Int);

Go


Exec #P2 2;

Go

image

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

לסיכום: פרוצדורה שפונה לטבלה זמנית- תחפש אותה בתוך עצמה, אם לא- תפנה לפרוצדורה או ל-Session שהפעילו אותה, וכך הלאה; כאשר SQL דינאמי דינו כדין פרוצדורה.

מנגד- לא ניתן להסתכל בכיוון ההפוך כלפי מטה ולראות את האובייקטים הזמניים שם.

מודעות פרסומת

להגיב »

עדיין אין תגובות.

RSS feed for comments on this post. TrackBack URI

כתיבת תגובה

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

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s

בלוג בוורדפרס.קום.

%d בלוגרים אהבו את זה: