Context_Info – משתנה גלובאלי ברמת ה-Session

13/03/2012

2 תגובות

ב-SQL Server אין משתנים גלובליים שקיימים מחוץ ל-Scope של הבלוק או האובייקט בו הם מוגדרים, והאופציה הכי קרובה לזה היא Context_Info שהוא מעין משתנה גלובאלי מסוג (Varbinary(128 (כלומר- יכול להכיל עד 128 תווים) שקיים עד שה-Session מסתיים.
לא ניתן להכניס לתוכו ערך באופן ישיר תוך ביצוע Cast/Convert אלא רק ערכים בינאריים (מפורשים או משתנים), למשל:

Declare @VrBn Varbinary(128);

Set     @VrBn=Cast('Hello World' As Varbinary(128));

Set     Context_Info @VrBn;

שליפת הערך מתוכו נעשית כך:

Select Cast(Context_Info() As Varchar(Max));

clip_image002

להלן שתי דוגמאות כיצד ניתן להשתמש בו.

העברת מידע בין פרוצדורות: קצת בעייתי לדעת בתוך הפרוצדורה מי הפעיל אותה. ניתן לאלתר פתרון מעט בעייתי אם נוסיף לכל פרוצדורה משתנה מסוג Sysname, ומי שיפעיל אותה ישלח לה את השם של עצמו (למשל- פרוצדורה P_1 תפעיל את פרוצדורה P_2 כך: Exec P_2 'P_1';). פתרון חלופי יכול להיות להכניס את השם העצמי ל-Context_Info:

Use tempdb;

Go

 

If Object_Id('P_1','P') Is Not Null Drop Proc P_1;

Go

 

Create  Proc P_1 As

Begin   Tran

Declare @VrBn Varbinary(128);

Set     @VrBn=Cast(IsNull(Object_Name(@@ProcID),'') As Varbinary(128));

Set     Context_Info @VrBn;

Exec    P_2;

Rollback;

Go

 

If Object_Id('P_2','P') Is Not Null Drop Proc P_2;

Go

 

Create Proc P_2 As

Select Cast(Context_Info() As Sysname) [Executed_By];

Go

וכעת נפעיל את פרוצדורה P_1 (שתפעיל את P_2 ותעביר לה את השם העצמי דרך Context_Info):

Exec P_1;

 

Select Cast(Context_Info() As Sysname) [Executed_By];

clip_image004

בפרוצדורה P_1 פתחתי טרנזקציה וביטלתי אותה על ידי Rollback כדי להמחיש שלמרות זאת- הערך ב-Context_Info נשמר;

והוא אמנם מוצג גם מתוך P_2 (בתוך הטרנזקציה) וגם לאחר שהריצות של שתי הפרוצדורות הסתיימו.

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

מעקב אחר ריצה של קוד: כאשר קוד מורץ ב-SSMS ניתן לעקוב אחר הריצה דרך לשוניות ה-Results וה-Messages בחציו התחתון של המסך, וכשהוא מורץ בג'וב ניתן להפנות את החיוויים השוטפים לקובץ חיצוני; אבל כשהקוד מורץ בדרך אחרת – תוכנה חיצונית שמפעילה אותו, Service Broker, משתמש אחר שאין לנו קשר איתו וכו' – ניתן לנצל את ה-Context_Info כדי לאחסן בו חיוויים לגבי השלב אליו הגיעה הריצה. למשל:

If Object_Id('P_3','P') Is Not Null Drop Proc P_3;

Go

 

Create Proc P_3 As

Declare @VrBn Varbinary(128);

 

Set @VrBn=Cast('P_3 begins' As Varbinary(128));

Set Context_Info @VrBn;

 

WaitFor Delay '00:01';

 

Set @VrBn=Cast('P_3 in the middle' As Varbinary(128));

Set Context_Info @VrBn;

 

WaitFor Delay '00:01';

 

Set @VrBn=Cast('P_3 ends' As Varbinary(128));

Set Context_Info @VrBn;

Go

פרוצדורה P_3 מדמה ריצה בת שני שלבים, כל שלב נמשך דקה, וה-Context_Info מעודכן בהתחלה, באמצע, ובסוף.

נפעיל את הפרוצדורה מחלון חדש (New Query) ב-SSMS כך:

Select @@SPID;

Go

 

Exec tempdb..P_3;

Go

clip_image006

את מספר ה-Session ניתן למצוא אחת משתי הדרכים המצויינות בעיגול אדום,

ואנו זקוקים לו כדי לאתר את המידע בטבלת המערכת (רק דרכה נוכל לברר מה ה-Context_Info של Sessions אחרים):

Select Cast(Context_Info As Varchar) From sys.sysprocesses Where SPID=57;

clip_image008

ברור שיש לכתוב בתנאי את ה-spid המתאים..

כדאי להפעיל מספר פעמים במהלך שתי דקות הריצה כדי לראות כיצד הערך מתעדכן.

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

1. הוא זמין ואין צורך להגדיר דבר או ליצור אובייקטים חדשים לשם כך.

2. המידע הנשמר בו קיים כל עוד ה-Session קיים, ואינו תלוי בטרנזקציות.

3. אין צורך בהרשאות מיוחדות כדי להשתמש בו.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים

2 תגובות

  1. משה14/03/2012 ב 22:23

    מה קורה בשרשרות קריאות של פרוצדורות,
    P2 תגדיר את Context לקריאה לP3 ובעצם תאפש לעצמה. אין בקרה מי משתמש בזה.

    להגיב
  2. גרי רשף15/03/2012 ב 06:58

    משה- ה-Context_Info הוא ברמת ה-Session.
    כלומר- אם אתה מפעיל פרוצדורה 1, והיא מפעילה פרוצדורה 2, והיא את פרוצדורה 3 וכך הלאה- כולן פונות לאותו Context_Info ויכולות לתקשר זו עם זו דרכו (כמובן הן אינן רצות במקביל אלא אחת אחרי השניה או האחת ממתינה עד שהשניה תסתיים ואז תמשיך, ולכן כל אחת תוכל להעביר מידע לזו שהיא מפעילה).

    לעומת זאת אם אתה מפעיל פרוצדורה ותוך כדי גם משתמש אחר או איזה ג'וב מפעיל פרוצדורה (אולי אפילו אותה פרוצדורה)- לכל אחת יהיה Context_info אחר וכל אחת תראה בו מידע אחר.

    להגיב