My Blog List

Tuesday, August 12, 2014

[D3js] ทำความรู้จักกับ D3js และอะไรที่ทำให้มัน น่าสนใจ (1/2)

สวัสดีครับ
วันนี้ผมมานำเสนอเรื่องราวเกี่ยวกับ D3js (Data-Driven Documents) เป็นการใช้ javascript โดยเอา Data ไปผูกไว้ (เดี๋ยวผมจะอธิบายอีกทีนะครับ ว่ามันคือยังไง)
สามารถเข้าไปติดตามได้ที่ Official d3js website ที่ d3js.org ครับ

บล๊อกนี้จะมีสองส่วน คือ ส่วนของ Coding และ Design นะครับ

Coding
- การติดตั้ง
โหลดไฟล์ d3.min.js มาจาก Official Website แล้วแค่ import มาใช้ก็เรียบร้อยแล้ว
ต่อไปมาดูความสามารถของเจ้า d3js กันนะครับ ผมจะยกบาง Features ที่เด่นๆ แปลกๆ มาเล่าให้ฟัง

หนึ่งในความสามารถของ d3 คือการ Select ซึ่งก็คล้ายๆกับ Selector ของเจ้าอื่น ที่สามารถเลือกได้ทั้ง id, tag, class แต่ความสามารถของมันจริงๆที่ซ่อนอยู่ ก็คือ การ Select Node ที่ไม่มีอยู่จริงได้ด้วย
มันจะสร้างเป็น Virtual node เอาไว้ เวลาที่มันหาไม่เจอ แต่เจ้า Virtual node นี้ไม่ใช่ Concrete นะครับ คือไม่มีตัวตนอยู่จริงๆ (แต่เราก็สามารถทำให้มันมีตัวตนจริงๆได้)
มาชมตัวอย่างกันซักหน่อย เดี๋ยวจะนึกภาพตามไม่ออก
ตัวอย่างข้างบนนี้แสดงให้เห็นว่า เราสามารถ Select P tag ได้สบายๆ 
ลองมาดูอีกครั้งครับ
จากตัวอย่างนี้ แสดงให้เห็นว่า เราก็สามารถ Select H tag ซึ่งตอนนี้มันไม่มีอยู่จริงได้เหมือนกัน
เจ้าตัวนี้แหละครับ มันคือ Virtual Node ที่ทำให้ D3 เจ๋ง

และมีการใช้ selectAll เอาไว้ Select หลายๆตัว แล้วจัดการอะไรพร้อมๆกัน
เช่น

ต่อไปคือใจความสำคัญของเจ้า d3js เลยครับ นั่นคือ data()
คือเราสามารถแปะข้อมูล เข้าไปกับ Node ใดๆ ก็ได้ และเรียกใช้เมื่อใดก็ได้ (ข้อมูลจะถูกแปะอยู่ที่ Node อย่างนั้น ไม่หายไป)
มาดูตัวอย่างกันนะครับ
สังเกตว่า ผม "แปะ" data ที่เป็น Array มีสมาชิกตัวเดียว คือ 1 เข้าไปให้กับ G tag
เลข 1 ของผม ก็จะเข้าไปอยู่ใน G tag ในตัวแปรที่ชื่อ data ครับ

*เพิ่มเติมนะครับ .data() พารามิเตอร์ที่จะส่งไป ต้องเป็น Array นะครับ ถ้าคุณมีตัวแปรที่เป็น object ก็แค่ใส่ [ ] คร่อมมันไป ก็จบแล้วครับ

ต่อไป มาใช้ความสามารถของ d3 กันให้เต็มที่หน่อย
โดยการ select tag ที่ไม่มีอยู่จริง แล้วเอา data เข้าไปแปะ แล้วดูว่าผลลัพธ์มันจะเป็นยังไง
ในที่นี้ ผม SelectAll G tag ซึ่งมันไม่มีอยู่จริง ก็จะเลยสร้าง Virtual Node ให้ผม
แล้วเมื่อผมเอา ข้อมูลไป "แปะ" แต่ข้อมูลดันเป็น Array 3 ตัว
ผลลัพธ์ก็คือ ผมจะได้ Virtual Node มา 3 ตัว
โดยที่
Virtual node ตัวแรก มี data = 1
Virtual node ตัวที่สอง มี data = 2
Virtual node ตัวที่สาม มี data = 3
ตามลำดับครับ

แต่การมี Virtual node ก็ไม่สามารถเอาไปทำอะไรต่อได้ นอกเสียจากเราเปลี่ยนมัน ให้กลายเป็น Node จริงๆซะก่อน ด้วยคำสั่ง .enter() มันจะสร้าง Virtual node ขึ้นเป็น Concrete Node  มีเนื้อ มีหนัง จับได้ ลูบได้ อูวววว์ (จะเสียวทำไม?)

เมื่อเราสั่ง .enter() แล้ว เราต้องสั่ง .append("g") เข้าไปด้วย เพราะมันจะไม่รู้ว่าเรากำลังจะสร้าง Node อะไร
(คือ ไอเจ้า Virtual Node เนี่ย มันเป็นแค่ตัวแปรเบาๆ ไม่ได้มีบอกว่าเป็น Virtual  Node ของ Tag ชนิดไหน)

มาดูตัวอย่างกัน!!!

จะเห็นว่า เมื่อเติม .enter().append("g") เข้าไปแล้ว
จะเห็น ว่ามันสร้าง G tag ขึ้นมาให้เราจริงๆ ... ว้าววว เยี่ยมไปเลย
แถม G แต่ละตัว ยังแปะ data เป็น 1,2,3 ตามลำดับ ให้เราเรียกใช้ได้ภายหลังอีกด้วย

ทีนี้มาดูตัวอย่างการใช้ข้อมูลที่อยู่ในตัวแปว data ที่เรา แปะ เข้าไปกันบ้างครับ
จากตัวอย่างข้างบนนี้ เราจะรู้ว่า Default Parameter ที่ถูกส่งให้ function ต่างๆ มันก็คือตัวแปร data นั่นเอง
เราเอาไปใช้ได้ตามใจของเราเลย
สะดวกดีมั้ยล่ะครับ เขียนบรรทัดเดียว ก็สามารถสร้าง Element ได้พร้อมๆกัน หลายตัว

*Note นิดนึงครับ
คือหากเรา แปะ data ให้กับ Node ใดๆแล้ว
data จะถูกส่งผ่าน ไปยังลูกหลานของมันด้วยครับ เวลาเราสร้าง Element ลูก, มันก็จะมี data นั้นด้วย
เราก็สามารถเปลี่ยน data ได้นะครับ แค่มัน Default ส่งไปให้ด้วย เฉยๆ สะดวกดี

หรือเขียนแบบนี้ก็ได้
ผมสร้างฟังก์ชันขึ้นมา แล้วส่งฟังก์ชันนั้น เข้าไปใน .text() ตรงๆเลย
สังเกตว่า ผมไม่ได้ส่งพารามิเตอร์ ใดๆ ให้ฟังก์ชันเลย มันจะรู้เอง ว่าเรากำลังหมายถึง data แน่ๆ


เอาละครับ บล๊อกนี้จะยาวเกินไปแล้ว
ผมตัดจบตอนที่หนึ่งดีกว่า
บล๊อกถัดไป ผมจะพูดถึงเรื่อง Fantasy drawing function อื่นๆ


- fantasy drawing function (path, arc)
- domain, range good to know

Desing
- การใช้ graph group
- การออกแบบแบบ scalable

[SQL Server] Query ข้อมูลแยกตามเวลา โดยกำหนด Hour_Interval เอง

สวัสดีครับ
การ Query บางทีก็ต้องการข้อมูลเป็น ช่วงของเวลา
เช่น

2014-6-16 02:00:00  A
2014-6-16 04:00:00  B
2014-6-16 06:00:00  C
2014-6-16 08:00:00  D

คงต้องมีให้ใช้กันบ้างใช่ไหมครับ
นั่งคิดอยู่นานว่าจะทำยังไง
จนคิดวิธีนี้ออก วิธียัดสมการคำนวนวันที่ไปตรงๆนี่แหละครับ ฮ่าๆๆ
ไม่ค่อยได้ใช้ความสามารถของ sqlserver ซักเท่าไหร่

ลองดูกันนะครับ ...

ก่อนอื่นก็เตรียมตัวแปรที่จำเป็นให้เรียบร้อย

DECLARE @start_date DATETIME = '2014-07-06 21:00:00' ;
DECLARE @end_date DATETIME = '2014-07-08 5:00:00' ;
DECLARE @i INT = 7;

@i ก็คือตัวแปร ที่ใช้บอกว่า "เอาข้อมูลทุกๆ กี่ชั่วโมง" นะครับ

พอตระเตรียมตัวแปรกันเสร็จก็บรรเลงเลย
;WITH dta AS
(
        SELECT
               MIN(booking_date ) AS 'Date',
               COUNT(booking_id ) AS 'Total_Bookings'
        FROM    myTable (nolock)
        WHERE   booking_date between @start_date and @end_date
        GROUP BY (DATEDIFF( DAY,@start_date ,booking_date)* 24+DATEPART (HOUR, booking_date)-DATEPART (HOUR, @start_date))/@i
)

SELECT * FROM dta ORDER BY dta.[Date] ;

อธิบายโค้ดหน่อย
การใช้ ;WITH (name) AS (...)
เป็นการสร้าง TEMP VARIABLE แบบใช้ได้แค่ อีกครั้งเดียว พอใช้เสร็จ ตัวแปรนี้ก็จะหายไป
เช่น
ผมสร้าง
;WITH dta AS
(
   SELECT * FROM myTable;
เนี่ย พอสร้างเสร็จ
ผมสามารถเขียนโค้ดต่อว่า

SELECT * FROM dta; ถึงตรงนี้ยังใช้ได้ครับ (พอเสร็จจากบรรทัดนี้ dta ก็จะหายไป)
ถ้ามีการเรียกใช้ dta อีก มันก็จะหาไม่เจอแล้ว (บางคนคิดว่าไม่มีประโยชน์ จริงๆแล้วมันมีนะครับ มันสามารถทำให้การเขียนโค้ดเป็น Cascading Style ได้)

อ้ออ ลืมบอกไป
ผมมี Naming Convention ตรงการตั้งชื่อตัวแปร table ที่สร้างขึ้น
ผมจะเขียนเป็น dta ย่อมาจาก "Derived Table A"
ถ้ามีการสร้าง table ขึ้นมาอีก ผมก็จะรันมันไปเรื่อย
dta, dtb, dtc ...

ง่ายดีมั้ยครับ ฮ่าๆๆ 

ต่อไปก็ตรงใจความสำคัญเลย (ซึ่งผมก็ไม่รู้จะเขียนอธิบายยังไงเหมือนกัน)
เอาเป็นว่า ใช้หลักการ ตั้ง start_date ให้เป็น 0 แล้วมันก็จะสามารถหาร HOUR_INTERVAL ได้โดยง่าย ...(เอาเป็นว่า ลองทำความเข้าใจดูเอาเองละกันนะครับ ฮ่าๆๆ)

และผลลัพธ์ก็จะได้ดังนี้
ใครมีวิธีที่ดีกว่านี้ แบ่งปันกันนะครับ