Day: January 13, 2016

  • ข้อจำกัดและข้อควรระวังในการใช้เงื่อนไข IN ในคำสั่ง SELECT บนฐานข้อมูล Oracle

    สำหรับบทความนี้ จะนำเสนอข้อจำกัดและข้อควรระวังในการใช้งานคำสั่ง SELECT บนฐานข้อมูล Oracle ซึ่งประสบมาจากการใช้งานจริงสองเรื่องด้วยกัน

     

    เรื่องแรกจะเป็นข้อจำกัดในการใช้เงื่อนไข IN (value1,value2,value3,…) ในคำสั่ง SELECT  ส่วนอีกเรื่องจะเป็นเรื่องของข้อควรระวังในการใช้ IN ร่วมกับเงื่อนไขที่เป็น subquery ในคำสั่ง SELECT  เช่นกัน

     

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

    รูปแบบที่ 1 รูปแบบ SELECT * FROM TABLE1 WHERE FIELD1 IN  (value1,value2,value3,…)  โดยผลลัพธ์จะเป็นรายการข้อมูลในตาราง TABLE1 ที่ค่าของข้อมูลใน FIELD1 มีอยู่ใน value1,value2,value3 ,…

    รูปแบบที่ 2 คล้ายกับรูปแบบที่ 1  นั่นเอง แต่จะเป็นการใช้ subquery แทนที่ (value1,value2,value3,…)   โดยมีรูปแบบ SELECT * FROM TABLE1 WHERE FIELD1 IN  (SELECT FIELD2 FROM TABLE2) สำหรับผลลัพธ์จะเป็นรายการข้อมูลในตาราง TABLE1 ที่ค่าของข้อมูลใน FIELD1 มีใน FIELD2 ซึ่งเป็นผลลัพธ์จากการ SELECT ข้อมูลจากตาราง TABLE2

     

    ข้อจำกัดในการใช้เงื่อนไข IN (value1,value2,value3,…)

    สำหรับใน Oracle นั้น list รายการที่อยู่ภายในเครื่องหมายวงเล็บ สามารถมีได้มากสุดไม่เกิน 1000 ค่า  ซึ่งบางท่านอาจจะสงสัยว่าในการ SELECT ข้อมูลตามปกตินั้น มีโอกาสน้อยมากที่เราจะพิมพ์ค่าของข้อมูลใน list จนถึง 1000  ค่า แต่ก็มีโอกาสที่จะพบได้คือ เมื่อมีการใช้งานคำสั่งนี้ผ่านโปรแกรมที่เขียนขึ้นนั่นเอง ตัวอย่างเช่น หน้าจอการทำงานของโปรแกรมที่มีลักษณะเป็นชุดของรายการข้อมูลที่ให้ผู้ใช้สามารถเลือกเองได้ จากนั้นรายการที่ถูกเลือกจะถูกส่งไปแปลงเป็นเงื่อนไขในคำสั่ง SELECT  อีกครั้ง ทำให้มีโอกาสที่จะเกิดกรณีที่มี list เกิน 1000 ค่า ได้นั่นเอง

    ในภาพด้านล่างจะเป็นตัวอย่างของเว็บสำหรับสืบค้นหนังสือของห้องสมุด ซึ่งผู้ใช้สามารถเลือกรายการผลลัพธ์จากการสืบค้นและเก็บรวบรวมไว้เพื่อทำการ export ไปใช้งานต่อได้ ซึ่งก็มีโอกาสที่จะเลือกผลลัพธ์ได้เกิน 1000  รายการเกิดขึ้นได้

    Capture

    ถือว่าเป็นจุดหนึ่งที่ผู้พัฒนาควรระมัดระวังในการเขียนโปรแกรมที่มีการใช้เงื่อนไข IN ลักษณะนี้ในคำสั่ง SELECT

     

    ข้อควรระวังในการใช้ IN ร่วมกับเงื่อนไขที่เป็น subquery

    ในที่นี้ขอยกตัวอย่างข้อมูลเพื่อให้เห็นภาพชัดเจน  โดยมีข้อมูลจากตารางสองตาราง คือ TABLE01 และ TABLE02

    สำหรับ TABLE01 เป็นข้อมูลที่ต้องการ SELECT เพื่อให้ได้ผลลัพธ์ออกมา ส่วนตาราง TABLE02 จะเป็นเงื่อนไขที่จะใช้ใน subquery  ข้อมูลในตารางทั้งสองจะเป็นดังนี้

     

    ข้อมูลใน  TABLE01

    t01

    ข้อมูลใน  TABLE02

    t02

     

    จะยกตัวอย่างกรณีการ SELECT ออกเป็น 2 กรณีดังนี้

    1. ต้องการข้อมูลใน TABLE01 ที่ข้อมูลในฟีลด์ F02 ของตารางนี้มีในฟีลด์ F02 ของ TABLE02 ด้วย

    คำสั่งที่ใช้คือ SELECT * FROM TABLE01 WHERE F02 IN (SELECT F02 FROM TABLE02);

    ผลลัพธ์ที่ได้คือ

    result1

    ซึ่งถูกต้อง

     

    1. ต้องการข้อมูลใน TABLE01 ที่ข้อมูลในฟีลด์ F02 ของตารางนี้ไม่มีในฟีลด์ F02 ของ TABLE02 โดยปรับคำสั่งจาก IN เป็น NOT IN

    คำสั่งที่ใช้คือ SELECT * FROM TABLE01 WHERE F02 NOT IN (SELECT F02 FROM TABLE02);

    ผลลัพธ์ที่ได้คือ

    result2

    ซึ่งถูกต้อง

     

    จะเห็นว่าทั้งสองกรณีทั้งการใช้ IN หรือ NOT IN ผลลัพธ์ที่ได้ก็ออกมาถูกต้อง

     

    ทดลองต่อไปโดยการเพิ่มข้อมูลในตาราง TABLE02 ดังนี้

    t02_2

    โดยข้อมูลที่เพิ่มจะมี 1 รายการที่ข้อมูลใน F02 มีค่าเป็น null

     

    จากนั้นลองทำการ SELECT แบบเดิมดังนี้

    1. ต้องการข้อมูลใน TABLE01 ที่ข้อมูลในฟีลด์ F02 ของตารางนี้มีในฟีลด์ F02 ของ TABLE02 ด้วย

    คำสั่งที่ใช้คือ SELECT * FROM TABLE01 WHERE F02 IN (SELECT F02 FROM TABLE02);

    ผลลัพธ์ที่ได้คือ

    result1

    จะเห็นว่าได้ผลลัพธ์แบบเดิม ซึ่งถูกต้อง

     

    1. ต้องการข้อมูลใน TABLE01 ที่ข้อมูลในฟีลด์ F02 ของตารางนี้ไม่มีในฟีลด์ F02 ของ TABLE02 โดยปรับคำสั่งจาก IN เป็น NOT IN

    คำสั่งที่ใช้คือ SELECT * FROM TABLE01 WHERE F02 NOT IN (SELECT F02 FROM TABLE02);

    ผลลัพธ์ที่ได้คือ

    result3

    ซึ่งไม่มีผลลัพธ์ใด ๆ ออกมาเลย

    ลองเพิ่มเงื่อนไขใน subquery โดยเอารายการที่ F02 มีค่าเป็น null ออกไป

    คำสั่งที่ใช้คือ SELECT * FROM TABLE01 WHERE F02 NOT IN (SELECT F02 FROM TABLE02 WHERE F02 IS NOT NULL);

    ผลลัพธ์ที่ได้คือ

    result2

    ซึ่งเป็นไปตามที่เราต้องการ

     

    สรุปได้ว่า กรณีที่เราต้องการใช้งานคำสั่ง SELECT ที่ใช้ร่วมกับเงื่อนไข NOT IN ตามด้วย subquery  ต้องระมัดระวังในเรื่องของผลลัพธ์ที่ได้จาก subquery มีค่าข้อมูลที่เป็น null อยู่ด้วย ซึ่งจะทำให้ได้ผลลัพธ์ไม่ตรงตามที่เราต้องการ

     

    ข้อมูลอ้างอิง 

    https://docs.oracle.com/cd/B19306_01/server.102/b14200/conditions013.htm

    https://docs.oracle.com/cd/B19306_01/server.102/b14200/expressions014.htm#i1033664

     

     

     

     

     

     

  • การ Encrypt/Decrypt ข้อมูลในไฟล์ Web.config

    การเข้ารหัส (Encrypt) ไฟล์ Web.config ถือเป็นวิธีการหนึ่งในการช่วยเพิ่มความปลอดภัยและช่วยป้องกันการถูกโจมตีจากผู้บุกรุกในการเชื่อมต่อกับฐานข้อมูล เนื่องจากไฟล์ web.config เป็นที่รวมการ config ค่าต่างๆ ของ web application ของเราไว้ เช่น ข้อมูลรหัสผ่านสำหรับการเชื่อมต่อฐานข้อมูล (ConnectionString), AppSetting, คีย์ API หรือข้อมูลสำคัญอื่นๆ ที่เกี่ยวกับการตั้งค่าต่างๆ โดยบทความนี้นำเสนอการเข้ารหัสและการถอดรหัส (Encrypt/Decrypt) ไฟล์ Web.config ด้วยคำสั่งผ่าน Command line โดยใช้ tools ที่มากับ .NET Framework ดังนั้นเครื่องคอมพิวเตอร์ของเราจะต้องมีการติดตั้ง .NET Framework ไว้อยู่ก่อนแล้ว สำหรับข้อมูลในไฟล์ Web.config จะถูกแบ่งออกเป็น section หลายๆ section ด้วยกัน โดยผู้เขียนจะขอยกตัวอย่างการ Encrypt/Decrypt ข้อมูลในส่วนของ section<connectionString>ดังนี้

     

    ก่อนทำการ Encrypt Web.config

    ที่ section <connectionStrings> ซึ่งเก็บข้อมูล config ค่าต่างๆ ของ Database sever ไว้ เช่น user/password เป็นต้น

    <configuration>
    <connectionStrings>
    <add name="SqlServices" connectionString="Data Source=localhost;PASSWORD=1234;Integrated Security=SSPI;Initial Catalog=Northwind;" />
    </connectionStrings>
    </configuration>

     

    Encrypt Web.config

    1.เปิด Command Prompt ขึ้นมา (อย่าลืมเปิดแบบ Run as administrator ด้วยนะคะ)

    2.พิมพ์คำสั่ง ดังนี้(Version ของ.NET Framework ขึ้นอยู่กับที่ลงไว้ที่เครื่อง) :

    cd C:\Windows\Microsoft.NET\Framework\v4.0.30319

    step1

    3.เข้าไปที่ directory path ที่เก็บไฟล์ web.config แล้วพิมพ์คำสั่ง ดังนี้

    aspnet_regiis.exe -pef connectionStrings  C:\inetpub\wwwroot

    step2

    หมายเหตุ: “connectionString” เป็น case sensitive ตัวพิมพ์เล็ก พิมพ์ใหญ่ ต้องพิมพ์ให้ถูกต้อง

     

    หลังทำการ Encrypt Web.config

    หลังจากทำการ Encrypt แล้วจะเห็นว่า ที่ section connectionString จะเป็นรหัสที่ไม่สามารถอ่านเข้าใจได้

    <connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
       <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
         xmlns="http://www.w3.org/2001/04/xmlenc#">
         <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
         <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
           <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
             <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
             <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
               <KeyName>Rsa Key</KeyName>
             </KeyInfo>
             <CipherData>
               <CipherValue>SryXAF+wpnIpZ8P3HMP8ffMDBorz9j08/oX2vXDA+9LkMHY1i50qeCqYmOYnQXK4C6iNhyIZx9R+AcE7yY7AQeHzzPhZ/bZ04NPuOpd7wD+NL82CWeec/fToTIBbHvE6zNBgenUSE+8zTv9II357tsqpjH1xaII+zmZbgo5+fnhjAD8nVffqd+NQ0x+IXDwyBraeT50TlEXx4lJlAph7jqdglg1Xf/yjSTfwrOB2NcVIHVaVWN3CrelWgQKASftGXdDVingbRn2RXphyooTuVsZgJdzbFMpd7H6fJHggORSPwOud1ZU5vE4aNAMHDa4fb6FOA3I8R0urWD4sT34YRg==</CipherValue>
             </CipherData>
           </EncryptedKey>
         </KeyInfo>
         <CipherData>
           <CipherValue>iKttnP9CEMRfq+mhcHqN8f5GiwsySBLw0CWeiAxSQVIfoEQMNutubrRFruoNIb+m0XJnPL5FIypqqQ72dqZ4DSeQdUJwAVyO4HSlM5F3b+Jnogz9aMAuwdoww5QKdI94yH9fx8RwxhAzq1a9eApjLglAwTmKY84Wg/Lqpcn9wfvKyIZJyQaJCaLCEteGhB9bopjT9z+nmMZVQ2LrDEtKMEe1oltPoR8EvTel2/2jJ/gwvJK+vTw9sL2GMPJIoA/NZQWjR2CWaCGlEFIjmgqT8BjTwiSS6hEp90CEQVN14U9A71vXquH73X6ZAyIFScf2XJS1Y+iCaEFo6r8qIIiGdrAGUTgfa8r9EwHzb5emCzlQzEDWgzno5IUDxuMBNzJinFudgwPFkA/xhdAHcHnB+quFOGGKVZJw72o5Ix7IeWI1Frs2n8+JZMrpPNW5r4SHHLrMD/iPR5FSfDz/sQqR5f+3/N3i3aq0LX8NfthfYtS9N4oCdPBPbIReP1w05we1Q+PJMO4KVG4w3x8k9O7aT0zoi+5CuPgKR30IgbE0sL8fOeki5OKYfsscNMzV/6T23axh9Ky+axI9fAMarjjL5aeYfa8n6jeevpA4f2SmlkbvW6P72292Ihk3bD1HMghn7ibjZ2q6hLwTb9QWyQvSDjHPsqeoiuKBTNJZgsCqBA4QK8j6/9exufTsOe/SgTclHPfiXpSI7CgbaGd6JQ2P5QSDVwQNQPuaf4qFKsZdPWfdEGcCgxLVZtcU0Cd12AnpaWIpiVUjCz6pWl4YygEXsvntLQfLSQ7XX2Q8lA1DjqBOcDQXY9mMo6PvZi3IsqcdF/DEn931nwBPO09T2AqWJWuAaK/Vh4+olkVYFWuj/Eerp2UkG2ItJduUaRNWzXV9s1hQ/q/36S0RTxN0DgXPII6CowQWIV2d5ZYwSUKVgsiDM43GBPF4SFFJOUec37yzYv6XT3/BmDmNpq32a+VAUB/OP23k4mnTvm/Nay1Iy6E8sUOYSpCY1up6XAcFL1XsacpNKLpMTmH6LsROX9BhmdTNaWgCQaDNVNeAISoJ8HZ5lw/EX1f6Rtz4uWyvBfSOPPAWPkNQawexjVKl0FtR6fRSnAGMkgflURH5QNX5xm+y0sfZsNz9sAFZofoJLQ3rdV7ToFJE+JlEvPKRHFDVbxDCURQ5CynXFnqlLj+2LhBbyX0n6oHQwMgTTTIf/+PNcjOx3zBn6/V3T0PdD6fnjVtTDnbJzN7ct7SOuifW0OfCfyeKSs0IOzPm8BucZ4CODaTwjY1bz2kgGRTjuenCp1N1GIRhMJBIBJhWOs3nee6Y9DgtlpWc3SZRZeYkmOffT5xNwQeTLrIXvTZCByHZcN3+g8OG0HqlodUCbPgf8jK45dMeT8piyFwZem8Dotjz7mXOaJxF7C66SQKvFVfuJjXDmTbmdzqwt33z+w39TV8ueXGyB/5S1kpV/ul+c7LwTwked7bMJtFJpVwdFKFiUxMebDuu3vINlVSZJfo43SSmTD3rM2UXV8ol65KzlmacBhgCwXtLFg7/8uGQrrUVq9gqHsoPyEgt</CipherValue>
         </CipherData>
       </EncryptedData>
     </connectionStrings>
    

     

    Decrypt Web.config

    การถอดรหัส (Decrypt) ไฟล์ Web.config สามารถใช้คำสั่งเช่นเดียวกันกับการ Encrypt แต่เปลี่ยนจาก -pef เป็น -pdf ดังคำสั่งต่อไปนี้ :

    aspnet_regiis.exe -pdf connectionStrings  C:\inetpub\wwwroot

    หมายเหตุ: “connectionString” เป็น case sensitive ตัวพิมพ์เล็ก พิมพ์ใหญ่ ต้องพิมพ์ให้ถูกต้อง

    การ Decrypt จะทำได้เฉพาะที่เครื่องที่ทำการ encrypt เท่านั้น จะไม่สามารถ decrypt ไฟล์ web.config นี้จากเครื่องอื่นได้ หากเรานำไฟล์ไป Decrypt จากเครื่องอื่นก็จะพบ message Failed ดังรูป

    step3

    โดยการ Encrypt/Decrypt ด้วยการใช้คำสั่งผ่าน command line สามารถทำได้กับทุก section (ไม่เฉพาะ connectionString)ในไฟล์ Web.confog เพียงแต่เปลี่ยนคำสั่งในส่วนของ section ตามที่ต้องการ

     

    แหล่งข้อมูลอ้างอิง : https://msdn.microsoft.com/en-us/library/ff647398.aspx

  • การใช้ LINQ ในการจัดการข้อมูลอย่างง่าย สำหรับมือใหม่(Ep.1)

              ก่อนที่จะไปถึงในส่วนของวิธีการจัดการข้อมูลด้วย LINQ เรามาพูดถึงที่มาที่ไปอย่างคร่าวๆ ของ LINQ กันสักเล็กน้อยนะคะ LINQ มีชื่อเต็มว่า “Language-Intergrated Query” ถือเป็นภาษาใหม่ที่ขยายความสามารถในการเขียนโปรแกรมโดยเลียนแบบภาษา SQL จึงทำให้มีการใช้งาน keyword ที่คุ้นเคยกันดีในคำสั่ง SQL เช่น select from where เป็นต้น ซึ่งมีความนิยมกับการทำงานด้านฐานข้อมูลมากขึ้นในปัจจุบัน และได้ถูกนำมารวมกับภาษาพัฒนาโปรแกรมทำให้การพัฒนาโปรแกรมควบคุมข้อมูลให้อยู่ในแนวการเขียนโปรแกรมเดียวกัน และช่วยอำนวยความสะดวกรวมถึงเพิ่มความคล่องตัวให้กับผู้พัฒนาในการจัดการข้อมูลมากยิ่งขึ้น โดย LINQ นี้ถูกนำมาใช้ตั้งแต่ .NET Framework 3.5 ที่มากับ Visual studio 2008 ซึ่งจะมีการติดต่ออยู่ด้วยกัน 3 ประเภท คือ

    • ข้อมูลประเภท Object
    • ข้อมูลประเภทฐานข้อมูลเชิงสัมพันธ์ ซึ่งมีรูปแบบการใช้งานแตกต่างกันไป จึงแยกออกเป็น
      • LINQ to Dataset
      • LINQ to SQL
      • LINQ to Entity
    • ข้อมูล XML

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

    • การดึงข้อมูลแบบทั่วไปโดยใช้คำสั่ง Select

    ตัวอย่างที่ 1 : กรณีดึงข้อมูลจาก Object มาแสดง

    List<Customer> customers= GetCustomer ();
    var qCustomers = from cust in customers
    select cust;
    

    คำอธิบาย : จากตัวอย่างข้างต้น จะเห็นว่า มีการใช้คำสั่ง select ในการดึงข้อมูลจาก object list ของ class ที่มีชื่อว่า Customer ทั้งหมดมาใส่ในตัวแปร qCustomers โดยมีการประกาศเป็น var ไว้ เนื่องจาก LINQ เองจะสามารถแปลงค่าของข้อมูลตัวแปรที่รับมาโดยไม่ต้องมีการระบุชนิดของข้อมูล และ compiler จะทำการแปลงรูปแบบและชนิดของตัวแปรได้อัตโนมัติ โดยสามารถนำผลลัพธ์ที่ได้มาวนแสดง ดังนี้

    Console.WriteLine("Customer name list:");
    foreach (var custlist in qCustomers )
    {
    Console.WriteLine(custlist.name);
    }

    ตัวอย่างที่ 2 : กรณีจากตัวแปรอาร์เรย์

    int[] numbers = { 2,7,5,3,1,6 };
    var result = from n in numbers
    select n -1;
    Console.WriteLine("Result:");
    foreach (var i in result)
    {
    Console.WriteLine(i);
    }
    

    คำอธิบาย : จากตัวอย่างข้างต้น จะเห็นว่า มีการใช้คำสั่ง select ในการดึงข้อมูลจากตัวแปรอาร์เรย์ที่มีชื่อว่า numbers ทั้งหมดมาใส่ในตัวแปร result โดยเพิ่มเติมให้มีการนำค่าที่ดึงมาได้ -1 และนำมาวนแสดง

    ผลลัพธ์ที่ได้ : 1 6 4 2 0 5

    ตัวอย่างที่ 3 : การแปลงค่าข้อมูลจากตัวแปรอาร์เรย์ที่มีชนิดเป็น string ให้เป็นตัวอักษรพิมพ์เล็กและพิมพ์ใหญ่ ดังนี้

    string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" };
    var upperLowerWords =
    from w in words
    select new { Upper = w.ToUpper(), Lower = w.ToLower() };
    foreach (var ul in upperLowerWords)
    {
    Console.WriteLine("Uppercase: {0}, Lowercase: {1}", ul.Upper, ul.Lower);
    }
    

    คำอธิบาย : เป็นการนำข้อมูลจากตัวแปรอารย์เรย์ที่เป็นชนิด string มาแปลงเป็นตัวอักษรพิมพ์ใหญ่และกำหนดให้อยู่ในฟิลด์ใหม่ที่มีชื่อว่า Upper และแปลงอักษรที่อ่านได้เป็นตัวพิมพ์เล็กให้อยู่ในฟิลด์ใหม่ที่มีชื่อว่า Lower โดยเมื่อมีการเรียกใช้เพื่อดึงค่ามาแสดง เราสามารถเรียกใช้ผ่านทางชื่อที่เรากำหนดขึ้นใหม่ได้ เช่น ul.Upper นั่นเอง

    ผลลัพธ์ที่ได้ :
    Uppercase: APPLE, Lowercase: apple
    Uppercase: BLUEBERRY, Lowercase: blueberry
    Uppercase: CHERRY, Lowercase: cherry

    • การค้นหาข้อมูลโดยใช้ Where เพื่อเป็นการเพิ่มเงื่อนไขในการดึงข้อมูล ซึ่งมีทั้งวิธีที่ใช้ operator ที่มีชื่อว่า Where และแบบที่ใช้ เมธอด Where ซึ่งจะอธิบายและยกตัวอย่างดังนี้

    ตัวอย่างที่ 1 : เป็นการดึงข้อมูลสินค้าที่หมดในสต็อก โดยใช้ operator ที่มีชื่อว่า Where

    List<Product> products = GetProductList();
    var soldOutProducts = from p in products
     where p.UnitsInStock == 0
    select p;
    
    Console.WriteLine("Sold out products:");
    foreach (var product in soldOutProducts)
    {
    Console.WriteLine("{0} is sold out!", product.ProductName);
    }
    

    คำอธิบาย : เป็นการดึงข้อมูลจาก object list ของคลาส Product มาแสดง โดยใช้ operator ที่ชื่อว่า Where ซึ่งมีลักษณะการใช้งานคำสั่งคล้ายกับในการเขียนคำสั่ง SQL ซึ่งเงื่อนไขที่ใช้ในการดึงข้อมูลคือจะต้องมีจำนวนใน stock เป็น 0 และนำชื่อของสินค้าที่หมดในสต็อกมาวนแสดงโดยเรียกใช้ properties ที่ชื่อว่า ProductName

    ตัวอย่างที่ 2 : เป็นลักษณะการดึงข้อมูลแบบมีเงื่อนไขโดยใช้เมธอด Where

    string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
    var shortDigits = digits.Where((digit, index) => digit.Length < index);
    
    Console.WriteLine("Short digits:");
    foreach (var d in shortDigits)
    {
    Console.WriteLine("The word {0} is shorter than its value.", d);
    }
    

    คำอธิบาย : จากตัวอย่างข้างต้น เป็นการดึงข้อมูลตามเงื่อนไข ด้วยเมธอด Where ซึ่งเงื่อนไขข้างต้นจะดึงข้อมูลเฉพาะรายการที่มีความยาวของตัวอักษรน้อยกว่าค่า index ของข้อมูลอาร์เรย์

    • การเรียงลำดับข้อมูล

    ตัวอย่างที่ 1 : เป็นการเรียงลำดับข้อมูลโดยทั่วไป ด้วยคีย์เวิร์ด Order by

    var movies = from row in _db.Movies
    orderby row.Category, row.Name descending
    select row;
    

    คำอธิบาย : จากตัวอย่างข้างต้น เป็นการดึงข้อมูลโดยมีการเรียงลำดับข้อมูลตามฟิลด์ Category และ Name ที่มีการเรียงลำดับจากมากไปน้อย ตามลำดับ โดย keyword ที่ใช้ในการเรียงลำดับ คือ เรียงจากน้อยไปมากใช้ ascending แต่หากต้องการเรียงลำดับจากมากไปน้อยให้ใช้ descending แต่หากไม่ระบุลักษณะการเรียงลำดับไว้ จะมีค่าตั้งต้นเป็นการเรียงลำดับจากน้อยไปมาก(ascending) นั่นเอง

    ตัวอย่างที่ 2 : เป็นการเรียงลำดับของข้อมูลมากกว่า 1 คอลัมน์ ด้วยเมธอด OderBy และ ThenBy

    var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name)
    

    คำอธิบาย : จากตัวอย่างข้างต้น เป็นการดึงข้อมูลโดยมีการเรียงลำดับข้อมูลตามฟิลด์ Category และ Name ที่มีการเรียงลำดับจากมากไปน้อย ตามลำดับ โดยใช้เมธอด OrderBy ตามด้วย ThenBy

    • การจัดกลุ่มข้อมูล Group by

    ตัวอย่างที่ 1 :

    var orderGroups =
    from p in products
    group p by p.Category into g
    select new { Category = g.Key, Products = g };
    

    คำอธิบาย : จากตัวอย่างข้างต้น เป็นการจัดกลุ่มข้อมูลด้วยฟิลด์ Category โดยใช้คีย์เวิร์ด group by

    ตัวอย่างที่ 2 :

    var query = source.GroupBy(x => new { x.Column1, x.Column2 });
    

    คำอธิบาย : จากตัวอย่างข้างต้น เป็นการดึงข้อมูลแบบมีการจัดกลุ่มโดยใช้เมธอด GroupBy แบบมากกว่า 1 คอลัมน์

              สำหรับบทความนี้ ผู้เขียนจะขอพูดถึงการใช้งานพื้นฐานของการเขียน LINQ เบื้องต้นไว้เพียงเท่านี้ก่อน แต่ในความเป็นจริงแล้วประโยชน์และความสามารถของ LINQ นี้ยังมีอีกมากมาย ซึ่งผู้เขียนจะขอหยิบยกและพูดถึงเพิ่มเติมในหัวข้อที่คิดว่าผู้พัฒนาน่าจะนำไปใช้ประโยชน์ได้เพิ่มเติมในบทความถัดไป(Ep.2) เช่น การกำจัดข้อมูลที่ซ้ำกัน การคำนวณค่าผลรวม และการเชื่อมข้อมูล(join) ตาราง เป็นต้น หากบทความนี้มีเนื้อหาหรือส่วนใดของบทความผิดพลาด ผู้เขียนขออภัยไว้ ณ ที่นี้ด้วย ทั้งนี้ผู้อ่านสามารถเสนอแนะและแลกเปลี่ยนความคิดเห็นร่วมกันได้ ขอบคุณค่ะ
    ปล.อย่าลืมติดตามเนื้อหาต่อใน Ep.2 นะคะ ^^

    แหล่งข้อมูลอ้างอิง :
    https://code.msdn.microsoft.com/LINQ-Restriction-Operators-b15d29ca
    http://stackoverflow.com/questions/847066/group-by-multiple-columns

  • ทำอย่างไรให้สามารถกำหนดจุดพิกัดบนแผนที่ Google map แบบจุดเดียวและหลายจุดจากฐานข้อมูลได้ด้วย ASP.NET C# (ภาคต่อ)

                 จากบทความที่แล้ว ผู้เขียนได้เขียนไว้เกี่ยวกับเรื่องวิธีการกำหนดจุดพิกัดบนแผนที่กันไปบ้างแล้ว ในหัวข้อ “ทำอย่างไรให้สามารถกำหนดจุดพิกัดบนแผนที่ Google map แบบจุดเดียวและหลายจุดจากฐานข้อมูลได้ด้วย ASP.NET C#” สำหรับในบทความนี้ผู้เขียนจึงขอพูดถึงในส่วนของการดึงค่าละติจูด ลองจิจูดของสถานที่ ซึ่งนับว่าเป็นส่วนประกอบสำคัญในการแสดงผลพิกัดบนแผนที่ ซึ่งเดิมทีแล้วนั้น ผู้ใช้อาจต้องค้นหาข้อมูลพิกัดดังกล่าวจาก Google map เองและนำพิกัดดังกล่าวมากรอกลงฐานข้อมูลหรือมาระบุเพื่อการแสดงพิกัดนั้นๆในการเขียนโปรแกรม คงเป็นการดี หากการแสดงผลพิกัดจากฐานข้อมูลนั้น จะมีตัวช่วยอำนวยความสะดวกให้กับผู้ใช้ในการดึงค่าละติจูด และลองจิจูดโดยการกรอกข้อมูลชื่อสถานที่ลงไปเพื่อใช้ในการค้นหา ซึ่งน่าจะเป็นประโยชน์และทำให้ผู้ใช้สามารถใช้งานได้ง่ายขึ้นในการนำพิกัดเหล่านั้นไประบุบนแผนที่นั่นเอง

                 โดยการดึงค่าพิกัดละติจูด-ลองจิจูดของสถานที่ สามารถทำได้หลายวิธี ขึ้นอยู่กับการนำไปประยุกต์ใช้ ซึ่งในบทความนี้ผู้เขียนขอแนะนำ 2 วิธี ดังนี้

    • การเรียกใช้เซอร์วิสของ Google Geocoding API  โดยการส่งพารามิเตอร์เป็นที่อยู่ของสถานที่ดังกล่าว

    ฝั่ง C#

        private void getLatAndLong()
        {
            try
            {
             ////เป็นการกำหนด url ที่จะใช้ในการเรียกเซอร์วิสของ Google Geocoding API 
    โดยมีการส่งค่าพารามิเตอร์เป็นข้อมูลที่อยู่
                string url = "http://maps.google.com/maps/api/geocode/xml?address=" + txtLocation.Text + "&sensor=false";
                WebRequest request = WebRequest.Create(url);
                using (WebResponse response = (HttpWebResponse)request.GetResponse())
                {
            ////ผลลัพธ์จะอยู่ในรูปแบบของ XML หรือ JSON และจะถูกอ่านให้อยู่ในรูปแบบ Dataset 
    โดยใช้ StreamReader
                     using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                    {
                        DataSet dsResult = new DataSet();
                        dsResult.ReadXml(reader);
           ////จัดทำโครงสร้างตาราง(datatable) ที่จะใช้ในการแสดงผลใน Gridview
                        DataTable dtCoordinates = new DataTable();
                        dtCoordinates.Columns.AddRange(new DataColumn[4] { new DataColumn("Id", typeof(int)),
                            new DataColumn("Address", typeof(string)),
                            new DataColumn("Latitude",typeof(string)),
                            new DataColumn("Longitude",typeof(string)) });
    
           ////ดึงค่าผลลัพธ์จากตารางต่างๆ เพื่อนำค่าที่จำเป็นมาแสดงผลตามต้องการ
                        foreach (DataRow row in dsResult.Tables["result"].Rows)
                        {
                            string geometry_id = dsResult.Tables["geometry"].Select("result_id = " + row["result_id"].ToString())[0]["geometry_id"].ToString();
                            DataRow location = dsResult.Tables["location"].Select("geometry_id = " + geometry_id)[0];
                             dtCoordinates.Rows.Add(row["result_id"],  row["formatted_address"], location["lat"], location["lng"]);
                        }
           ////แสดงผลข้อมูลของค่าที่ดึงมาได้ใน Gridview (ถ้ามี)
                        if (dtCoordinates.Rows.Count > 0)
                        {
                            gvLatLong.DataSource = dtCoordinates;
                            gvLatLong.DataBind();
                        }
                        else
                        {
                            gvLatLong.DataSource = null;
                            gvLatLong.DataBind();
                            ScriptManager.RegisterStartupScript(Page, Page.GetType(), "alert", "alert('Can not find Latitude and Longitude!'); ", true);
    
                        }
                    }
                }
            }
            catch (Exception ex) {
                ScriptManager.RegisterStartupScript(Page, Page.GetType(), "alert", "alert('Can not find Latitude and Longitude!'); ", true);
                gvLatLong.DataSource = null;
                gvLatLong.DataBind();
            }
        
        }
     protected void btnSearch_Click(object sender, EventArgs e)
     {
        getLatAndLong();
     }
    

                 จากโค้ดข้างต้น เป็นการค้นหาละติจูด-ลองจิจูดจากที่อยู่ของสถานที่นั้นๆ โดยใช้ Google Maps Geocoding API ซึ่งเซอร์วิสของ Google Geocoding API มีการส่งค่าโดยใช้ WebRequest โดยจะรับค่าที่อยู่ของสถานที่เป็นพารามิเตอร์และส่งกลับเป็นพิกัดและข้อมูลอื่นๆมาในรูปแบบของ XML หรือ JSON นั่นเอง และค่าที่ส่งกลับมาในรูปแบบ XML นั้นจะถูกอ่านให้อยู่ในรูปแบบ Dataset โดยใช้ StreamReader ซึ่งค่าที่ส่งกลับมานั้นจะมีด้วยกันหลายตาราง และมีการดึงค่าข้อมูลที่เกี่ยวข้องที่จะนำมาใช้เพิ่มลงในตารางที่ถูกสร้างขึ้นใหม่ และนำไปแสดงในกริดวิวนั่นเอง

    ฝั่ง .aspx (Client side)

    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
     <title>GetLatitude-Longitude</title>
    <!-- การอ้างอิงเพื่อให้สามารถเรียกใช้งาน Autocomplete ได้-->
     <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>
    </head>
    <body>
     <form id="form1" runat="server">
     <div>
     <asp:TextBox ID="txtLocation" runat="server" Width="450px"></asp:TextBox><asp:Button ID="btnSearch" runat="server" Text="Search" OnClick="btnSearch_Click" />
     </div>
     <br />
     <div>
     <asp:GridView ID="gvLatLong" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None">
     <AlternatingRowStyle BackColor="White" />
     <EditRowStyle BackColor="#2461BF" />
     <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
     <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
     <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
     <RowStyle BackColor="#EFF3FB" />
     <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
     <SortedAscendingCellStyle BackColor="#F5F7FB" />
     <SortedAscendingHeaderStyle BackColor="#6D95E1" />
     <SortedDescendingCellStyle BackColor="#E9EBEF" />
     <SortedDescendingHeaderStyle BackColor="#4870BE" />
     </asp:GridView>
     </div>
     </form>
     <script type="text/javascript">
     google.maps.event.addDomListener(window, 'load', function () {
    ////การกำหนดคอนโทรล textbox ที่จะเพิ่มความสามารถ place auto-complete 
    เพื่อให้สามารถค้นหาสถานที่ตั้งได้ง่ายขึ้นเมื่อผู้ใช้พิมพ์ข้อมูลชื่อสถานที่ในกล่อง textbox
     var txtplaces = document.getElementById('<%= txtLocation.ClientID %>');
     var places = new google.maps.places.Autocomplete(document.getElementById('<%= txtLocation.ClientID %>'));
     });
     </script>
    </body>
    </html>
    

                 จากโค้ดข้างต้น นอกจากจะเป็นการเรียกใช้เซอร์วิสโดยการคลิกปุ่ม “btnSearch” และรับค่าข้อมูลที่อยู่จากกล่อง textbox แล้ว ยังมีการเพิ่มส่วนของการเพิ่มความสามารถในการค้นหาข้อมูล “ที่อยู่” โดยการกรอกข้อมูล “ชื่อสถานที่” โดยใช้ feature ที่มีของ Google Places API (หรืออาจเรียกว่า place Autocomplete ) เนื่องจากหากมีการเรียกใช้เซอร์วิสดังกล่าวโดยตรง ผู้ใช้จำเป็นที่จะต้องกรอกข้อมูล “ที่อยู่” เพื่อเป็นพารามิเตอร์ให้กับเซอร์วิสที่เรียกใช้ให้ส่งค่าผลลัพธ์กลับมา ซึ่งความน่าจะเป็นที่ผู้ใช้จะสามารถทราบข้อมูลชื่อสถานที่นั้นย่อมเป็นไปได้มากกว่าการทราบข้อมูลที่อยู่ของสถานที่นั้นๆ ผู้เขียนจึงนำมาประยุกต์ใช้งานเข้าด้วยกันกับการเรียกเซอร์วิสที่กล่าวไว้แล้วก่อนหน้านั่นเอง โดยจะแนะนำวิธีการใช้งานเพิ่มเติมในหัวข้อต่อจากนี้

     เพิ่มเติม :
      Place Autocomplete: เป็นfeature ที่มีของ Google Places API เปรียบเสมือนตัวช่วยคำค้นเมื่อมีการป้อนข้อมูลชื่อสถานที่ลงไป โดย feature ดังกล่าวจะแสดงข้อมูลสถานที่ที่ใกล้เคียงกับคำค้นขึ้นมาให้กับผู้ใช้ได้เลือกนั่นเอง

    โดยมีวิธีการเรียกใช้ place autocomplete  ดังนี้
    1. อ้างอิงการเพื่อเรียกใช้งาน Google Places API

    <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>

    2. กำหนดคอนโทรล textbox ที่ต้องการเพิ่มความสามารถ place autocomplete

     var input = document.getElementById('<%= txtSearch.ClientID %>');
     var div = document.getElementById('result');
     var autocomplete = new google.maps.places.Autocomplete(input);

    ตัวอย่างหน้าจอที่ได้เมื่อมีการพิมพ์คำค้น

    place_service1

    ผลลัพธ์

    place_service2

    เพิ่มเติม : Namespace ที่ต้องอ้างอิงเพิ่มเติมในการใช้งานโค้ดที่กล่าวไว้ข้างต้น มีดังนี้
                –  System.IO
                –  System.Net
                –  System.Data
                –  System.Text
    • แบบใช้ place Autocomplete ซึ่งเป็น feature ของ Google Places API ที่จะช่วยในการค้นหาที่อยู่จากชื่อสถานที่ได้และประยุกต์เพิ่มเติมเพื่อดึงค่ามาแสดงเมื่อมีการเลือกรายการสถานที่นั้นๆ
    <!DOCTYPE html>
    <html>
     <head>
    <!-- การอ้างอิงเพื่อให้สามารถเรียกใช้งาน Autocomplete ได้-->
     <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>
     <title>Place Autocomplete</title>
     <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
     <meta charset="utf-8">
     </head>
     <body>
     <form runat="server">
     <asp:TextBox ID="txtSearch" runat="server" placeholder="Enter a location" Width="350px"></asp:TextBox> 
     <div id="result"></div>
     
     <script>
     function initialize() {
    ////การกำหนดคอนโทรล textbox ที่จะเพิ่มความสามารถ place auto-complete 
    เพื่อให้สามารถค้นหาสถานที่ตั้งได้ง่ายขึ้นเมื่อผู้ใช้พิมพ์ข้อมูลชื่อสถานที่ในกล่อง textbox
     var input = document.getElementById('<%= txtSearch.ClientID %>');
     var div = document.getElementById('result');
     var autocomplete = new google.maps.places.Autocomplete(input);
     google.maps.event.addListener(autocomplete, 'place_changed', function () {
    
    ////ดึงค่าที่ได้เมื่อมีการเลือกรายการจากสถานที่ที่อยู่ที่ขึ้นมา และนำไปใช้ในการแสดงผล
     var place = autocomplete.getPlace();
     if (!place.geometry) {
     return;
     }
     else {
    ////นำค่าที่ดึงได้มาแสดงผลในพื้นที่ที่ต้องการ (result)
     div.innerHTML = '<br><div><strong>' + place.name + '</strong><br>' +
     '<strong>Address :</strong> ' + place.formatted_address + '<br><strong>Latitude :</strong> ' + place.geometry.location.lat() + ' <strong>Longitude:</strong>' + place.geometry.location.lng() + '</div>';
     }
     });
     }
     ////สั่งรันฟังก์ชั่น initialize() ตอนมีการโหลดหน้าจอ
     google.maps.event.addDomListener(window, 'load', initialize); 
     </script>
     </form>
     </body>
    </html>
    

                 จากโค้ดข้างต้น เป็นการดึงค่าละติจูดและลองจิจูดโดยใช้ feature ที่มีของ Google Places API (place Autocomplete) เพื่ออำนวยความสะดวกให้กับผู้ใช้ในการพิมพ์ชื่อสถานที่ลงใน textbox โดย feature นี้จะมีความสามารถในการดึงข้อมูลสถานที่และชื่อที่ใกล้เคียงขึ้นมาแสดงให้ผู้ใช้เลือก(ตามที่ได้กล่าวมาแล้วข้างต้น) และได้มีการสร้าง event เพิ่มเติมเมื่อผู้ใช้เลือกรายการใดขึ้นมา ก็จะมีการเรียกใช้เมธอด getPlace() เพื่อดึงค่าข้อมูลต่างๆมาใช้งานต่อไปรวมถึงค่าละติจูดและลองจิจูดที่เราต้องการใช้ในการกำหนดพิกัดด้วย

    ตัวอย่างหน้าจอการค้นหาข้อมูลที่อยู่ เมื่อมีการพิมพ์คำค้น

    place_auto1

    ผลลัพธ์ที่ได้จากการค้นหา

    place_auto2

                 วิธีการที่ได้กล่าวไว้ในบทความนี้เป็นเพียงแนวทางให้กับผู้พัฒนาสามารถนำไปประยุกต์ใช้ตามความต้องการเท่านั้น ซึ่งผลลัพธ์ที่ได้อาจมีความคลาดเคลื่อนเกี่ยวกับค่าของละติจูด และลองจิจูดเล็กน้อยในบางสถานที่ แต่ยังคงถือว่าอยู่ในเกณฑ์ที่รับได้ เมื่อนำไปลงพิกัดจุดไม่คลาดเคลื่อนหรือแตกต่างกันมากนัก อีกทั้งยังช่วยอำนวยความสะดวกให้กับผู้ใช้ได้ระดับหนึ่งที่จะไม่ต้องไปค้นหาพิกัดจากแหล่งข้อมูลอื่นอีก และผู้เขียนหวังเป็นอย่างยิ่งว่า บทความนี้จะเป็นประโยชน์และช่วยอำนวยความสะดวกให้กับการพัฒนาโปรแกรมที่เกี่ยวกับการกำหนดพิกัดจุดบนแผนที่โดยมีการดึงมาจากฐานข้อมูล หรือการพัฒนาโปรแกรมอื่นๆ ที่จำเป็นต้องมีค่าข้อมูลละติจูดลองจิจูดร่วมด้วยไม่มากก็น้อย หากมีส่วนใดของเนื้อหาบทความที่มีความผิดพลาด ผู้เขียนขออภัยไว้ ณ ที่นี้ และหากท่านใดมีข้อมูลเพิ่มเติม หรือพบวิธีการอื่นๆที่สามารถดึงค่าละติจูด-ลองจิจูดนอกเหนือจากวิธีที่กล่าวมาข้างต้นสามารถชี้แนะและแลกเปลี่ยนประสบการณ์ร่วมกันได้นะคะ ขอบคุณค่ะ ^^

    แหล่งข้อมูลอ้างอิง :
    http://www.aspsnippets.com/Articles/Find-Co-ordinates-Latitude-and-Longitude-of-an-Address-Location-using-Google-Geocoding-API-in-ASPNet-using-C-and-VBNet.aspx
    https://developers.google.com/places/javascript/