Bill Inmon กับ Ralph Kimball

posted on 09 Aug 2011 11:34 by roofimon

เรื่องมีอยู่ว่าต้องเตรียมการอะไรบางอย่างสำหรับงานสัมมนาเรื่อง Pentaho ก็เลยอยากจะเกริ่นนำไปเรื่อง Data Warehouse และแน่นอนเรื่องหนึ่งที่น่าสนใจเกียวกับ Data Warehouse คือการต่อสู้กันระหว่างสองขั้วความคิด (Two School Of Thought) นั่นคือเรื่องราวของ Raph Kimball (Star Schema with Share Dimentional) กับ Bill Inmon (Enterprise Data Warehouse, Cooperate Information Factory) สองศาสดาแห่ง Data Warehouse 

คำถามคือเราจะเลือกเริ่มทำงาน ด้วยแนวคิดไหนเป็นหลักระหว่างการทำ Enterprise Data Warehouse หรือการทำ Star schema database with fact tables sharing conformed dimensions? สิ่งแรกเลยเราควรเข้าใจก่อนว่า Enterprise Data Warehouse หรือ Cooperate Information Factory นั้นคือสถานที่เก็บข้อมูลทั้งหมดขององค์กรโดยที่โครงสร้างข้อมูลจะถูกออกแบบ ให้อยู่ในรูปของ Third Normal Form หลักจากนั้นเมื่อมีความต้องการใช้ข้อมูล สิ่งที่ต้องทำคือการทอนขนาดของ Warehouse ลงให้เหลือเท่ากับสิ่งที่ต้องการใช้และดึงข้อมูลออกไปเก็บไว้ ดังนั้นจะเห็นว่า Data Mart ของ EDW คือตัวย่อขนาดของ EDW นั่นเอง และการบวนการสร้าง EDW ที่ Inmon เสนอนั้นเรามักรู้จักกันในนามของ Top-Down Approach คือการสร้างของใหญ่ก่อนแล้วค่อยๆสร้าง Data Mart ในภายหลังวิธีการนี้ใช้เวลาการสร้างที่ยาวนานและลงทุนสูงแต่สิ่งที่ได้คืน กลับมาคือ ข้อมูลที่มีความถูกต้องเที่ยงตรงในเชิงเวลา(Time Variant) สูงมาก

ส่วน Star Schema Approach นั้นจะคิดอีกด้านหนึ่งนั่นคือโครงสร้างข้อมูลจะถูกเก็บในรูปแบบของ Fact Table  และข้อมูลใน Fact Table จะถูกโยงออกมาพบกับ Dimensional Table โดยวิธีการออกแบบจะไม่ใช้หลัการ Normalization แต่จะทำการ Denormailize แทนเพื่อลดความซับซ้อนของ Query ซึ่งจะส่งผลให้ Business User สามารถเข้ามาใช้ข้อมูลได้อย่างสะดวกมากขึ้นนอกจากนี้ จะมีไม่การทำสิ่งที่เรียกว่า Centralize Data เหมือน EDW แต่จะทำการสร้างชุดของ  Star Schema ขนาดเล็กที่สามารถตอบโจทย์ของแต่ละ Business Domain(โดยสิ่งนี้เรียกว่า Data Mart) ไว้เป็นจำนวนมาก นั่นคือที่มาของคำว่า Kimball Bus นั่นเอง ดังนั้นการสร้าง Data Warehouse Bus จะเป็นการทำแบบ Bottom Up นั่นเองคือสร้าง Data Mart จาก Business Process ที่ต้องการก่อนแล้วจากนั้นค่อยๆประกอบมันเข้าด้วยกันเป็น Warehouse ขนาดใหญ่ กระบวนการนี้ใช้เวลาน้อยกว่าเงินน้อยกว่าแต่มักจะจบลงด้วยการมี Silo เป็นจำนวนมากและอาจเกิดความไม่เที่ยงตรงได้ถ้าไม่มีการทำ Data Governance ที่ดี

เอาคร่าวๆเท่านี้ก่อนนะครับ ท่านใดมีข้อมูลที่ต้องการเสริมขอเชิญได้นะครับ -/\-

Functions
ใน Erlang นั้นฟังก์ชั่นจะขึ้นต้นด้วยอะตอมที่ใช้เป็นชื่อของฟังก์ชั่น โดยที่ส่วนหัวของฟังก์ชั่นจะประกอบไปด้วยชื่อและคู่ของวงเล็บที่ครอบพารามิเตอร์ที่ต้องการส่ง (มากว่าศูนย์ตัว) โดยที่พารามิเตอร์ที่ส่งเข้าไปในฟังก์ชั่นนั้นจะมีชื่อเรียกว่า arity หลังจากนั้นเราจะใช้เครื่องหมาย ( -> ) เพื่อระบุว่าเรากำลังจะเข้าสู่เนื้อของฟังก์ชั่น ตามตัวอย่างที่ 2-1 ใช้แสดงฟังก์ชั่นของ Erlang ที่สร้างขึ้นมาเพื่อคำนวณพื้นที่ของรูปร่างต่างๆ โดยที่ในเนื้อของฟังก์ชั่นถ้าเรามีการไปเรียกหรือทำงานมากกว่าหนึ่งอย่างเราก็สามารถทำได้ด้วยการแยกส่วนเหล่านั้นด้วยเครื่องหมาย , และการแยกแพทเทิร์นที่ต่างกันของฟังก์ชั่นสามารถทำได้ด้วยการใส่เครื่องหมาย ; และเมื่อเราต้องการจบฟังก์ชั่นเราต้องจบมันด้วยเครื่องหมาย .  
    area({square, Side}) ->
        Side * Side ;
    area({circle, Radius}) ->
        math:pi() * Radius * Radius;
    area({triangle, A, B, C}) ->
        S = (A + B + C)/2,
        math:sqrt(S*(S-A)*(S-B)*(S-C));
    area(Other) ->
            {error, invalid_object}.
เมื่อฟังก์ชั่นถูกเรียกมันจะทำการวิ่งหาแพทเทิร์นที่เหมาะสมถ้ามันพบแพทเทิร์นที่เหมาะสมมันก็จะทำการส่งพารามิเตอร์ที่เข้าไปในส่วนของฟังก์ชั่นนั้น โดยที่มีหลักการการเขียนแพทเทิร์นที่ดีคือทุกแพทเทิร์นจะต้องระบุพารามิเตอร์ให้เหมาะกับแพทเทิร์นที่ถูกสร้างและไม่รบกวนกับแพทเทิร์นอื่น เพื่อที่เราจะได้กำหนดแพทเทิร์นสุดท้ายสำหรับจัดการกับการเรียกใช้ที่ไม่เข้ากับแพทเทิร์นใดๆเลย จากตัวอย่าง 2-1 ฟังก์ชั่น area จะทำงานเมื่อเราส่งแพทเทิร์น square, circle และ triangle มาและจะส่งค่าทัปเปิ้ล {error, invalid_object} ในกรณีที่เราส่งแพทเทิร์นอื่นเข้ามา
    area({circle, 2}).
เมื่อเราทำการเรียกฟังก์ชั่นในลักษณะนี้มันจะวิ่งไปแมปกับแพทเทิร์นแรกก่อนซึ่งผลคือจะไม่ผ่านเพราะ circle ไม่เท่ากับ square มันจึงทะลุลงไปที่แพทเทิร์นที่สองและทำการเปรียบเทียบซึ่งจะตรงทั้งแพทเทิร์นจำนวนพารามิเตอร์ดังนั้นตัวฟังก์ชั่นบริเวณนั้นจะเริ่มทำงาน math:pi()*2*2, ผลจะได้หลังจาการป้ดเศษ 12.57 และเมื่อมีการพบแพทเทิร์นที่เหมาะสมแล้วนั่นหมายความว่าแพทเทิร์นที่เหลือจะไม่ถูกเปรียบเทียบและสำหรับคลอสสุดสุดท้าย area(Other)-> ... มีไว้สำหรับจับคลอสที่เหลือจากสามคลอสที่อยู่ด้านบนซึ่งจะโยนเออร์เร่อร์ออกมา
    {error, invalid_object}.
ต่อไปเราจะลองสลับข้างเอาคลอสอื่นขึ้นมาก่อน ดังนั้นผลที่ได้คือการเปรียบเทียบจะตกลงไปที่ Other ทั้งหมด {error, unknown_shape}, เพราะการเปรียบเทียบกับ Other จะสำเร็จตลอดเวลา
    flatten(Other) -> {error, unknown_shape};
    flatten(cube) -> square;
    flatten(sphere) -> circle.
ต่อไปเป็นตัวอย่างการเขียนโปรแกรมฟังก์ชั่น factorial
    factorial(0) -> 1;
    factorial(N) ->
        N * factorial(N-1).
ถ้าเราเรียก factorial(3) การเปรียบเทียบแพทเทิร์นครั้งแรกกับ factirial(0) จะไม่สำเร็จทำให้ระบบจะทำการวิ่งลงไปหาคลอสที่สองนั่นคือ factorial(N) ซึ่งจะสำเร็จและจะผูก N เข้ากับ 3 ซึ่งคลอสนี้จะส่ง 3*factorial(2) ออกมา
สิ่งที่ระบบจะทำต่อไปคือเรียก factorial(2) ซึ่งผลที่ได้จากการเรียกคือ 2*factorial(1) ต่อไปมันก็จะเรียก 1*factoial(0) ที่น่าสนใจคือ factorial(0) จะส่งค่า 1 กลับและทำการจบการเรียกเพราะพบเครื่องหมาย ; เราสามารถเขียนเป็นชั้นๆเพื่อให้เห็นภาพได้ดังนี้
    factorial(3).
    Level 1: 3 * factorial(3 - 1) (returns 6)
    Level 2: 2 * factorial(2 - 1) (returns 2)
    Level 3: 1 * factorial(1 - 1) (returns 1)
    Level 4: 1 (returns 1)

Modules
ฟังก์ชั่นจะถูกจับกลุ่มกันเป็นสิ่งที่เรียกว่าโมดูล โดยที่โมดูลนั้นจะต้องถูกจัดไว้ในไฟล์ที่มีนามสกุล .erl และชื่อไฟล์กับชื่อโมดูลจะต้องตรงกันเท่านั้นและชื่อโมดูลจะต้องเป็นไปตามมาตรฐาน -module(Name) ตามตัวอย่าง
ที่เราจะสร้างโมดูล demo ในไฟล์ demo.erl
    -module(demo).
    -export([double/1]).
        % This is a comment.
        % Everything on a line after % is ignored.
        double(Value) ->
           times(Value, 2).
        times(X,Y) ->
            X*Y.
โดยที่คำสั่ง export นั้นจะเป็นตัวที่บอกว่าฟังก์ชั่นไหนจะถูก export ตามมาตรฐาน Function/Arity ฟังก์ชั้นนี้เป็น Global ฟังก์ั่ชั่นนั่นแสดงว่ามันสามารถถูกเรียกได้จากข้างนอกโมดูลได้ตัวอย่างการเรียกใช้ฟังก์ชั่นในดมดูลสามารถทำได้ดังนี้เช่น demo:double(2) และผลที่ได้คือ 4. สำหรับฟังก์ชั่นที่เป็นโลคอลนั้นจะถูกเรียกได้เฉพาะในโมดูลนี้นั้นๆเท่านั้น 
วิธีการระบุตัวตนของฟังก์ชั่นใน Erlang จะทำผ่านชื่อ, จำนวนพารามิเตอร์และโมดูลที่เป็นเจ้าของมัน โดยที่ฟังก์ชั่นในโมดูลเดียวกันอาจจะมีชื่อเหมือนกันแต่มีจำนวนพารามิเตอร์ไม่เท่ากันก้ได้ และในการใช้งานเราไม่จำเป็นต้องเรียงลำดับว่าต้องสร้างฟังก์ชั่นก่อนแล้วถึงจะเรียกใช้งานมันได้ สำหรับ Erlang แล้วเราสามารถสลับตำแหน่งให้การสร้างฟังก์ชั่นไว้หลังการเรียกใช้ได้
หลังจากที่เราทำการสร้างโมดูลแล้วส่วนต่อไปคือเราต้องคอมไพล์เพื่อนำไปใช้งาน สำหรับ Erlang แล้วการคอมไพล์จะทำให้เราได้ไฟล์อีกหนึ่งไฟล์ที่ชื่อเหมือนโมดูลแต่นามสกุลต่างกัน (module.beam) ต่อไปเราลองมาใช้ความสามารถเบื้องต้นของ Erlang Shell กันโดยให้เปิดมันขึ้นมาที่ไดเรคทอรี่ที่เราเก็บ source code ของเราไว้ โดยเราสามารถย้ายตำแหน่งไปมาได้เหมือนอยู่บน OS Shell ด้วยการใช้คำสั่ง cd(Directory) และเมื่อเราย้ายไปยังไดเรคทอรี่ที่เราต้องการแล้วเราก็สามารถคอมไฟล์โปรแกรมที่เราต้องการได้เลยด้วยการใช้คำสั่ง c(Module) ที่เราต้องการ
ในระบบ Erlang ขนาดใหญ่นั้นจะประกอบไปด้วยโมลูลจำนวนมาก ซึ่งทั้งหมดจะถูกคอมไพล์ด้วยมาตรฐานเดียวกันหมด ซึ่งผลที่ได้จากการคอมไพล์คือไฟล์ .beam ที่มี byte code อยู่ภายในและเราสามารถเรียกใช้มันได้และในการเรียกใช้ฟังก์ชั่นเราต้องอ้างถึงชื่อเต็มของฟังก์ชั่นนั้นๆ เนื่องจากเรากำลังพยายามเรียกใช้มันจากนอกโมดูล
    1> cd("/home/francesco/examples").
        /home/francesco/examples
        ok
    2> c(demo).
        {ok,demo}
    3> demo:double(10).
        20
    4> demo:times(1,2).
    ** exception error: undefined function demo:times/2

Module Directives
จากตัวอย่างที่ผ่านมาเราจะเห็นว่าโมดูลทุกตัวจะมีส่วนประกอบอีกตัวที่ชื่อว่า แอททริบิวท์(Attribute) โดยมันจะถูกเขียนอยู่ในลักษณะ -attribute(Value) โดยที่มันมักจะถูกเขียนไว้ที่ส่วนหัวของโมดูล สำหรับแอททริบิวท์ที่บังคบว่าจะต้องมีเสมอคือ -module เพื่อใช้บอกชื่อของโมดูลนั้นและตามมาด้วย -export ที่ใช้สำหรับการแสดงรายละเอียดของฟังก์ชั่นเช่น function/arity
A useful directive when programming is the –compile(export_all) directive, which at compile time will export all functions defined in the module. Another way of doing this
is to specify an option on compiling the file:
โมดูลไดเรคทีฟอีกตัวที่เป็นประโยชน์คือ -compile(export_all) ซึ่งมันจะทำการ export ทุกฟังก์ชั่นที่มีในโมดูลออกมาในจังหวะที่ทำการคอมไพล์ ซึ่งอีกทางที่เราสามารถทำได้คือการระบุออปชั่นไว้จังหวะที่เราทำการสั่งคอมไพล์ 
    c(Mod,[export_all]).
แต่สำหรับคำสั่งนี้เหมาะสำกรับการ test เท่านั้น ไม่ควรใช้บนงานแบบ production ต่อไปเป็นอีกคำสั่งที่น่าสนใจคือ -import(Module, [Function/Arity,...]) เราใช้สำหรับการ import ฟังชั่นจากโมดูลอื่นที่เราต้องการมาใช้ในโมดูลของเรา ยกตัวอย่างเช่น –import(math,[sqrt/1]) ที่เราเคยใช้ไปก่อนหน้านี้แล้ว
    -import(math, [sqrt/1]).
    area({triangle, A, B, C}) ->
        S = (A + B + C)/2,
        sqrt(S*(S-A)*(S-B)*(S-C));
ในบางกรณีการใช้คำสั่ง import อาจทำให้เราสับสนได้ว่าฟังก์ชั่นที่เราเรียกใช้งานเป็นฟังก์ชั่น Local ดังนั้นในบางกรณีเราต้องตรวจสอบรายละเอียดของฟังก์ชั่นนั้นด้วยคำสั่ง Mod:module_info/0 หรือ Mod:module_info/1 และจาก shell ก็สามารถเรียก m(Module)
    5> demo:module_info().
        [{exports,[{double,1},{module_info,0},{module_info,1}]},
            {imports,[]},
            {attributes,[{vsn,[74024422977681734035664295266840124102]}]},
            {compile,[{options,[]},
            {version,"4.5.1"},
            {time,{2008,2,25,18,0,28}},
            {source,"/home/francesco/examples/demo.erl"}]}]
    6> m(demo).
            Module demo compiled: Date: February 25 2008, Time: 18.01
            Compiler options: []
            Object file: /home/francesco/examples/demo.beam
            Exports:
                double/1
                module_info/0
                module_info/1
                ok

Erlang Complex Data Structures

ในเอิร์ลแลง(Erlang)โครงสร้างข้อมูลที่ซับซ้อนหมายถึงการนำโครงส้างข้อมูลแบบพื้นฐาน ไม่ว่าจะเป็นอะตอม ลิสท์ ทัปเปิล และอื่นๆมาประกอบรวมกันเพื่อจำลองข้อมูลที่ซับซ้อนบางอย่าง เช่นเราต้องการสร้าง person ให้เป็นโครงสร้างที่ซับซ้อนที่ประกอบไปด้วย ชื่อ นามสกุล สัตว์เลี้ยง ความชื่นชอบ หรือ อื่นๆ จะได้ออกมาประมาณนี้
[{person,"Joe","Armstrong",
    [ {shoeSize,42},
        {pets,[{cat,zorro},{cat,daisy}]},
        {children,[{thomas,21},{claire,17}]}]},
 {person,"Mike","Williams",
    [ {shoeSize,41},
        {likes,[boats,wine]}]}
]

หรือถ้าเราต้องการสร้าง person ขึ้นมาแบบค่อยเป็นค่อยไปทีละขั้น ด้วยการใช้โครงสร้างพื้นฐานก็ทำได้แต่ผลที่ออกมาจะดูอ่านยากมาก

1> JoeAttributeList = [{shoeSize,42}, {pets,[{cat, zorro},{cat,daisy}]},
1> {children,[{thomas,21},{claire,17}]}].
        [{shoeSize,42},
            {pets,[{cat,zorro},{cat,daisy}]},
            {children,[{thomas,21},{claire,17}]}]
2> JoeTuple = {person,"Joe","Armstrong",JoeAttributeList}.
        {person,"Joe","Armstrong",
            [{shoeSize,42},
                {pets,[{cat,zorro},{cat,daisy}]},
                {children,[{thomas,21},{claire,17}]}]}
3> MikeAttributeList = [{shoeSize,41},{likes,[boats,wine]}].
        [{shoeSize,41},{likes,[boats,wine]}]
4> MikeTuple = {person,"Mike","Williams",MikeAttributeList}.
        {person,"Mike","Williams",
            [{shoeSize,41},{likes,[bo