en-UShe-IL
You are here:   Blog
Register   |  Login

Blog Archive:

Maximize
* Can be used in order to search for older blogs Entries

Search in blogs


Blog Categories:

Maximize
* Can be used in order to search for blogs Entries by Categories

Blog Tags:

Maximize
* Can be used in order to search for blogs by keywords

TNWikiSummit


Awared MVP 


 


Microsoft® Community Contributor 


Microsoft® Community Contributor


 Read first, before you you use the blog! Maximize
יול15

Written by: ronen ariely
15/07/2014 20:56 RssIcon

הקדמה

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

על מנת לעמוד במטרות של טור זה, השרת חייב להבטיח שהערכים בחותמת הזמן יהיו תמיד ייחודיים, בין שני זמנים שונים.

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

חותמת זמן, מאחורי הקלעים

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

התנהגות חותמת הזמן:

>> כל מסד נתונים כולל מונה אחד.

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

>> כל טבלה יכולה להכיל לכל היותר טור אחד מסוג חותמת זמן.

>> טור חותמת זמן אינו מומלץ לשמש כמפתח , ועל אחת כמה וכמה כמפתח ראשי בטבלה! מכיוון שחותמת הזמן משתנה בכל עדכון של הרשומה בטבלה.

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

>> שימוש בטור חותמת זמן כאינדקס יגרור עדכון של האינדקס בכל עדכון של רשומה בטבלה.

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

        - נקודה זו אינה נכונה אם נעשה שימוש בשם המקביל rowversion! במקרה זה חייבים להגדיר שם טור.

rowversion

חותמת זמן הוא טור שקיים כבר בגרסאות הראשונות של שרתי SQL, אבל השם עצמו יצר בעיה עם הניסיון להתקרב לתקנים. הגדרת המילה "חותמת הזמן" בשפת T-SQL אינה זהה להגדרת התקן של SQL-92. למעשה ההגדרה של המושג "חותמת זמן" לפי תקן זה, זהה להגדרה של סוג datetime בשפת T-SQL. לכן בגרסת SQL Server 2008 הוחלט להחליף את השימוש במילה timestamp. כפי שהזכרתי, חותמת זמן משמשת בדרך כלל כמנגנון לשמירת גרסאות של שורות בטבלה, ולכן בשרתי sql server 2008 נוספה לנו מילה מקבילה (Synonym) בשם rowversion.

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

דוגמה ליצירת אלמנט synonym:

USE tempdb;
GO
CREATE SYNONYM MyAddressType
FOR AdventureWorks2012.Person.AddressType;
GO

למרות שהשם חותמת זמן בכל המדריכים של מייקרוסופט הוחלף, והמילה חותמת זמן הוגדרה כמיושנת (deprecated), הרי שהסוג עצמו עדיין מוגדר מבחינת השרת מאחורי הקלעים כ timetstamp, והשימוש במילה timestamp עובד בכל הגרסאות נכון לזמן כתיבת מאמר זה, כולל SQL Server 2014.

* ההמלצה הרשמית היא לא לעשות שימוש במילה timestamp ולהתרגל לעבוד עם המילה החדשה rowversion אשר אמורה בעתיד לשמש עבר הסוג עצמו.

נוכל להראות בתרגיל קצר שאכן שימוש במילה rowversion לשם הגדרת סוג, והסוג timestamp הם למעשה אותו סוג מבחינה מעשית ששמו  timestamp. נייצר שתי טבלאות זהות פרט לכך שבאחת מהן נגדיר את הטור [RV] כ rowversdion ובטבלה השניה נגדיר את הטור כ timestamp.

CREATE TABLE MyTest1 (myKey int PRIMARY KEY
    ,myValue int, RV rowversion);
GO
CREATE TABLE MyTest2 (myKey int PRIMARY KEY
    ,myValue int, RV timestamp);
GO

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

select all_columns.system_type_id, types.name
from sys.all_columns
inner join sys.types on all_columns.system_type_id = types.system_type_id
where object_id = OBJECT_ID('MyTest1')
select all_columns.system_type_id, types.name
from sys.all_columns
inner join sys.types on all_columns.system_type_id = types.system_type_id
where object_id = OBJECT_ID('MyTest2')
GO

Check columns types

Generate Scripts Using SSMS GUI

* מה שמוזר מעט לדעתי, זה שבגרסת SQL 2014, אם נעזר בממשק הגרפי (כפי שנראה בתמונה מעל) על מנת לייצר סקריפט בצורה אוטומטית (Generate Scripts Using SSMS GUI), היינו מצפים לקבל סקריפט העומד בדרישות של מיקרוסופט ועושה שימוש במילה rowversion. מעשית המצב הפוך ומישהו בחברת מייקרוסופט שכח לעדכן את הדברים. השאילתה שנקבל תעשה שימוש בשם timestamp.

* הערה: ב BOL של SQL Servr 2014 רשום:

"timestamp is the synonym for the rowversion data type and is subject to the behavior of data type synonyms." [http://msdn.microsoft.com/en-us/library/ms182776.aspx]

הבדיקות מעל מראות שהדברים הפוכים ולמעשה rowversion הוא synonym של timestamp! חיזוק נוסף לכך מתקבל באותו קישור של ה BOL כמה שורות מתחת

"the rowversion synonym does not follow this behavior"

* הערה: לצורך מאמר זה בעברית, אני אמשיך להשתמש במושג חותמת זמן מכיוון שהתרגום של השם rowversion פשוט נשמע לי לא כל כך טוב.

הבעיה – למה צריך סוג נתונים כל כך מוזר ומורכב

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

לדוגמה אם נעבוד עם מספרים שלמים INT, אז נוכל להגיד שהרזולוציה שלנו היא 1. אם יהיה לנו צורך לעבוד עם 2 ערכים שההבדל ביניהן יותר קטן מהרזולוציה שלנו, למשל המספרים 0.1 וכן 0.2, אז נקבל שכאשר הם יעברו המרה למספר שלם אז הערכים עלולים להיות שווים, מכיוון שהם עוברים תהליך של עיגול הערכים בזמן ההמרה. המספר 0.1 מעוגל למספר 0 וגם המספר 0.2 מעוגל למספר 0. לכן יכולת ההפרדה לא תאפשר לנו להבדיל בן הערכים.

select CONVERT(INT, 0.1), CONVERT(INT, 0.2)



באותו אופן אם נבחר לעבוד עם מספר עשרוני עם 2 תווים אחרי הנקודה decimal(5,2) אז הרזולוציה שלנו תהיה בהתאם 2 תווים אחרי הנקודה ולכן המספר 1.011 והמספר 1.012 יעוגלו לאותו ערך של 1.01.

select CONVERT(decimal(5,2), 1.011), CONVERT(decimal(5,2), 1.012)


שרתי SQL כוללים את הסוגים הבאים עבור ערכים מספריים (Numeric): 

bigint, numeric, bit, smallint, decimal, smallmoney, int, tinyint, money, float, real


כמו כן שרתי SQL כוללים את הסוגים הבאים עבור ערכים המתארים זמנים:

date, datetimeoffset, datetime2, smalldatetime, datetime, time

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

* פרטים מדויקים על הרזולוציה של הסוגים השונים של זמנים ניתן לקבל כאן: http://msdn.microsoft.com/en-us/library/ms186724.aspx

לדוגמה, הערכים של סוג datetime מדוייקים עד כדי 0.00333 שניות, ז"א שהערכים בפועל מעוגלים בהתאם לערכים של 0.000, 0.003, 0.007 שניות.

.


סנכרון של חותמת זמן מול תאריך וזמן

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

ישנם מספר גישות שיכולות לעזור במערכות שונות:

אפשרות ראשונה: במערכות בהן הפעילות יחסית קטנה על הרשומות בכוללות חותמת זמן, ניתן פשוט להוסיף טור של datetime2 ובכך לאפשר סנכרון (כמובן שבטור התאריך-זמן ייתכן רשומות כפולות בשל הרזולוציה של הסוג והעיגול המבוצע). טור זה יכול להיות טור מחושב.

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

אפשרות שלישית: במערכות כבדות מאוד, בהן מספר הרשומות שמתעדכנות בכל שנייה גדול מאוד, ניתן לייצר טבלת סנכרון שבה נשמור טור של חותמת זמן וטור datetime2. עתה נוכל לבצע פעולה ברקע של הכנסת רשומה אל טבלת הסנכרון כל X זמן. נתוני טבלת הסנכרון יאפשרו לנו לקבוע את הערכת הזמן של כל נתון מסוג חותמת זמן על ידי שימוש בגבולות של הזמן לפני והזמן אחרי שיש לנו בטבלת הסנכרון. תדירות פעולת הכנסת הנתון לטבלת הסנכרון תקבע לנו את הרזולוציה של הסנכרון שלנו. למשל אפשר להכניס נתון דקה, או כל שנייה, או תאורטית אפילו כל 0.00333 שניות, שזו הרזולוציה המקסימלית של טור datetime. בכל מקרה אין הגיון בתדירות הגבוהה יותר מהרזולוציה של סוג הטור! 

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


לסיכום:

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

.

נספח 1: שאלות שעלו בפורומים ומאמר זה אמור לענות:

1. מה זה rowstamp?

2. מה זה timestamp?

3. מה ההבדל בין rowversion ובין timestamp?

4. מדוע אני מייצר טבלה עם טור של rowversion אבל נוצרה לי טבלה עם timestamp? 


.

מקורות מידע למאמר ומקורות מידע נוספים

  • Datetime, sql 2000(Transact-SQL)
    http://technet.microsoft.com/en-us/library/ms187819(v=sql.105).aspx
  • Timestamp (SQL Server 2000)
    http://technet.microsoft.com/en-us/library/aa260631(v=sql.80).aspx
  • rowversion (Transact-SQL)
    http://msdn.microsoft.com/en-us/library/ms182776.aspx
  • Data Types (Transact-SQL)
    http://msdn.microsoft.com/en-us/library/ms187752.aspx
  • Date and Time Data Types and Functions (Transact-SQL)
    http://msdn.microsoft.com/en-us/library/ms186724.aspx
  • CURRENT_TIMESTAMP (Transact-SQL)
    http://msdn.microsoft.com/en-us/library/ms188751.aspx
  • Synonyms (Database Engine)
    http://msdn.microsoft.com/en-us/library/ms187552.aspx
  • .

.