30 พฤศจิกายน 2562

Windows : Install new Font

การติดตั้งฟอนต์ทำได้หลายวิธี แต่ในบทความนี้จะนำเสนอการเพิ่มฟอนต์ด้วยคำสั่ง โดยการเขียนเก็บไว้ในไฟล์ .cmd โดยเขียนคำสั่งดังนี้

1. ใช้ Text Editor สร้างไฟล์ชื่อ AddFont.cmd
2.  ป้อนคำสั่งดังนี้

AddFont.cmd

16 พฤศจิกายน 2562

Visual FoxPro : แจกหนังสือ Visual FoxPro

นังสือ Visual FoxPro เล่มนี้ผู้เขียนได้เขียนไว้ตั้งแต่ปี 2543 จัดพิมพ์และจำหน่ายจนหมดแล้ว ถึงแม้ว่าโปรแกรมจะเก่าแต่ขอบอกว่าทำงานได้จนถึงทุกวันนี้ และก็ยังสามารถทำงานได้กับ Windows 10 ไปอีกยาวนาน เขียนโปรแกรมเก็บข้อมูล dbf file หรือจะติดต่อกับฐานข้อมูล SQL Server, MySQL, ORACLE และอื่น ๆ ได้อย่างง่ายดาย (หนังสือเล่มนี้ไม่ได้กล่าวถึงฐานข้อมูลที่เป็น Database Server)
หวังว่าผู้ที่เข้ามาศึกษาคงได้รับประโยชน์ และช่วยกันสร้างสรรค์โปรแกรมให้คนใช้งานกันเยอะ ๆ

ดาวน์โหลดหนังสือ คลิกที่นี่

https://1drv.ms/b/s!AnOzBJhrun8trHPeeMeHwxxZz1n9?e=bThXX5

ท่านสามารถบริจาคสมทบทุนช่วยเหลือใครที่ไหนก็ได้ตามกำลังศรัทธา ได้บุญร่วมกัน



05 พฤศจิกายน 2562

SQL Server - Edit Top 200 Rows - How to Edit Data in Result Grid

ารแก้ไขข้อมูลภายใต้ SQL Server Management Studio ด้วย Edit Top 200 Rows เราก็จะเห็นข้อมูลแสดงออกมาในกริด และแก้ไขได้เฉพาะ 200 แถวแรกเท่านั้น แต่ถ้าต้องการเปลี่ยนเงื่อนไขในการแสดงผลข้อมูลตามที่ต้องการ ก็สามารถทำได้ไม่ยาก ดังนี้

ให้ทำการเลือกตารางที่ต้องการแก้ไข โดยคลิกเมาส์ปุ่มขวาที่ตาราง จากนั้นเลือก Edit Top 200 Rows


สำรวจว่ามี Toolbars ชื่อ Query Designer แสดงอยู่บนหน้าจอหรือไม่
โดยไปคลิกเลือกเมนู View -> Toolbars แล้วดูว่าหน้ารายการ Query Designer มีเครื่องหมายถูกอยู่ด้านหน้าหรือไม่ (ถ้าไม่มีให้คลิกเลือก)

จะปรากฎ Toolbars ของ Query Designer

วิธีที่ 1 สำหรับผู้ที่ชอบใช้คำสั่ง SQL
ให้คลิกเลือกปุ่ม SQL (Show SQL Pane) จากนั้นทำการเพิ่มเงื่อนไขในการแสดงข้อมูลที่ต้องการแก้ไข เช่น  WHERE  Title = 'Ms.' แล้วกดปุ่ม Ctrl  R เพื่อแสดงผลข้อมูล แล้วทำการเข้าไปแก้ข้อมูลในกริดตามต้องการ

วิธีที่ 2
ให้คลิกเลือกปุ่ม Show Criteria Pane จะปรากฎหน้าต่างให้เราสามารถกำหนดเงื่อนไขแบบไม่ต้องเขียนคำสั่ง ดังภาพ

สวัสดีวันหยุดพิเศษ ประชุมอาเซียนซัมมิท

11 พฤษภาคม 2562

SQL Server - CTE Recursive Query

ตั้งแต่ SQL Server 2005 เป็นต้นมา ได้มีการนำ CTE (Common Table Expression's) เข้ามาสริม ซึ่ง CTE ถ้าจะเปรียบก็เหมือนกับตารางชั่วคราวตัวนึง  ซึ่งสามารถนำไปทำอะไรได้เยอะ  สิ่งนึงที่เป็นความสามารถของ CTE คือการทำรีเคอซีฟ (recursive) ตัวมันเองเรียกตัวมันเอง ดังนั้นตารางใดที่มีการต้องคิวรีเพื่อหาข้อมูลตัวมันเองซ้ำไปซ้ำมานั้นแหละ CTE เหมาะสุด
หลายท่านคงงง แล้วจะใช้งานได้อย่างไร ?
เพื่อให้เห็นภาพ ขอยกตัวอย่างข้อมูลสายการบังคับบัญชาของหน่วยงาน โดยการสร้างตารางเก็บข้อมูลตำแหน่งงาน ด้วยคำสั่ง ดังนี้

CREATE TABLE [dbo].[tblPosition](
[PositionID] [nchar](10) NOT NULL PRIMARY KEY,
[Description] [nvarchar](100) NULL,
[DependOn] [nchar](10) NULL
);
GO

ทำการเพิ่มข้อมูลตำแหน่งงาน และสายการบังคับบัญชา ดังนี้

INSERT INTO tblPosition VALUES('00000', 'President', NULL);
INSERT INTO tblPosition VALUES('10000', 'Managing Director', '00000');
INSERT INTO tblPosition VALUES('11000', 'Account Manager', '10000');
INSERT INTO tblPosition VALUES('12000', 'Marketing Manager', '10000');
INSERT INTO tblPosition VALUES('13000', 'IT Manager', '10000');
INSERT INTO tblPosition VALUES('11001', 'Accountant', '11000');
INSERT INTO tblPosition VALUES('12001','Marketing Representative','12000');
INSERT INTO tblPosition VALUES('13001', 'System Analyst', '13000');
INSERT INTO tblPosition VALUES('13002', 'Senior Programmer', '13000');
INSERT INTO tblPosition VALUES('13003', 'Programmer', '13000');

เมื่อเราเรียกแสดงข้อมูลด้วยคำสั่ง SELECT * FROM tblPosition จะได้ข้อมูลดังภาพ

ถ้าหากเราต้องการแสดงโครงสร้างตามลำดับขั้นของตำแหน่งต่าง ๆ ว่าตำแหน่งใดอยู่ภายใต้ตำแหน่งใด เราสามารถเขียนคำสั่งคิวรีโดยอาศัย CTE ได้ดังนี้

WITH CTE (DependOn, PositionID, [Description], LevelNumber)
  AS (
SELECT DependOn, PositionID, [Description], 1 LevelNumber
   FROM tblPosition  WHERE DependOn IS NULL
UNION ALL
SELECT E.DependOn, E.PositionID, E.[Description], LevelNumber + 1 AS LevelNumber
   FROM tblPosition E
INNER JOIN CTE ON E.DependOn=CTE.PositionID
        )
SELECT * FROM CTE ORDER BY  PositionID, LevelNumber;

เมื่อเรียกคำสั่งข้างต้น จะปรากฎข้อมูลดังภาพ

จากภาพจะเห็นว่ามีคอลัมน์ LevelNumber ปรากฎขึ้นมา ซึ่งจะแสดงถึงระดับของตำแหน่ง เช่น President อยู่ Level ที่ 1 ส่วน Programmer อยู่ Level ที่ 4 เป็นต้น

หมายเหตุ 
WITH CTE (DependOn, PositionID, [Description], LevelNumber)
เป็นการประกาศตารางชั่วคราวชื่อ CTE และภายใต้ตารางนี้มีคอลัมน์ต่าง ๆ ประกอบด้วย DependOn, PositionID, [Description], LevelNumber

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

WITH CTE(PositionID, [Narration], [Description], LevelNumber ,DependOn)
  AS (
SELECT PositionID, CONVERT(nvarchar(max) ,[Description]) AS [Narration], [Description],
   1 AS LevelNumber, DependOn
   FROM tblPosition  WHERE DependOn IS NULL
UNION ALL
SELECT E.PositionID, CONVERT(nvarchar(max) ,REPLICATE(SPACE(2), LevelNumber) + E.[Description]) AS [Narration],
   E.[Description], LevelNumber + 1 AS LevelNumber, E.DependOn
   FROM tblPosition E
INNER JOIN CTE ON E.DependOn=CTE.PositionID
)
SELECT * FROM CTE ORDER BY  PositionID, LevelNumber;

เมื่อเรียกคำสั่งข้างต้นจะได้ผลลัพธ์ดังภาพ

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

แต่จากทั้ง 2 ตัวอย่างข้างต้น ยังมีข้อผิดพลาดอยู่ ซึ่งผู้เขียนตั้งใจให้มันเกิด
ติ๊ก ต๊อก ติ๊ก ต๊อก หาดูว่าผิดตรงไหน

บอกเลยดีกว่า ผิดตรงการเรียงลำดับ ถ้ากรณีเราตั้งรหัส PositionID สะเปะสะปะ การเรียงลำดับจะทำให้มั่ว ดังนั้น หากต้องการจัดลำดับที่ถูกต้อง จำเป็นต้องเพิ่มคอลัมน์ภายใต้ CTE ขึ้นมาอีก 1 คอลัมน์ แล้วเขียนคำสั่ง ตามตัวอย่าง แล้วลองเรียกใช้งานดู

WITH CTE(PositionID, [Narration], [Description], LevelNumber ,DependOn, Trace)
  AS (
SELECT PositionID, CONVERT(nvarchar(max) ,[Description]) AS [Narration], [Description],
   1 AS LevelNumber, DependOn , CONVERT(nvarchar(max),PositionID) AS Trace
   FROM tblPosition  WHERE DependOn IS NULL
UNION ALL
SELECT E.PositionID, CONVERT(nvarchar(max) ,REPLICATE(SPACE(2), LevelNumber) + E.[Description]) AS [Narration],
   E.[Description], LevelNumber + 1 AS LevelNumber, E.DependOn ,
   CONVERT(nvarchar(max),CTE.Trace + E.PositionID) AS Trace
   FROM tblPosition E
   INNER JOIN CTE ON E.DependOn=CTE.PositionID
)
SELECT * FROM CTE ORDER BY  Trace;

ผลจากการปรับคำสั่งข้างต้น จะได้ผลลัพธ์ออกมาดังภาพ

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

แค่ตาราง tblPosition เพียงตารางเดียว ทำไมมันช่างงงงวย วกวนเช่นนี้ ทาเกชิ สู้ ๆ

ยุคสมัยเปลี่ยนไปเดี๋ยวทำงานที่ไหนก็ได้ โชคดี  =  (โชค + อธิบดี) มีเงินใช้

เพื่อนผมชื่อโชค ครับ  ย้ำ โชค นะครับ

21 มีนาคม 2562

SQL Server - Identity Column

IDENTITY เป็นคุณสมบัติหนึ่งของคอลัมน์ ที่มีเก็บข้อมูลประเภทตัวเลข ได้แก่ประเภท int, tinyint, smallint, bigint, decimal และ numeric หากเราต้องการกำหนดค่าของคอลัมน์ Identity ให้เป็นคีย์ควรกำหนดข้อมูลเป็นตัวเลขจำนวนเต็ม ซึ่งเราจะนิยมใช้อยู่ 2 ประเภท คือ  int หรือ bigint  เมื่อกำหนดแล้ว โดยคุณสมบัติของคอลัมน์นี้จะเป็นการสร้างเลขลำดับแบบอัตโนมัติทันทีเมื่อมีการเพิ่มแถวของข้อมูลเข้าสู่ตาราง (อาจเพิ่มข้อมูลจะสำเร็จหรือไม่สำเร็จก็ได้)
ตัวอย่าง  การสร้างคอลัมน์ Identity ชื่อ row_id ให้กับตาราง product
CREATE TABLE product
            (row_id                       bigint NOT NULL IDENTITY(1,1) PRIMARY KEY , 
 product_code             nvarchar(20) NOT NULL ,
 product_name            nvarchar(100),
 product_type              nvarchar(10), 
 um                            nvarchar(20),
 sale_price                   money)

CREATE UNIQUE INDEX product_product_code ON product (product_code)

หลายท่านสงสัย ทำไมถึงต้องมี row_id ที่เป็น identity และทำเป็น PK ทั้ง ๆ ที่ มี product_code ที่เป็นรหัสสินค้าที่ไม่ซ้ำกันซึ่งสามารถทำเป็น primary key ได้อยู่แล้ว ผู้เขียนเลี่ยงจะไปทำเป็น unique ให้กับรหัส product_code แทน  ด้วยเหตุผลทางด้านความเร็ว

แล้วมันเอาไปทำอะไรได้บ้าง   

1. เอาไปทำลำดับของข้อมูลอันไหนเกิดก่อนหลัง 
2. เอาไปทำเป็นคีย์ ใช้เวลาปรับปรุงข้อมูล ลบข้อมูล "คงสงสัย อ้าวมันก็มี product_code อยู่แล้ว แล้วจะใช้ row_id ทำไม" มาดูตัวอย่าง เช่น 
          เราเลือกข้อมูลมารายการมา 1 รายการ เช่น product_code = '000001'
          DECLARE @ROW_ID int ;

          SELECT @ROW_ID = row_id , * FROM product WHERE product_code = '000001'
          เก็บ row_id ไว้ในตัวแปร @ROW_ID (ถ้าเขียนโปรแกรมก็เก็บและเรียกใช้ ก็แนว ๆ นี้แหละ)
          เวลาจะปรับปรุง หรือลบ ก็ใช้ row_id มาใช้ได้เลย ทำให้เราสามารถหลับหูหลับตาใช้เงื่อนไข where row_id เดิม ๆ ในการปรับปรุงได้กับทุก ๆ ตาราง
         UPDATE SET product_code = '000002' , product_name = 'KKKK' WHERE row_id = @ROW_ID
ไม่ว่าจะใช้ตารางใด ๆ ก็ตาม เราไม่ต้องไปสนใจกับรหัสต่าง ๆ ที่เราตั้งขึ้นมา ก็ใช้ row_id ที่เป็น identity ได้เลยสะดวกดี

3. เลื่อนข้อมูลทีละรายการ ก็ใช้ได้
         
DECLARE @ROW_ID int ;
         SET @ROW_ID = 0;
        --- เริ่มอ่านจาก @ROW_ID แรก
         SELECT TOP 1 @ROW_ID = row_id , * FROM product WHERE row_id > @ROW_ID
         --  อ่านรายการต่อไป และต่อ ๆ ไป ก็เขียนเช่นเดิม ตามลำดับก่อนหลัง
         SELECT TOP 1 @ROW_ID = row_id , * FROM product WHERE row_id > @ROW_ID
4. ช่วยให้ข้อมูลเป็นเอกลักษณ์ บางตาราง อาจจะมีข้อมูลรายการซ้ำกันทุกคอลัมน์ SQL SERVER จะงงทำไม่ได้เมื่อมีการปรับปรุงข้อมูล เราก็เอา Identity คอลัมน์มาช่วยได้

5. และอื่น ๆ ตามที่อยากทำและนำไปใช้

ข้อระวัง


สิ่งที่ต้องระวัง เคยเห็นบางระบบ เอา Identity คอลัมน์ไปทำ เป็น Key เพื่อเชื่อมโยงไปยังตารางอื่น  (PK -> FK) ไม่ควรทำเป็นอย่างยิ่ง ถ้าจะใช้ให้ตั้งเป็นรหัส เช่น product_code แล้วเอาไปเชื่อมโยงแทน เวลาเกิดปัญหาถ้าคนอื่นมาทำต่อ หรือมาจัดการข้อมูลโดยไม่ทราบว่ามีการเชื่อมโยง แล้วหากมีการ generate Identity ใหม่ก็บรรลัยเกิด 

สวัสดีช่วงหน้าร้อน
เนื้อหารายละเอียดสามารถอ่านได้ที่ หนังสือรอบรู้ประยุกต์ใช้ SQL Server สั่งซื้อออนไลน์ได้ที่ ศูนย์หนังสือจุฬา http://www.chulabook.com/description.asp?barcode=9789740335108
"I Believe in You"

Copyright(c) 2007 - 2022 by Kasem Kamolchaipisit.