ตัวอย่าง SQL นับจำนวนผู้ลงทะเบียนประจำเดือน โดยแยกตามวันประจำสัปดาห์ GROUP BY SUM WEEKDAY

โดย SONGCHAI SAETERN
ตัวอย่าง SQL นับจำนวนผู้ลงทะเบียนประจำเดือน โดยแยกตามวันประจำสัปดาห์ (จันทร์ - อาทิตย์)

-- WEEKDAY(date)
-- 0 = Monday, 1 = Tuesday, 2 = Wednesday, 3 = Thursday, 4 = Friday, 5 = Saturday, 6 = Sunday
SELECT
begin_date
, SUM(WEEKDAY(begin_date) = 0) AS 'วันจันทร์'
, SUM(WEEKDAY(begin_date) = 1) AS 'วันอังคาร'
, SUM(WEEKDAY(begin_date) = 2) AS 'วันพุธ'
, SUM(WEEKDAY(begin_date) = 3) AS 'วันพฤหัสฯ'
, SUM(WEEKDAY(begin_date) = 4) AS 'วันศุกร์'
, SUM(WEEKDAY(begin_date) = 5) AS 'วันเสาร์'
, SUM(WEEKDAY(begin_date) = 6) AS 'วันอาทิตย์'
FROM
tb_register
WHERE
begin_date BETWEEN '2020-02-01' AND '2020-02-29'
GROUP BY
WEEKDAY(begin_date)


1. เริ่มจาก แสดงข้อมูลผู้ใช้งานโดย WHERE ค้นหาเฉพาะช่วงวันที่ของเดือนที่ต้องการ



2. แสดงตัวเลขหลัก วันประจำสัปดาห์ ด้วยฟังก์ชั่น WEEKDAY()



3. มาจัดกลุ่มด้วย GROUP BY ด้วยฟังก์ชั่น WEEKDAY() ก็จะเหลือแค่ตัวเลข 3 ค่า ก็คือ วันพฤหัสฯ วันศุกร์ และวันเสาร์




4. มาลองนับจำนวนของแต่ละวัน ด้วยฟังก์ชั่น COUNT(*) จะเห็นยอดจำนวนผู้ลงทะเบียนในแต่ละวัน



5. ถ้าหากว่าจะนำไปใช้เลย ก็จะต้องเขียน PHP เพิ่มเติมเพื่อตรวจสอบเงื่อนไข แต่ถ้าต้องการให้จบที่ SQL ก็เพียงแค่ใช้ SUM() และภายในก็ตรวจสอบเงื่อนไขว่า WEEKDAY() ตรงกับ ตัวเลขวันประจำสัปดาห์ที่ต้องการหรือไม่ ก็จะได้คอลัมน์แยกกัน 7 คอลัมน์ตามวันทร์ที่ต้องการ






PHP CI MANIA - PHP Code Generator 

โปรแกรมช่วยสร้างโค้ด "ลดเวลาการเขียนโปรแกรม"
 http://fastcoding.phpcodemania.com/index.php/#purchase-order
ราคาสุดคุ้ม  1,500  บาท
http://fastcoding.phpcodemania.com

SELECT ซ้อน SELECT (GROUP BY, HAVING, SUBQuery, COUNT)

โดย SONGCHAI SAETERN
เมื่อต้องการนับจำนวนรายการตามเงื่อนไข แต่ก็จำเป็นต้องใช้ GROUP BY เพราะการใช้ COUNT + DISTINCT ไม่สามารถดึงข้อมูลตามเงื่อนไขได้



 อยากจะนับจำนวนสมาชิกที่ชำระเงิน

ถ้าใช้ COUNT() ก็จะออกมาหมด ทำให้ได้จำนวนเกินจากความเป็นจริง เพราะสมาชิกบางคนชำระมากกว่า 1 ครั้ง





ก็เลยต้องเอามา GROUP กันด้วย รหัสสมาชิกก่อน เพื่อตัดเรคอร์ดที่ซ้ำกันออกไป สมาชิกจะชำระกี่ครั้ง ก็นับเป็น 1 คน (เพราะเป็นสมาชิกคนเดียวกัน)


จะเห็นว่า จะได้ยอดที่ถูกต้อง 139


แต่ข้อมูลมันแจกแจงรายการต้อง 139 แถว ซึ่งเราไม่ได้อยากได้รายละเอียด แค่อยากนับจำนวนเท่านั้นจะทำไงล่ะทีนี้



ก็จัดการ SELECT ครอบ SELECT ไว้เลย ซึ่งก็คือการทำ SubQuery นั่นเอง

ก็จะออกมาเรคอร์ดเดียว เป็นจำนวนที่ต้องการนำไปใช้งานได้ทันที



จริงๆแล้วเราใช้ DISTINCT ได้นะ ถ้าเงื่อนไขมีแค่เท่านี้ ไม่จำเป็นต้อง SubQuery

ดูสิ พอนับแบบตัดรายการซ้ำกันออกก็ได้
139 พอดิบพอดีเลย

ใช่ครับ ถ้าเงื่อนไขมีเพียงเท่านี้ก็ใช้ได้เลย
แต่ถ้ามีเงื่อนไขอื่นล่ะ???




ถ้าจะนับเฉพาะสมาชิกที่ชำระเงินตั้งแต่ 1500 บาทขึ้นไปเท่านั้นล่ะ?

ค่าที่ได้มาก็มีโอกาสผิดเพี้ยนนะ เพราะบางคนก็ชำระหลายครั้ง แต่พอรวมกันก็ถึง 1500 บาทเหมือนกันนะ

ตัวอย่างนี้ได้แค่ 61 แต่จริงๆแล้ว มี 64 คนเลยนะ!





กลับไปที่การ GROUP BY กันต่อ

เราสามารถใช้เงื่อนไข SUM ใน HAVING ได้นะ คือหลังจากเอามา GROUP กันแล้ว บางคนที่ชำระ 3 ครั้ง ก็จะ SUM รวมเป็นยอดเดียว ก็มีโอกาสถึง 1500 บาท อีกตั้ง 3 คนแน่ะ >O<

ผลลัพธ์ที่ได้คือ 64 คน คือคำตอบที่ต้องการ
แต่ทีนี้ผลลัพธ์ที่ได้มันแจกแจงรายการไง กลายเป็น 64 เรคอร์ด ถ้าอยากได้แค่จำนวนก็ต้อง SubQuery แบบเดิมอีกครั้ง







พอเขียน SELECT ครอบเข้าไป

เราก็จะได้ผลลัพธ์ 64 ออกมาเรคอร์ดเดียวไว้ใช้งานแล้วล่ะ ^O^



โค้ด SQL

SELECT

    COUNT(*) AS total
FROM (
    SELECT
        COUNT(*)
    FROM
        tb_payment
    GROUP BY
        member_id
    HAVING
        SUM(amount_pay) > 1500
) AS tb_count





PHP CI MANIA - PHP Code Generator 

โปรแกรมช่วยสร้างโค้ด "ลดเวลาการเขียนโปรแกรม"
http://fastcoding.phpcodemania.com/index.php/#purchase-order
ราคาสุดคุ้ม
http://fastcoding.phpcodemania.com

SHOW FULL COLUMNS สำหรับ MS SQL SERVER (Primary Key, Comment, Auto Increments)

โดย SONGCHAI SAETERN
คำสั่งสำหรับแสดงโครงสร้างของตาราง โดยจะแสดง Primary Key, Comment, Auto Increments พร้อมกับชื่อฟิลด์ ประเภทข้อมูล และความยาวของข้อมูลมาด้วย

คำสั่ง MySQL


SHOW FULL COLUMNS FROM tb_portfolio



คำสั่ง MS SQL SERVER


SELECT
COL.name AS Field,
TYP.name AS Type,
TYP.max_length AS MaxLength,
CASE
WHEN ISNULL(i.is_primary_key, 0) = 1
THEN 'PRI'
ELSE ''
    END AS 'PrimaryKey',
CASE
WHEN COL.is_identity = 1
THEN 'auto_increment'
ELSE ''
    END AS Extra,
sep.value AS Comment,
COL.collation_name AS Collation
From sys.columns COL
INNER JOIN sys.tables TAB
On COL.object_id = TAB.object_id
INNER JOIN sys.types TYP
ON TYP.user_type_id = COL.user_type_id
LEFT OUTER JOIN
    sys.index_columns ic ON ic.object_id = COL.object_id AND ic.column_id = COL.column_id
LEFT OUTER JOIN
    sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
left join sys.extended_properties sep on TAB.object_id = sep.major_id
                                         and COL.column_id = sep.minor_id
                                         and sep.name = 'MS_Description' 
WHERE
TAB.name = 'tb_portfolio'




อ้างอิง

Use a Query to access column description in SQL
https://stackoverflow.com/questions/15161505/use-a-query-to-access-column-description-in-sql/23143280

SQL - Check if a column auto increments
https://stackoverflow.com/questions/13772019/sql-check-if-a-column-auto-increments

SQL server query to get the list of columns in a table along with Data types, NOT NULL, and PRIMARY KEY constraints
https://stackoverflow.com/questions/2418527/sql-server-query-to-get-the-list-of-columns-in-a-table-along-with-data-types-no/2418665


สนับสนุนผลงานของผู้เขียนได้ที่

PHP CI MANIA - PHP Code Generator 

โปรแกรมช่วยสร้างโค้ด "ลดเวลาการเขียนโปรแกรม"
 http://fastcoding.phpcodemania.com/index.php/#purchase-order
ราคาสุดคุ้ม  1,500  บาท
http://fastcoding.phpcodemania.com

MySQL กับการ JOIN กลุ่มข้อมูลแบบเลือกรายการเดียว ที่ช่วงเวลาเปิดใช้งานสัมพันธ์กัน

โดย SONGCHAI SAETERN
จากบทความ Retrieving the last record in each group จะเป็นการ JOIN แบบเลือกรายการล่าสุด เช่นกรณีที่ข้อมูลในตารางหลัก มีการเปลี่ยนแปลงรุ่น เปลี่ยนเวอร์ชั่น แต่ยังคงใช้รหัสเดิม และต้องการแค่ข้อมูลเวอร์ชั่นล่าสุดเรคอร์ดเดียวเท่านั้น


นอกจากนี้ยังมีอีกกรณี นั่นก็คือ เมื่อเราย้อนกลับไปดูข้อมูลที่เคยบันทึกเอาไว้ โดยอ้างอิงรหัสเดียวกัน ที่ปัจจุบันได้เปลี่ยนเวอร์ชั่นไปแล้ว ข้อมูลที่ JOIN มาจะแสดงอย่างไร??? จะรู้ได้อย่างไรว่า  ณ วันที่นั้นๆ ใช้ข้อมูลเรคอร์ดไหน???

(ถ้าเราเก็บไอดี PK ไปเลยจะไม่เจอปัญหานี้ แต่เนื่องจากฟิลด์ Code ดันเป็น Unique ID ซึ่งยึดเป็น PK ไปในตัวก็เลยคิดว่าไม่จำเป็นต้องสร้าง ID ขึ้นมาอีก)



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

กรรไกรสองรายการ ที่ใช้หมวดหมู่เดียวกัน แต่คนละปี

กรรไกรตัดกิ่ง   ณ วันที่ 2017-06-01 ได้ทำรายการด้วยชื่อ  "วัสดุทำสวน"
กรรไกรเล็ก       ณ วันที่ 2018-01-31 ได้ทำรายการด้วยชื่อ  "วัสดุอุปกรณ์ทำสวน"

SELECT * FROM product
WHERE name LIKE '%กรรไกร%'

เรามาดูข้อมูลเมื่อ JOIN กับตารางหมวดหมู่กันดู


SELECT product.*,tb_category.name AS cate_name  FROM product
LEFT JOIN tb_category ON product.category_code = tb_category.code
WHERE product.name LIKE '%กรรไกร%'

จะเห็นว่าได้ข้อมูลมาหลายเรคอร์ด และเรคอร์ดที่ตรงกับข้อมูลที่เคยทำรายการจริงก็คือแถวที่ 3 และ แถวที่ 6 ที่ลูกศรสีแดงชี้


เราจะทำการ GROUP รหัสสินค้า เพื่อลดจำนวนเรคอร์ดที่ซ้ำซ้อนออกไป


SELECT
product.*
, tb_category.name AS cate_name
, tb_category.start_used_date
FROM
product
LEFT JOIN
tb_category ON product.category_code = tb_category.code
WHERE
product.name LIKE '%กรรไกร%'
GROUP BY
product.id

ผลปรากฏว่า ชื่อหมวดหมู่ที่แสดงไม่ถูกต้อง เมื่อสังเกตวันที่ start_used_date จะเห็นว่า ได้ดึงแค่เรคอร์ดแรกมาเท่านั้น ไม่ได้ดึงตามช่วงเวลาของข้อมูลจริง


วิธีแก้ไขก็คือ เพิ่มเงื่อนไขเข้าไปในการ JOIN 

โดยกำหนด Sub Query ให้เลือกเฉพาะรายการที่เปิดใช้งานก่อนนำไปใช้งานในครั้งนั้นๆ



SELECT
product.*
, cate.name AS cate_name
, cate.start_used_date
FROM
product
LEFT JOIN
tb_category cate ON cate.id = (
SELECT MAX(bb.id)
         FROM tb_category AS bb
         WHERE bb.code = product.category_code
         AND bb.start_used_date <= product.active_date
       )
WHERE
product.name LIKE '%กรรไกร%'
GROUP BY product.id


เมื่อเราเพิ่มเงื่อนไข start_used_date <= product.active_date เข้าไปใน Sub Query ก็จะได้ ID ของเรคอร์ดที่เปิดใช้งานในช่วง active_date ของรายการนั้นๆ ซึ่งตรงกับที่เกิดขึ้นจริง


หากเห็นว่าเป็นประโยชน์ หรือมีกรณีใกล้เคียงกัน ก็ลองเอาไปปรับใช้กันดูนะครับ


สำหรับแหล่งอ้างอิง  Retrieving the last record in each group  


PHP CI MANIA PHP Code Generator 
โปรแกรมช่วยสร้างโค้ด ลดเวลาการเขียนโปรแกรม เขียนโปรแกรมง่ายและสะดวกขึ้น
สนใจสั่งซื้อราคาสุดคุ้ม >> http://fastcoding.phpcodemania.com/

ภาษา SQL คืออะไร?

โดย SONGCHAI SAETERN

       SQL มาจากคำว่า Structured Query Language เป็นภาษามาตรฐานในการเข้าถึง ฐานข้อมูล เราสามารถใช้งานภาษา SQL ได้จากโปรแกรมต่างๆ ที่ต้องทำการกับระบบฐานข้อมูล


SQL เหมาะกับระบบฐานข้อมูลเชิงสัมพันธ์ ซึ่งแบ่งการทำงานได้เป็น 4 ประเภท

  1.  Insert query ใช้สำหรับการเพิ่มข้อมูล 
  2. Select query ใช้สำหรับดึงข้อมูลที่ต้องการ 
  3. Update query ใช้สำหรับแก้ไขข้อมูล 
  4. Delete query ใช้สำหรับลบข้อมูลออกไป



สำหรับการเขียนโปรแกรม PHP ร่วมกับฐานข้อมูล MySQL นั้น เราจะมีฟังก์ชั่นฐานข้อมูลเพื่อใช้งานร่วมกับภาษา SQL อยู่ 3 กลุ่มด้วยกัน

  1. MySQL Function เลิกใช้งานไปแล้วในเวอร์ชั่นตั้งแต่ 5.5 ขึ้นไป
  2. MySQLi Function นำมาใช้แทนฟังก์ชั่นเดิมที่เก่าไปแล้ว
  3. PDO สำหรับในส่วนนี้จะเป็นแบบ OOP และไม่ได้จำกัดเฉพาะ MySQL ซึ่งเราสามารถใช้ PDO ร่วมกับฐานข้อมูลอ่านๆที่ PHP รองรับได้อีกด้วย เรียกว่าเขียนครั้งเดียวคุ้ม

สำหรับเนื้อหาเกี่ยวกับ SQL สามารถหาอ่านได้เพิ่มเติมด้วยการค้นหาแบบเฉพาะเจาะจงได้ดังนี้


PHP + ชื่อฐานข้อมูล + คำสั่ง SQL ที่ต้องการ


เช่น การหาผลรวม ค้นหาได้ด้วยคำว่า

"PHP MySQL หาผลรวม"



PHP CI MANIA PHP Code Generator 
โปรแกรมช่วยสร้างโค้ด ลดเวลาการเขียนโปรแกรม เขียนโปรแกรมง่ายและสะดวกขึ้น
สนใจสั่งซื้อราคาสุดคุ้ม >> http://fastcoding.phpcodemania.com/



เนื้อหาอื่นๆที่เกี่ยวข้องกับ "SQL คืออะไร"