› פורומים › אפיון ופיתוח פריוריטי › מפתח אוטומטי
- הנושא הזה ריק.
- Post
-
- אוקטובר 26, 2016 בשעה 10:31 am
שלום וברכה למומחי הפורום.יש לי טבלה שאין לה מפתח ראשי טבעי, [לדוגמא: טבלת לוגים. רשומה יכולה להיות זהה לחלוטין לחברתה, למעט המפתח הראשי]
המשתמש איננו מתעניין במפתח, והוא לא מגדיר את המפתחות, ולכן עמודת המפתח מוסתרת.
משום מה, פריוריטי החליטו שאי אפשר להשתמש בעמודת מספור אוטומטי [מפתח A] כמפתח ראשי, וחובה להוסיף גם מפתח U.
ניסיתי לעשות כך:
הגדרתי לטבלה עמודה בשם ID שהיא מספור אוטומטי.
הגדרתי גם עמודה ID2 שהיא מפתח U.
בטופס, ניסיתי להגדיר טריגר POST-UPDATE שיעתיק את הערך מעמודה ID לעמודה ID2.הבעיה: הטריגר איננו מעדכן את עמודה ID2, כנראה שהוא מתבצע לפני שעמודה ID מקבלת את ערך המספור האוטומטי.
מה הדרך המקובלת להתמודד עם הבעיה?
תודה רבה
- Replies
-
- אוקטובר 26, 2016 בשעה 2:14 pm
פריוריטי מתגברת על הבעיה הזו באמצעות טבלה בשם LASTS אשר מכילה את הערך האחרון של כל מיני דברים, כולל טבלאות. לפני שמירת שורה בטבלה, הקוד קודם משיג את הערך האחרון ששמור בטבלת LASTS, מעדכנת את המספר הזה ואז מנסה להכניס לתוך טבלת הלוג את השורה החדשה. יש לולאה אשר מטפלת במקרה מישהו כבר תפס את המספר שהוקצב. בסוף המספר החדש נשמר בחזרה בטבלת LASTS לפעם הבאה.
:LOGFILE_KLINE = :LOGFILE_DATE = SQL.DATE ;
SELECT VALUE INTO :LOGFILE_KLINE FROM LASTS
WHERE NAME = 'XXXX_CHANGE_LOG';
GOTO 512 WHERE :RETVAL > 0;
INSERT INTO LASTS (NAME, VALUE)
VALUES ('XXXX_CHANGE_LOG', SQL.DATE);
LABEL 512;
:LOGFILE_KLINE = :LOGFILE_KLINE + 1;
SELECT ENTMESSAGE('ORDERS','F',800) INTO :TEXT FROM DUMMY ;
INSERT INTO XXXX_ORD_CHANGE_LOG
(LOG, ORD, USER, UDATE, TEXT)
VALUES(:LOGFILE_KLINE, :ORD, SQL.USER, SQL.DATE, :TEXT);
LOOP 512 WHERE :RETVAL < 1;
UPDATE LASTS SET VALUE = :LOGFILE_KLINE
WHERE NAME = 'XXXX_CHANGE_LOG';
- אוקטובר 26, 2016 בשעה 3:12 pm
- אוקטובר 28, 2016 בשעה 2:54 am
נעם, תודה רבה, יישמתי את השיטה וכתבתי קוד כזה.
אשמח לקבל הערות או שיפורים לקוד.:LASTID = :NEWID = 0;
:NAMEIDENTIFIER = 'MYTABLE_ID';
LABEL 100;
GOTO 120 WHERE EXISTS (SELECT * FROM LASTS WHERE NAME
= :NAMEIDENTIFIER);
INSERT INTO LASTS(NAME, VALUE)
VALUES(:NAMEIDENTIFIER, 0);
LABEL 120;
SELECT VALUE INTO :LASTID FROM LASTS
WHERE NAME = :NAMEIDENTIFIER;
:NEWID = :LASTID + 1;
UPDATE LASTS SET VALUE = :NEWID
WHERE NAME = :NAMEIDENTIFIER AND VALUE = :LASTID;
/* resolve concurrency conflicts */
LOOP 100 WHERE :RETVAL < 1;
:$.ID = :NEWID; /* set new id */
השאלה: איזו הפעלה תפעיל את הקוד הזה?
ניסיתי הפעלות ברמת הטופס, כמו PRE-INSERT, לא עובד. כאשר אני בא לשמור את הרשומה אני מקבל התראת שגיאה שהשדה ID אין בו ערך.
ניסיתי הפעלות ברמת השדה ID, זה עובד אבל רק כאשר נכנסים לשדה או יוצאים ממנו.
אם השדה מוסתר, לא עובד.מה הפתרון?
- אוקטובר 28, 2016 בשעה 12:07 pm
נדמה לי שהגעתי לתוצאה משביעת רצון, תודה רבה לנועם על העזרה.במקרה שמישהו אחר יתקל במשימה, להלן הפתרון:
1. בטבלה מגדירים עמודה מסוג INT ועליה מפתח U שישמש במפתח הראשי.
2. לטופס, מוסיפים את שדה המפתח כשהוא נסתר או לקריאה בלבד.
3. בטופס, צריך להגדיר אירוע POST-FIELD, באחת משתי אפשרויות:
– אם לטופס יש שדות חובה, אפשר להגדיר את האירוע לאחד משדות החובה.
– אם אין לטופס אף שדה חובה, צריך להגדיר את האירוע בכל שדות הטופס, כדי שכל אתחול של הרשומה יריץ את האירוע.
4. הקוד של האירוע הוא כדלהלן:
END WHERE :$.your_unique_column_name > 0;
:LAST_NAME = 'your_table_name_UNIQUEID';
:LASTID = :NEWID = 0;
LABEL 100;
GOTO 120 WHERE EXISTS (SELECT * FROM LASTS WHERE NAME
= :LAST_NAME);
INSERT INTO LASTS(NAME, VALUE)
VALUES(:LAST_NAME, 0);
LABEL 120;
SELECT VALUE INTO :LASTID FROM LASTS
WHERE NAME = :LAST_NAME;
:NEWID = :LASTID + 1;
UPDATE LASTS SET VALUE = :NEWID
WHERE NAME = :LAST_NAME AND VALUE = :LASTID;
/* resolve concurrency conflicts */
LOOP 100 WHERE :RETVAL < 1;
:$.your_unique_column_name = :NEWID; /* set new id */
כמובן, שיש להחליף את השמות שבקוד לשמות האמיתיים [בשתי השורות הראשונות ובשורה האחרונה].
מטעמי נוחות וסדר, כדאי לעשות עוד כמה פעולות:
5. להוסיף BUFFER כללי [בשם BUFSetId או דומה לזה]
6. בטופס, ברמת הטופס, להגדיר אירוע בשם הBUFFER שלנו, עם הקוד דלעיל.
7. באירועי השדות, לעשות INCLUDE לבBUFFER שלנו.אני מקווה שזה יעזור למישהו.
- אוקטובר 30, 2016 בשעה 9:42 am
- אוקטובר 30, 2016 בשעה 11:35 am
לעניות דעתי, הקוד שלך איננו מונע עריכה בו-זמנית ע"י שני משתמשים. עד שאתה עושה UPDATE ייתכן שמישהו כבר תפס את המספר הזה.בקוד שלי, הבעיה נפתרה כך
בתחילה אני מוודא שיש רשומה בטבלת LASTS. אם אין רשומה, אני מוסיף אותה כשהערך ההתחלתי הוא 0.אחר-כך אני שולף את הערך הקיים ברשומת LAST, מוסיף לו 1, ומייד מעדכן אותו בטבלת LASTS כדי לתפוס את המספר הזה.
תוך כדי שאני תופס, אני מוודא שאף אחד אחר לא תפס לי את המספר הזה.
UPDATE LASTS ...
WHERE VALUE = :LASTIDאם אחרי הUPDATE הזה RETVAL = 1, אני יודע שיש לי מספר תפוס, שאף אחד אחר לא ישתמש בו, ואני יכול להשתמש בו בבטחה.
אם RETVAL = 0, אני יודע שמישהו נגע לי ברשומה, ותפס לי את המספר שרצית לעדכן, ואני צריך לחזור על הלולאה.
[מטעמי בטחון, אני חוזר לשורה LABEL 100, כי אולי מישהו מחק לי את הרשומה]- אוקטובר 30, 2016 בשעה 11:58 am
להלן השוני בגישות: בקוד של פריוריטי, יש נסיון קודם לכתוב אל הטבלה הפרטית ורק לאחר כתיבה מוצלחת שומרים את ערך המפתח . בגישה שלך, אתה שומר את המפתח בחזרה בטבלת LASTS עוד לפני נסיון כתיבה. זה עלול ליצור בעיות אם שני אנשים מנסים לכתוב בו זמנית.הקוד בפריוריטי מנצל את מנוע ה-SQL בכך שניתן ליצור רק רשומה אחת בעל מפתח מסוים. במקום לבדוק מול LASTS אם המפתח תקין, הבדיקה נעשית ישירות מול הטבלה הפרטית לכן מונעת מה שנקרא בתורת האלגורתמים RACE CONDITIONS, כאשר שני משתמשים מנסים לעדכן את אותה הדבר. המפתח הראשי של הטבלה הפרטית הוא ID, בעוד המפתח הראשי בטבלת LASTS אינו מספר המפתח אלא שם כלשהו.
הקוד למטה מונע את המצב בו אתה מקבל מספר מפתח תקין אל מישהו אחר מקבל את אותו המספר ואף מצליח לכתוב איתו לפני שאתה מנסה. יותר חשוב לעדכן את הטבלה הפרטית מאשר טבלת LASTS.
LABEL 100;
:ID = :ID + 1;
INSERT INTO XXXX_TABLE (ID, FIELD)
VALUES (:ID, 'A');
LOOP 100 WHERE :RETVAL < 1;
UPDATE LASTS
SET ....
אני רוצה להדגיש שהקוד למעטה פותח ע"י מפתחי פריוריטי ולא על ידי, לכן ניתן להסיק שהם ידעו מה נכון.
- אוקטובר 30, 2016 בשעה 12:09 pm
אינני יודע פריוריטי היטב, אבל SQL אני יודע היטב.למיטב הבנתי, ואשמח אם תתקן אותי, אם אתה מתחיל רשומה חדשה ומריץ את הקוד הבא:
:$.MY_UNIQUE_COLUMN = 1001;
זה לא מונע ממשתמש אחר לבוא אחריך ולהתחיל גם הוא רשומה חדשה עם אותו ID.
האפליקציה שלו איננה יודעת שאתה כבר תפסת את המספר הזה.
אם הוא יייצא מהמסך לפניך, הרשומה שלו תישמר בהצלחה, וכאשר אתה תבוא לשמור את הרשומה תקבל שגיאת הפרת מפתח ותהיה בבעיה.בדקתי באמצעות SQL PROFILER וראיתי שכאשר אתה מגדיר את הID, אין שום עדכון בדטהבייס ואף אחד איננו יודע שתפסת את המספר 1001.
הקוד שלי פותר את הבעיה בדרך פשוטה ואלגנטית.
- אוקטובר 30, 2016 בשעה 12:19 pm
- אוקטובר 30, 2016 בשעה 12:29 pm
נכון, כל אחד יכול *לנסות* ולשמור רשומה עם מפתח 1001. אבל רק אחד יצליח! זו הסיבה שבודקים את ערך המשתנה RETVAL – למשתמש שהצליח, RETVAL יהיה 1 והוא ייצא מהלולאה אל הפקודה ששומרת את הערך שלו בטבלת LASTS. למשתמש השני, RETVAL יהיה 0 לכן ינסה לשמור את רשומה עם מפתח 1002. הצליח – שומר את הערך 1002. לא מצליח – מנסה את 1003 וכך הלאה.- אוקטובר 30, 2016 בשעה 12:57 pm
חזרתי וקראתי את הקוד שלך ועתה הבנתי שהINSERT הוא לתוך טבלת היעד, ובעצם הוא יוצר את הרשומה המבוקשת.אכן, זה פותר את בעיית הבו-זמניות, אבל לעניות דעתי פתרון תמוה ומסוכן מאד.
אם אתה מכניס את הרשומה בצורה ידנית אתה שובר את העקרון שאין לשנות נתונים ידנית!
נניח שיש לי טופס עם 20 שדות חובה, ועוד חוקים עסקיים ועוד טריגרים, ואתה בא ומכניס את הרשומה בצורה ידנית, באמצעות INSERT.
מה עם שדות החובה? ומה עם כל הטריגרים?לדעתי זו פרקטיקה בעייתית, ואם מתכנתי פריוריטי עשו כך זה חמור מאד. בשביל מה יש טפסים אם בסוף אתה עוקף אותם ועושה INSERT ידני?
אבל הקוד שלי לא עושה שום INSERT אלא רק תופס ID. אחרי שיש לך ID פנוי אתה יכול להמשיך להזין את הרשומה כרגיל.
וזו הדרך המקובלת להתמודד עם מספור אוטומטי. השרת מקצה ID לכל משתמש שרוצה להזין רשומה, אבל הרשומה נשמרת רק כאשר המשתמש בא לשמור.
- אוקטובר 30, 2016 בשעה 1:14 pm
אל תשכח שאנחנו מדברים על טבלה ללא מפתח טבעי, כמו יומן סטטוסים. אתה מתאר משהו כמו תנועות יומן: אם תסתכל על טבלת FNCITEMS, תראה שאין מפתח אחד יחודי. מתפח הטבלה מורכב משני שדות: FNCTRANS, מספר תנועת היומן, ו-KLINE, מספר השורה בתוך התעודה. אף אחד מהמספרים האלה אינו יחודי אך ביחד הם יוצרים מפתח יחודי.- אוקטובר 30, 2016 בשעה 1:44 pm
טבלת לוגים זו היתה דוגמא, יש עוד הרבה מקרים שאין מפתח ראשי טבעי וצריכים מספור אוטומטי.ובכל מקרה, אני חושב שINSERT ידני זו פרקטיקה גרועה ומסוכנת.
ולצערי מפתחי חב' פריוריטי עלולים לטעות וגם לעשות עושים דברים מוזרים.
כשקראתי את הודעתך הראשונה הפותחת במילים
פריוריטי מתגברת על הבעיה הזו באמצעות טבלה בשם LASTS
חייכתי חיוך מר, כי לדעתי פריוריטי איננה מתגברת על הבעיה אלא יוצרת אותה.
השימוש בטבלת LASTS הוא פתרון עקום מאד וגם מסורבל מאד. אין שום הגיון לאלץ את המפתחים לכתוב קוד שכל תפקידו הוא לנהל מיספור אוטומטי.פתרון אמיתי של הבעיה הוא באמצעות מפתח A שהוא גם מפתח U.
- יש להתחבר למערכת על מנת להגיב.