סיבוב טבלה על ציר

בעיה נפוצה שלא היה לה פתרון פשוט על ידי פקודה עד גרסה 11 היא סיבוב טבלה על ציר. בגרסה 11 ישנו פתרון יעיל ומהיר לבעיה על ידי צמד פקודות: Pivot ו-Unpivot שלא מצריך ניפנופי ידיים והסברים על מימוש שליפה שתבצע את זה.

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

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

טבלאות לקריאה בלבד

בגרסאות קודמות אם היינו רוצים לסמן טבלאות מסויימות לקריאה בלבד (read only) היינו מסירים את ההרשאות הכתיבה עליהן מכל המשתמשים ומקווים לטוב. למה מקווים לטוב? כי למרות הרצון הטוב, לא ניתן לבטל את ההרשאות של הבעלים (owner) של הטבלה ולכן הוא היה יכול להמשיך לשנות אותה (אפילו בטעות) למרות ההגדרה האפליקטיבית שביצענו.

החל מגרסה 11gR1 ניתן לנעול טבלה ולהגדיר אותה כ-read only ובכך למנוע גישת כתיבה אליה (כמובן שניתן להמשיך לקרוא ממנה).

המשך קריאה…

יצירת קובץ CSV על ידי שימוש ב-SQLPLUS

היום התבקשתי על ידי אחת המפתחות לעזור לה לשמור שליפה של 230 עמודות ולמעלה מ-50 מליון רשומות לקובץ CSV. מבחינתי זו הזדמנות טובה לתעד את זה המסקנות שלי מהבעיה והפתרון היחסית גנרי שיצרתי בשבילו.

שליפות ב-sqlplus מחזירות בדרך כלל את הרשומות באחד משתי דרכים: הדרך הראשונה היא fixed length columns (כלומר העמודה הראשונה מתחילה תמיד במיקום 1, העמודה השנייה תמיד במיקום 7 וכן הלאה). הדרך השנייה היא כ-HTML ועל זה נדבר בהזדמנות אחרת. קובץ CSV הוא בסופו של דבר קובץ טקסט לכל דבר שבו העמודות (ערכים) מופרדים על ידי פסיקים (ומכאן שמו: Comma-separated values).

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

המשך קריאה…

שימוש ב-with כדי לפשט שליפות

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

נתחיל בשיעור היסטוריה קצר כדי להבין מה זה בעצם אומר. כידוע, SQL היא לא שפה ייחודית לאורקל – זו שפה עם סטנדרטים שנקבעים על ידי ANSI. הגרסה הראשונה של השפה שעברה סטנדרטיזציה הייתה בשנת 1986 עם הוצאת SQL86.  בשנת 92 יצאה גרסה נוספת (SQL92) שהייתה גם הגרסה הראשונה שאורקל ניסתה להתאים את עצמה אליה בגרסת Oracle 8. בשנת 1999 יצאה גרסה חדשה ומורחבת של השפה (SQL99) שכללה הרבה מהתכונות שהיו באורקל לפני זה (triggers, recursive sql ועוד) ואורקל התאימו את עצמם שוב למרבית התקנים החדשים בגרסה Oracle 9i (אם אני לא טועה זה היה בגרסה 9.2).

המשך קריאה…

שליפת רשימת מספרים בלתי מוגבלת בלי להשתמש בטבלה

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

select rownum from dual connect by level <= 1000;

כשה-level מוגבל על ידי המספר המקסימלי שאנחנו רוצים לקבל.


רשימת ה-session-ים שה-QC הוא ב-INACTIVE אבל יש לו ילדים שעדיין תופסים Parallel

תופעה מעניינת עם כלים שמחזירים row set בגודל מסויים ושמאפשרים לעשות fetching היא שאם השליפה המקורית הייתה מקבילית (parallel) הכלים נוטים שלא לשחרר את הפרוססים שהם השתמשו בהם בשביל השליפה ולתפוס אותם כל עוד לא נעשתה שליפה אחרת באותו חלון.

אז מה בעצם הבעיה? הבעיה היא שכאשר יש load גבוה על המערכת ויש צורך ב-Parallel servers, התוכנות האלה מחזיקות את המשאבים ושליפות אחרות מקבלות downgrade.

שני כלים שעושים את זה הם TOAD ו-PL/SQL Developer וניתן להשתמש בשליפה הבאה כדי לאתר מי הם הסשנים שנראים Inactive אבל בפועל הם תופסים משאבים (השליפה הזו נעטפה על ידי סקריפט CSH שגם מעיף את המשתמשים האלה כאשר הם מחזיקים את המשאבים יותר מדי זמן):

with curr_sessions as (
SELECT ROUND(BITAND(s.ownerid, 65535)) parent_session_sid,
       s.sid,
       s.status,
       s.sql_id,
       UPPER(s.program) program
  FROM v$session s, v$process p
 WHERE ((s.username IS NOT NULL) AND (NVL(s.osuser, 'x') &lt;&gt; 'SYSTEM') AND (s.TYPE &lt;&gt; 'BACKGROUND'))
   AND (p.addr(+) = s.paddr))
select distinct a.sid parent, b.sid child, a.status status_p, b.status status_c, a.program, a.sql_id
  from curr_sessions a
  join curr_sessions b on a.sid = b.parent_Session_sid
                      and a.status = 'INACTIVE'
                      and a.program in ('TOAD.EXE')
order by 1