แปล SQL เป็น Linq - Part II

Posted 15/10/2009 01:23 by chaowman

ต่อจาก Post ที่แล้ว ครับ
ผมได้เกริ่นวิธีสำหรับในการ SELECT Field แบบต่าง ๆ ไปแล้ว
คราวนี้ เรามาดูกันว่า คำสั่ง WHERE และ JOIN เขียนเป็น Linq ยังไง

 

WHERE - Simple

เริ่มจากคำสั่ง WHERE แบบธรรมดา
คำสั่ง SELECT ในตอนที่แล้วเป็นการเลือกว่าจะเอา Column ไหนมาอยู่ในผลลัพธ์
ส่วนคำสั่ง WHERE จะเป็นการเลือกว่าจะเอา Row ไหนมารวมอยู่ในผลลัพธ์

image 

เลือกลูกค้าทั้งหมดที่มี CustomerID เป็น 'ABC'
ใน Linq เขียนอย่างนี้ครับ

image

ใช้คำสั่ง WHERE เช่นเดียวกับ SQL
ผลลัพธ์ก็จะได้ List ของลูกค้าเฉพาะที่มี CustomerID เป็น 'ABC'

 

WHERE - In

คราวนี้มาลองแบบ Advance ขึ้นมาหน่อย
คุณอยากเลือกลูกค้าจากข้อมูลใน Table อื่น
เช่นคุณอยากรู้ว่าลูกค้าคนไหนมียอด Order ในปีนี้บ้าง
ซึ่ง Table ที่เกี่ยวข้องคือ Customers และ Orders

ใน SQL เราสามารถใช้คำสั่ง IN และ Sub-Query ในการ Query ข้อมูล ดังนี้

image 

Sub-Query คือที่อยู่ในวงเล็บ บอกว่า
เลือก CustomerID จาก Orders โดยเลือกเฉพาะ Order ที่มีปีเป็น 2009

และตัว Query หลักบอกว่า
เลือก Customer ที่มี CustomerID อยู่ใน Sub-Query

ใน Linq เขียนอย่างนี้ครับ

image 

ใน Linq เราสามารถประกาศ Query แยกออกมาเพื่อความง่ายในการอ่าน
(แต่ถ้าคุณชอบ Sub-Query ใน Linq ก็สามารถทำได้เช่นเดียวกัน)

Query ตัวแรกเป็นการใช้ WHERE ธรรมดา บอกว่า
เลือก Order ในปี 2009 และเลือกเฉพาะ CustomerID

Query ตัวที่สองจะใช้ Contains ในขณะที่ SQL ใช้ IN
โดยเอา Sub-Query ขึ้นก่อนแล้วก็ใช้คำสั่ง Contains ตามด้วย CustomerID

ซึ่งผลลัพธ์ก็จะได้ลูกค้าทั้งหมดที่มี Order ในปี 2009

 

WHERE - Exists

คราวนี้เอาแบบ Advance ขึ้นไปอีก
เป็นการเลือกแบบต้องเปรียบเทียบเงื่อนไขจากหลาย Table เช่น
คุณต้องการหาว่าลูกค้าคนไหนที่มีที่อยู่ไม่ตรงกับที่อยู่จัดส่งบ้าง

ซึ่งที่อยู่จะอยู่ใน Customers ส่วนที่อยู่จัดส่งจะอยู่ใน Orders
เราสามารถใช้ EXISTS เพื่อแก้ปัญหานี้

image

ใน Query หลักเราเลือกลูกค้าทุกคน ที่อยู่ใน Sub-Query
ส่วนใน Sub-Query เราบอกว่า
เลือกทุก Order ที่ Order นั้นมี CustomerID ตรงกับ CustomerID ของตารางหลัก
และมี ShipCity ไม่ตรงกับ City ของตารางหลัก

เปรียบเทียบเป็น Linq เขียนอย่างนี้

image

จะเห็นว่าคล้าย ๆ กับ SQL
สิ่งที่ต่างจาก SQL คือ
ใน Linq เราจะใช้คำสั่ง Any ซึ่งห้อยอยู่ท้าย Sub-Query แทนที่จะใช้ EXISTS เหมือนกับ SQL

ผลลัพธ์ของ Query คือ ลูกค้าทุกคนที่มีที่อยู่จัดส่งไม่ตรงกับที่อยู่ของลูกค้า

 

JOIN – Simple

คุณสามารถเลือกข้อมูลมาจากตารางมากว่า 2 ตารางได้โดยใช้ JOIN
ใน SQL JOIN ใช้อย่างนี้ครับ

ในส่วนของ FROM คุณก็ใส่ชื่อตารางเพิ่มเข้าไปได้เลย
ใน Query นี้เป็นการเลือกทั้งตาราง Customers และ Orders

Linq เขียนอย่างนี้ครับ

image 

จุดที่ต่างกันคือ SQL ใช้ , (Comma) คั่นระหว่างชื่อตาราง
แต่ Linq ใช้คำสั่ง FROM อีกครั้งไปเลย

ผลลัพธ์ของ Query จะได้ CustomerID และ OrderID ที่ Order เป็นปี 2009

 

JOIN – INNER

*** คำเตือน ***
บทความต่อจากนี้ถ้าไม่แม่น SQL จะเข้าใจยากมากครับ
คุณอาจจะข้ามไป Part ถัดไปได้เลย
เนื่องจากโครงสร้างของ Object จะเชื่อมโยงกันผ่าน Property
ซึ่งต่างจาก Database ที่ต้องเชื่อมโยงกันผ่าน Reference
ดังนั้นคำสั่ง JOIN ใน Linq ไม่ค่อยมีความจำเป็นมากครับ

ถ้าทำใจได้แล้วมาต่อกันครับ
ถ้าคุณเขียน SQL คุณคงทราบว่าการเลือกตารางมากกว่า 2 ตาราง คุณอาจจะใช้ INNER JOIN ก็ได้

image

INNER JOIN แทบจะไม่ต่างกับ JOIN ธรรมดาเลย
แต่โดยทั่วไปมักจะนิยมใช้ INNER JOIN กันมากกว่าเพราะ

  1. Code SQL ของคุณจะอ่านและแก้ไขง่ายขึ้น
  2. เป็นคำสั่งแบบเดียวกับ OUTER JOIN ทำให้สามารถใช้ร่วมกันได้
  3. Code เป็นมาตรฐาน

บ้างก็ว่าจะทำให้ Performance เพิ่มขึ้น
แต่เท่าที่ผมดู Execution Plan ทั้งใน SQL Server และ Oracle มันไม่ต่างกันเลย

ใน Linq เราก็สามารถ INNER JOIN ได้

image

ใน Linq จะมีเพียง JOIN แบบเดียวคือ INNER JOIN
และ Syntax ก็ค่อนข้าง Fix คือ ต้องเปรียบเทียบแบบเท่ากับ (equals) เท่านั้น

และถ้าคุณจะต้องใช้ JOIN ใน Linq
ผมว่าคุณ Design Class ได้ไม่ดีแล้วล่ะครับ
อย่าลืมว่า Object มันเชื่อมโยงกันผ่าน Property ได้เลย
ไม่จำเป็นต้องมา JOIN เหมือน SQL

ดังนั้นใน Class Customers คุณควรจะต้องมี Property ชื่อ Orders
แล้วเวลาเขียน Linq ให้เขียนอย่างนี้แทนครับ

image 

จะเห็นว่า Code อ่านง่ายขึ้น

ถ้าคุณใช้ Linq to SQL สร้าง Class ให้
อย่าลืมกำหนด Reference ตารางต่าง ๆ ใน Database ด้วย
เพราะ Linq to SQL จะสร้าง Property ที่ Link ไปตารางอื่นให้อัตโนมัติ

นอกจากนี้ใน SQL
INNER JOIN คุณสามารถเปรียบเทียบ Field มากกว่า 1 Field

image

ในที่นี้เราเลือกเฉพาะลูกค้าและ Order ที่มีที่อยู่จัดส่งกับที่อยู่ของลูกค้าเป็นอันเดียวกัน

VB และ C# เขียน Concat keys ต่างกันนิดหน่อยครับ
ใน VB เขียนอย่างนี้

image

จะเห็นว่าค่อนข้างใกล้เคียงกับภาษา SQL

ใน C# เขียนอย่างนี้ครับ

image

จะเห็นว่า Syntax ของ VB ค่อนข้างใกล้เคียงกับ SQL ในหลาย ๆ ด้าน
เหมือนกับว่าเอา C# มาปรับปรุงอีกที

 

JOIN – Left

LEFT OUTER JOIN ใน SQL หมายถึงเลือกทั้งหมดจากตารางแรก
แต่เลือกเฉพาะข้อมูลที่ตรงกับเงื่อนไขในตารางที่ 2 เท่านั้น

เช่น ถ้าเราต้องการแสดงลูกค้าและยอดขายของแต่ละลูกค้า
แต่ถ้าลูกค้าคนไหนไม่มี Order ก็ให้แสดงมาด้วย แต่ยอดขายเป็น NULL
ใน SQL เขียนอย่างนี้ครับ

 image

แต่อย่างที่บอกด้านต้น Linq ไม่มี OUTER JOIN
Linq มีแต่ INNER JOIN
แต่โชคดีครับ Linq มี GROUP JOIN
ซึ่ง GROUP JOIN ก็คือ LEFT OUTER JOIN ผสมกับ GROUP BY นั่นเอง

ถ้าไม่ค่อยเข้าใจ ดูตัวอย่างครับ
VB เขียนอย่างนี้ครับ

image

เปลี่ยน LEFT OUTER JOIN เป็น GROUP JOIN และตัดคำสั่ง GROUP BY ออกไป
การทำ Aggregation เช่น SUM, COUNT ให้ทำหลังคำสั่ง Into ครับ
(จริง ๆ ทำที่คำสั่ง SELECT ตามปกติก็ได้ แต่ต้องใช้ Lambda Expression ครับ ใน VB ผมไม่ค่อยถนัด)

ใน C# ต่างออกไปนิดหน่อย

image

C# ไม่มีคำว่า GROUP JOIN ใช้ JOIN ได้ตามปกติเลยครับ
แต่ถ้าเติม Into ตามหลัง จะทำให้ JOIN ธรรมดา กลายเป็น GROUP JOIN
ด้านหลัง Into ของ C# ไม่สามารถทำ Aggregate ได้เหมือน VB
เลยต้องมาทำใน SELECT แทนเท่านั้นครับ
ตัวแปร g คือ Group ของ Orders ในแต่ละลูกค้าครับ

แต่อย่างที่บอก ถ้ามีคำสั่ง JOIN ใน Linq
หมายความว่าคุณอาจจะต้อง Design Class ใหม่ครับ
ถ้า Class Customer ของคุณมี Property ชื่อ Orders
คุณสามารถปรับ Query ของคุณให้อ่านง่ายขึ้นอย่างนี้ครับ

VB

image 
ตัดคำสั่ง GROUP JOIN ออก สั่ง Aggregate แทน
Aggregate เป็นการบอกว่าเราจะใช้คำสั่ง Aggregation เช่น Sum, Count
คำสั่ง Aggregate เข้ามาช่วยทำ Aggregation เท่านั้น

C#

image

ส่วน C# ตัด JOIN ออกไปทั้งบรรทัด
แล้วใช้ c.Orders แทนตัวแปร g

GROUP JOIN ใน Linq มีปัญหาอย่างนึงครับ
เพราะ GROUP JOIN คือ LEFT JOIN + GROUP BY
ดังนั้นเราจะทำแค่ LEFT JOIN จะมีปัญหามากครับ
คือเราต้องแตก Group มาอีกครั้งก่อนใช้งาน

ดูตัวอย่างของ SQL ก่อนครับ

image

จะเห็นว่าคำสั่งของ SQL ดู Simple มากใช่ไหมครับ

คราวนี้มาดูของ VB

image

เราต้องเอา DefaultIfEmpty มาช่วย เพื่อแตก Group ออกมา
โดยที่ Group ที่มีข้อมูลก็มีข้อมูลตามปกติ
แต่ Group ที่ไม่มีข้อมูลก็จะสามารถ Query ข้อมูลได้แต่ได้ Null มาแทน

นอกจากนี้ถ้าคุณเขียน Linq ที่ Query ข้อมูลจาก List
ที่ไม่ได้มาจาก Linq to SQL
คุณต้องระวัง Object Reference Exception ด้วยครับ
ดังนั้นคุณจะต้องเช็ค Null ก่อน แล้วค่อย SELECT Property ออกมา

มาดู C# บ้าง

image

ก็คล้าย ๆ VB เติม DefaultIfEmpty เข้าไป
แล้วก็ระวังเรื่อง Object Reference Exception เท่านั้น

คราวหน้า เจอกัน Part สุดท้ายของ Set นี้
มี OrderBy, GroupBy และ Set Operation ครับ

About chaowman

หาอะไรอยู่หรือจ๊ะ?

ร่วมให้กำลังใจนักเขียน

อ่านแล้วชอบใจ อยากให้กำลังใจกับผู้แต่งบทความนี้ ขอเชิญร่วมให้กำลังใจผ่าน Paysbuy/Paypal นะครับ ปลอดภัยเพราะทำงานผ่าน SSL และไม่มีค่าใช้จ่ายเพิ่มเติมครับ เว็บเราให้นักเขียน 100% ครับ

Comment ระบบเก่า

 

อ่าน said:

Part สุดท้ายแล้วนะครับ ต่อจาก Post ที่แล้ว คราวนี้เรามาดูวิธีใช้ Linq สำหรับ Order By, Group By และ Set

October 27, 2009 8:08 PM
 

frontpage said:

  คุณยังรู้สึกว่า ตอนนี้เขียนโปรแกรมทำอะไรที่ต้องแสดงข้อมูลที เป็นเรื่องยุ่งยากน่าเบื่อหรือเปล่า

March 21, 2010 3:49 AM
(required)  
(optional)
(required)  
Add

DisQUS Comment (ยังเอ๋อๆ อยู่)

blog comments powered by Disqus