יום שלישי, 13 בדצמבר 2011

פרצת אבטחה חמורה בקליקוויו

לראות ולא להאמין: בקבצי ה-QVW וה-QVD של קליקוויו, נשמר ה-Connection string המלא לבסיס הנתונים, כולל משתמש וסיסמה - בצורה לא מוצפנת.
הפרצה החמורה התגלתה ופורסמה ע"י Luca Jonathan Panetta, בפורום הבינלאומי של קליקויו:
http://community.qlikview.com/thread/40308?start=0&tstart=0
כדי לראות זאת, צריך פשוט לפתוח את הקובץ ב-Notepad ולחפש את המילה password.
דווח שהבעיה תוקנה בגרסה 11, כך שעכשיו יש עוד סיבה טובה לשדרג - ולא לשכוח להריץ שוב את כל המודלים (כולל הישנים שאינם בשימוש), כדי לעדכן את קבצי ה-QVD.
בינתיים מומלץ בחום לוודא שקבצי ה-QVD (וכמובן ה-QVW) מוגנים היטב ואינם נגישים למי שאיננו אמור לדעת את הסיסמה לבסיס הנתונים.

יום שני, 5 בדצמבר 2011

מציאת שדה טקסט ב-DataBase ע"פ ערך, בעזרת QlikView

קורה שאנחנו לא יודעים מהו שם השדה שאנו זקוקים לו ב-DataBase, ולא מצליחים למצוא אותו בעזרת שאילתות על הקטלוג.
אפשר לחפש את השדה ע"פ אחד הערכים שבו (בד"כ בשדה טקסט). ניתן להריץ ב-DataBase פונקציה שרצה על כל הטבלאות ומחפשת בכל שדה טקסט את המחרוזת. פונקציות כאלו מסתובבת ברשת (גם ל-Oracle וגם ל-MS-SQL).
הבעיה היא שהרצת פונקציה דורשת הרשאות כתיבה ל-DataBase - משהו שכדאי להימנע ממנו בתור אנשי BI.
ובכן, ניתן להשתמש ב-QlikView כדי לבצע פעולה דומה.

ראשית, כדי לנטרל הודעות שגיאה שעלולות לצוץ, נגדיר:
set errormode = 0;
ניצור את ההתחברות ל-DataBase, את הסכמה הרצויה, ואת המחרוזת שאנו מעוניינים לחפש:
(connect to db);
set vSchema = 'MySchema';
set vString = 'StringToFind';
נביא את כל הטבלאות וכל השדות:
tabs:
sqltables;

cols:
sqlcolumns;
ניצור טבלת עמודות זמנית, הכוללת רק את הסכמה שלנו, ורק עמודות מסוג טקסט. נצרף לה את סוג הטבלה, ונמחק את הטבלאות הקודמות:
Columns_Temp:
load 
     TABLE_NAME,
     COLUMN_NAME
resident
     cols
where
     TABLE_SCHEMA = '$(vSchema)'
     and DATA_TYPE = 129;

left join
load
     TABLE_NAME,
     TABLE_TYPE
resident
     tabs;

drop tables tabs, cols;

ניצור טבלת עמודות סופית, שבה רק עמודות מטבלאות מסוג "טבלה" (ולא Views וכד'), ונמחק את הטבלה הזמנית:

Columns:
load
     TABLE_NAME,
     COLUMN_NAME
resident
     Columns_Temp
where
     TABLE_TYPE = 'TABLE';

drop table Columns_Temp;

לבסוף, ניצור לולאה שרצה על כל עמודה, ומחפשת בה את המחרוזת:
for x = 0 to NoOfRows('Columns')-1
     let vTable = peek('TABLE_NAME',x, 'Columns');
     let vColumn = peek('COLUMN_NAME',x, 'Columns');
     Results:
     sql select
          $(vColumn) as value,
          '$(vColumn)' as column_name,
          '$(vTable)' as table_name
     from
          $(vSchema).$(vTable)
     where
          $(vColumn) = '$(vString)' ;
next
לאחר ריצת המודל, נקבל בשדות table_name ו-column_name את הטבלה והעמודה (או הטבלאות והעמודות) שבהן נמצא הערך.

יום רביעי, 30 בנובמבר 2011

מי שמציע - משפיע

כבר יותר משנתיים ש-QlikTech מאפשרים לקהל הרחב להציע רעיונות לשיפור QlikView ולדון בהם:
http://community.qlikview.com/ideas
לא כל חברה מאפשרת את זה, ועוד פחות חברות נותנות משוב ברור האם הרעיון אומץ על ידם, או למה הוא נדחה. לא מעט רעיונות כבר התקבלו, וזאת ההזדמנות להשפיע על הגרסאות הבאות של המוצר.

הנה כמה רעיונות שאני הצעתי:
אפשרות להציג בגרף עמודות עם שני ממדים את ערכי הפרטים (בתוך העמודות) וגם את סך-הכול (מחוץ לעמודות)
http://community.qlikview.com/ideas/2119
הצגת לוג מפורט יותר בעת ריצת הסקריפט
http://community.qlikview.com/ideas/2141
אפשרות להקליט מאקרו
http://community.qlikview.com/ideas/2129
פונקציית Null-To-Value (או לחלופין הרחבת פונקציית Alt גם למחרוזת)
http://community.qlikview.com/ideas/2142
העתקה/הדפסה של כל הגרף, ולא רק החלקים המוצגים במסך
http://community.qlikview.com/ideas/2122

יום רביעי, 23 בנובמבר 2011

ליסטבוקס היררכי

לא פעם יש בגיליון הרבה ליסטבוקסים, וזה עמוס מדיי.
אחת האפשרויות היא להשתמש ב"שידת-מגירות", הלא היא ה-Multi-Box. אני לא מת על זה, בעיקר כי אם נבחר יותר מערך אחד, אז לאחר שהמגירות נסגרות, המשתמש לא רואה מה הערכים שהוא בחר.
אפשרות חלופית היא ליצור ליסטבוקס היררכי. למשל במקום ליסטבוקס של מדינות וליסטבוקס של ערים, יהיה ליסטבוקס היררכי אחד המאגד את שניהם.
ניצור בסקריפט שדה המשרשר את המדינה והעיר, עם תו מפריד כלשהו:
Country & '/' & City as CountryAndCity
 ואז בליסטבוקס נסמן את Show as TreeView (ואם התו המפריד שונה מלוכסן - להכניס את התו). זה ייראה כך:
אמנם זה מסדר את המדינות והערים בהיררכיה, אבל הבעיה היא, שכפי שניתן לראות מהצבע האפור, ערכי ה-"אב" - מדינות, לא ניתנים לבחירה. ניתן לבחור רק את ה-"בנים" - הערים.
מה עושים?
הפתרון הוא ליצור בסקריפט טבלה חדשה ובה שני שדות: שדה העיר - כדי לקשר לטבלה הראשית, ושדה נוסף, שמכיל גם ערכים עם הביטוי הנ"ל, אבל גם (באותו שדה!) את המדינות בלבד:
CitiesHierarchy:
load
  City,
  Country & '/' & City as CountryAndCity
Resident
  Cities;
load
  City,
  Country as CountryAndCity
Resident
  Cities;

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

שליפת שדה ע"פ ערך מינימלי/מקסימלי בשדה אחר

קורה שאנו רוצים בסקריפט למצוא רשומה עם ערך מינימום או מקסימום של שדה, אך לשלוף שדה אחר של אותה רשומה.
למשל: להוסיף לטבלת לקוחות, את המוצר האחרון שהלקוח קנה. (לא את תאריך הקנייה, אלא את מק"ט המוצר).

ניתן ליצור זאת בסקריפט בקלות, ע"י שימוש בפונקציה SubField:

left join (Customers)
load
  CustomerID,
  subfield(maxstring(date(SaleDate,'YYYY-MM-DD hh:mm:ss') & '>' & ProductID),'>',2) as Last_Product
resident
  Orders
group by
     CustomerID;

הסבר - מפנים הפונקציה החוצה:
ראשית אנו מפרמטים את תאריך המכירה, מיחידת הזמן הגדולה (שנה) ליחידת הזמן הקטנה (שנייה), וזאת כי בהמשך נשתמש בפונקציית maxstring שממיינת באופן טקסטואלי. הפרמוט באופן הזה מבטיח שהתאריכים ימוינו נכון.
את התאריך המפורמט אנחנו משרשרים לתו < ולמק"ט המוצר. בינתיים הערכים נראים כך:
2011-02-14 10:14:33 > 146782
על הביטוי הזה אנו מפעילים פונקציית maxstring. יוצא שלכל לקוח משויך ביטוי של תאריך הרכישה האחרונה שלו, משורשר למק"ט המוצר באותה רכישה.
לבסוף אנו מפעילים פונקציית subfield, ומעבירים לה את הפרמטר < ו-2, כדי שתחלץ מהביטוי הנ"ל את האבר השני שבתוכו, כאשר החלוקה לאברים היא לפי התו <.
קיבלנו עבור כל לקוח, את מק"ט המוצר שהוא קנה אחרון.


הבלוג נפתח!

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