בעיה ב-SQL*UnLoader – ORA-24347

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

OCI

כדי להסביר את הבעיה, בוא נדבר רגע על OCI – Oracle Call Interface. ה-OCI הוא הממשק התכנותי שאורקל מספקים כבר כמעט 20 שנה הרבה יותר מ-20 שנה לצורך תכנות מול בסיס הנתונים שלהם. מדובר ב-API שמהווה את הבסיס לכמעט כל כלי שעובד מול אורקל – TOAD, PL/SQL Developer ועוד. הכלי sqluldr כתוב בשפת C ומשתמש ב-OCI – זה מה שעושה את הכלי כל כך פשוט ומהיר.

הבעיה

הבעיה מתחילה כאשר מנסים להשתמש ב-OCI כדי לשלוף מידע סיכומי (לדוגמה sum, max, count  וכן הלאה) ובעמודה הזו מתחבא Null באחת או יותר מהשורות.

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

ORA-24347: Warning of a NULL column in an aggregate function

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

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

עוד דבר שרק הוסיף לבלבול שלי היה שכשנתקלתי בבעיה הזו לראשונה, זה היה אחרי שהשליפה רצה כבר כמה שבועות בצורה תקינה. מסתבר שה-OCI לא מחזיר את ההודעה הזו בכל מצב. האזהרה הזו מופיעה רק אם מספר השורות שאנחנו רוצים לעשות להן סיכום (count, max וכו’) הוא גדול או שווה ל-1000. זה אומר שאותה שליפה יכולה לעבוד הרבה זמן אבל ברגע שיש מספיק רשומות בטבלה התוכנית תפסיק לעבוד.

שחזור הבעיה

לקח לי כמה שעות של ניסיונות לייצר test case לבעיה הזו. השליפה המקורית שלי שלפה משהו יחסית מסובך (count distinct עם case מורכב על Outer join – והשליפה הזו הייתה באחת השליפות הפנימיות) אבל בסוף הצלחתי לייצר דוגמה פשוטה ושלחתי אותה למפתח.

הנה הדוגמה ואם יהיה לי עדכונים לגבי זה, אני אוסיף אותם בעתיד. בנתיים, שווה להיזהר עם הכלי הזה ושימוש ב-aggrigate functions:

drop table ttt;
create table ttt (a number, b number);

begin
  for i in 1 .. 1100 loop
    insert into ttt values (i, mod(i, 7));
  end loop;

  update ttt set b = null where b = 1;
  commit;
end;
/

והתוצאה כשמריצים את השליפה:

c:app>sqluldr264.exe user=zohar/zohar query="select count(b) from (select * from ttt where rownum < 1000) group by a"
           0 rows exported at 2014-07-0214:54:14, size 0 MB.
         999 rows exported at 2014-07-0214:54:14, size 0 MB.
         output file uldrdata.1.txt closed at 999 rows, size 0 MB.

c:app>sqluldr264.exe user=zohar/zohar query="select count(b) from (select * from ttt where rownum <= 1000) group by a"
           0 rows exported at 2014-07-0214:53:57, size 0 MB.
ORA-24347: Warning of a NULL column in an aggregate function

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

0 תגובות

השאירו תגובה

Want to join the discussion?
Feel free to contribute!

השאר תגובה

This site uses Akismet to reduce spam. Learn how your comment data is processed.