Thursday, December 10, 2020

สร้าง INDEX ให้ได้ผล

 ที่มา:www.aware.co.th

หลาย ๆ ท่าน คงมีคำถามในใจว่า ทำไมสร้าง index มาแล้ว ทำไมการดึงข้อมูล (query) ยังช้าอยู่เหมือนเดิม ไม่เห็นจะเร็วขึ้นเลย ทั้งๆ ที่ในตำราก็บอกว่าสร้าง index แล้วจะทำให้ดึงข้อมูลได้เร็วขึ้น พอผมได้เข้าไปดูเลยพบว่าคอลัมน์ที่ทำมาใช้เป็น index มันไม่เหมาะสมนี่เอง ข้อมูลหลักแสนหลักล้านในตาราง แต่ดันเอาคอลัมน์ที่มีค่าที่แตกต่างกันเพียง 7 ค่า (SELECT Distinct Column_Name) มาเป็นทำเป็น index ซะงั้น ซึ่งไม่ถูกต้องตามหลักการเลือกคอลัมน์มาเป็น Index นั่นเอง ทำให้การดึงข้อมูลก็จะยังช้าอยู่เหมือนเดิมครับ

ผมมีเทคนิคง่ายๆ ในการเลือกคอลัมน์เพื่อใช้สร้าง index มาฝากครับ โดยการใช้สูตรตามด้านล่างนี้ครับ

SELECT Distinct Column Name / Number of Rows

หมายถึงให้เรา SELECT Distinct คอลัมน์ที่เราต้องการ (ซึ่งควรจะเป็นค่าที่ไม่ซ้ำและไม่มีค่า Null ปนอยู่) แล้วดูว่า select ได้ทั้งหมดกี่แถว(row) แล้วให้นำไป หาร กับจำนวนแถวทั้งหมดที่อยู่ในตารางนั้นๆ ครับ แล้วดูผลลัพธ์ว่าได้ค่าเป็นเท่าไหร่ ถ้าได้ค่าใกล้ 1 เท่าไร ก็แสดงว่าคอลัมน์นั้นน่าจะนำไปสร้าง index ที่ดีได้ครับ

ตัวอย่างในการเลือกคอลัมน์เพื่อสร้าง index ที่ดี
ถ้าในตารางของเรามีข้อมูล 100,000 แถว และคอลัมน์ที่ต้องการจะนำมาทำเป็น index มีจำนวน 90,000 แถวที่มีค่าไม่ซ้ำกัน
จากสูตรจะได้ว่า 90,000/100,000 = 0.9
0.9 ถือว่าเป็นค่าที่ใกล้ 1 ดังนั้นคอลัมน์นี้เข้าข่ายที่จะนำมาทำ index ที่ดีได้ครับ

ตัวอย่างในการเลือกคอลัมน์เพื่อสร้าง index ที่ไม่ดี
ถ้าในตารางของเรามีข้อมูล 100,000 แถว และคอลัมน์ที่ต้องการจะนำมาทำเป็น index มีจำนวนเพียง 800 แถวที่มีค่าไม่ซ้ำกัน
จากสูตรจะได้ว่า 800/100,000 = 0.008
0.008 ถือว่าเป็นค่าที่ห่างไกลจาก 1 มาก ดังนั้นคอลัมน์นี้ไม่ควรจะนำมาทำ index ครับ ซึ่งกรณีนี้เราควรปล่อยให้การดึงข้อมูลเป็นแบบดึงข้อมูลหมดในตาราง
(Full Table) จะทำให้มีประสิทธิภาพมากกว่าการใช้คอลัมน์นี้มาทำเป็น index ครับ

ส่วนในกรณีที่เราต้องการใช้หลายๆ คอลัมน์ มาสร้างเป็น index ให้พิจารณาเลือกคอลัมน์ที่มีการเรียกใช้บ่อยๆ ในความสัมพันธ์หรือการเชื่อมโยงกันของแต่ละตาราง (join table) ใน SQL Statement ซึ่งส่วนใหญ่ก็มักจะเป็นคอลัมน์เพื่อสร้าง index ที่ดีตามสูตรที่ได้กล่าวไปแล้วครับ

ข้อควรจำ:การที่มี index มากๆ ก็ใช่ว่าจะดีเสมอไป แม้ว่า index จะช่วยให้การดึงข้อมูล (Query) การเปลี่ยนแปลงข้อมูล (Update) และการลบข้อมูล (Delete) ทำได้เร็วขึ้น แต่มันจะไปลดประสิทธิภาพของการเพิ่มข้อมูล (Insert) แทน และการที่มี index จำนวนมาก ก็จะทำให้เปลืองเนื้อที่ในฐานข้อมูลด้วย ดังนั้นเราจึงควรพิจารณาสร้าง index เท่าที่จำเป็นเท่านั้นครับ

 

Monday, October 12, 2020

SQL หาผลรวม แต่ละเดือน

SELECT DATEPART(Year ,DateCol) As 'Year' 

                  ,DATEPART(Month ,DateCol) As 'MTH'

                 ,Round(SUM(YourDataColumn),2) As 'kWh'

FROM YourTable

WHERE ...YourCriteria
GROUP BY DATEPART(Year ,DateCol) , DATEPART(Month ,DateCol)

Monday, July 6, 2020

SSMS: Select Extended Properties เฉพาะที่ชื่อ MS_Description ของ Table ออกมาทั้งหมด

SELECT
 [Table Name] = i_s.TABLE_NAME,
 [Description] = s.value
FROM
 INFORMATION_SCHEMA.TABLES  i_s
LEFT OUTER JOIN
 sys.extended_properties s
ON
 s.major_id = OBJECT_ID(i_s.TABLE_SCHEMA+'.'+i_s.TABLE_NAME)
 AND s.name = 'MS_Description'
WHERE
 OBJECTPROPERTY(OBJECT_ID(i_s.TABLE_SCHEMA+'.'+i_s.TABLE_NAME), 'IsMsShipped')=0
 --AND i_s.TABLE_NAME = 'YourTableName'
ORDER BY
 i_s.TABLE_NAME

ถ้าเลือกมาเฉพาะ Table ที่มี Extended Properties
SELECT objtype, objname, name, value  
FROM fn_listextendedproperty (NULL, 'schema', 'dbo', 'table', default, NULL, NULL)

หรือ แบบนี้ก็ได้ผลเหมือนกัน
SELECT objtype, objname, name, value  
FROM fn_listextendedproperty (NULL, 'schema', 'dbo', 'table', NULL, NULL, NULL)

Thursday, June 25, 2020

วิธีการใช้คุณสมบัติส่วนขยาย (Extended Properties) ใน MS SQL Server Management Studio (SSMS)


Extended Properties เป็นจุดเด่นของ SSMS สามารถนำมาประยุกต์ใช้ได้หลายอย่างเช่น
  • Specify a caption for a table, view, or column.
  • Specify a display mask for a column.
  • Display a format of a column, define edit mask for a date column, define number of decimals, etc.
  • Specify formatting rules for displaying the data in a column.
  • Describe a specific database objects for all users.

ที่เคยใช้คือใส่ข้อมูลอธิบายใน Description ของ แต่ละ Table Column ดูได้จากลิ้งค์นี้
นอกจากนั้นยังสามารถนำมาใช้ได้กับอย่างอื่นได้อีกด้วย

Object ที่ใช้ Extended Properties ได้
  • Database
  • Stored Procedures
  • User-defined Functions
  • Table
  • Table Column
  • Table Index
  • Views
  • Rules
  • Triggers
  • Constraints 
ในที่นี้จะแสดงตัวอย่างดังนี้
 
1.วิธีการสร้าง/ปรับปรุง/ลบ คุณสมบัติส่วนขยาย Add, Update and Drop Extended Properties.
2.วิธีการดึงคุณสมบัติส่วนขยายที่สร้างไว้มาแสดง
3.วิธีการใช้ฟังก์ชัน FN_LISTEXTENDEDPROPERTY()


แนะนำวิธีสร้าง Extended Properties ใน Database ไว้ในที่นี้ 2 แบบ
แบบที่ 1. คลิกขวาที่ Database เลือก Properties แล้วเลือก Extended Properties
               แล้วป้อนค่าที่ต้องการในช่องใต้ Name, Value
               (อย่าลืมว่าในช่อง Name  จะต้องใส่ชื่อที่ไม่ซ้ำกับ ชื่อเดิมที่มีอยู่แล้ว)
               เสร็จแล้วคลิกปุ่ม OK เพื่อบันทึกข้อมูลที่ป้อนไว้




                เมื่อสร้างเสร็จแล้ว ลอง Query เรียกดูด้วยคำสั่ง

SELECT * FROM sys.extended_properties
   
แบบที่ 2. เรียกใช้ Stored Procedure
EXEC   sp_addextendedproperty 'MS_Description', 'Test DB Description'



--เรียกดู Extended Properties เฉพาะ Level ของ Database เท่านั้น
SELECT
   DB_NAME() AS DbName,
   p.name AS ExtendedPropertyName,
   p.value AS ExtendedPropertyValue
FROM
   sys.extended_properties AS p
WHERE
   p.major_id=0 
   AND p.minor_id=0 
   AND p.class=0
ORDER BY
   [Name] ASC

--เรียกดู Extended Properties ทั้งหมดใน Database นั้น
SELECT * FROM sys.extended_properties

--เรียกดู Database
SELECT DB_NAME(database_id) AS [DB Name], database_id  FROM sys.databases
--เรียกดู Schema
select * from sys.schemas
--เรียกดู Table/Column
select * from INFORMATION_SCHEMA.COLUMNS
select *  from sys.tables  


--เรียกดู Column เฉพาะที่มี Description
USE YourDatabase
SELECT
 [Table Name] = i_s.TABLE_NAME,
 [Column Name] = i_s.COLUMN_NAME,
 [Data Type] = i_s.DATA_TYPE,
 [Description] = s.value
FROM
 INFORMATION_SCHEMA.COLUMNS i_s
LEFT OUTER JOIN
 sys.extended_properties s
ON
 s.major_id = OBJECT_ID(i_s.TABLE_SCHEMA+'.'+i_s.TABLE_NAME)
 AND s.minor_id = i_s.ORDINAL_POSITION
 AND s.name = 'MS_Description'
WHERE
 OBJECTPROPERTY(OBJECT_ID(i_s.TABLE_SCHEMA+'.'+i_s.TABLE_NAME), 'IsMsShipped')=0
 --AND i_s.TABLE_NAME = 'YourTableName'
ORDER BY
 i_s.TABLE_NAME, i_s.ORDINAL_POSITION

--เรียกดูทุก Object ที่มี Description
SELECT S.name as [Schema Name], O.name AS [Object Name], ep.name, ep.value AS [Extended property]
FROM sys.extended_properties EP
LEFT JOIN sys.all_objects O ON ep.major_id = O.object_id 
LEFT JOIN sys.schemas S on O.schema_id = S.schema_id
LEFT JOIN sys.columns AS c ON ep.major_id = c.object_id AND ep.minor_id = c.column_id

--เรียกดู Description โดยใช้ Function ชื่อ fn_listextendedproperty
SELECT * FROM ::fn_listextendedproperty ('MS_Description2','Schema','dbo','Table','PageContent','Column','page') 
SELECT * FROM fn_listextendedproperty ('MS_Description2','Schema','dbo','Table','PageContent','Column','page') 
SELECT * FROM fn_listextendedproperty (NULL,'Schema','dbo','Table','PageContent','Column','page') 
SELECT * FROM fn_listextendedproperty (NULL,'Schema','dbo','Table','PageContent','Column',NULL)
SELECT * FROM fn_listextendedproperty (NULL,NULL,NULL,NULL,default,NULL,NULL)
SELECT * FROM fn_listextendedproperty (NULL,NULL,default,NULL,default,NULL,default)
--Add Extended Property (Database Level)
EXEC   sp_addextendedproperty 'MS_Description', 'Test DB Description'
--Update Extended Property (Database Level)
EXEC   sp_updateextendedproperty 'MS_Description', 'Test DB Description2'
--Drop Extended Property (Database Level)
EXEC   sp_dropextendedproperty  'MS_Description'
--Add Table / View Extended Properties
EXEC   sp_addextendedproperty 'MS_Description', 'Test Table Description','Schema', dbo, 'table', 'YourTableName'
EXEC   sp_addextendedproperty 'MS_Description2', 'Test View Description2','Schema', dbo, 'View', 'YourViewName'
--Add Column Extended Properties
EXEC   sp_addextendedproperty 'MS_Description', 'View Column Description','Schema', dbo, 'View', 'YourViewName', 'Column', YourColumnName
EXEC   sp_addextendedproperty 'MS_Description', 'Your Description', 'user', dbo, 'table', 'YourTableName', 'column', YourColumnName
EXEC   sp_addextendedproperty 'MS_Description3', 'Your Description', 'user', dbo, 'table', 'YourTableName', 'column', YourColumnName
EXEC   sp_addextendedproperty 'MS_Description4', 'Your Description', 'Schema', dbo, 'table', 'YourTableName', 'column', YourColumnName
--Add Stored Procedure Extended Properties
EXEC   sp_addextendedproperty 'YourProcedureExtenedPropertyName', 'Test Stored Procedure Description','Schema', dbo, 'Procedure', 'YourStoredProcedureName'
--Add Trigger Extended Properties
EXEC   sp_addextendedproperty 'YourTriggerExtenedPropertyName', 'Test Trigger Description','Schema', dbo, 'Table', 'YourTableName','Trigger','YourTriggerName'

Friday, May 22, 2020

ใช้ COPC32 เป็น OPC Data Logger ง่าย ไม่จำกัด Tag

  • นอกจากจะใช้ Plug-in ของ Kepware ชื่อ DataLogger ในการบันทึกข้อมูลจาก OPC ลง ฐานข้อมูลแล้ว ยังมีทางเลือกอื่น อีก
  • คือใช้ COPC32 เป็น OPC Data Logger

ที่มาจาก: https://yes5.wordpress.com/
ซอร์ฟแวร์ที่ต้องใช้
    MS SQL Server หรือ MS SQL Server Express
    Visual Studio 2015 Express
    COPC32
    OPC Server ค่ายใดก็ได้ที่ต้องการเอาข้อมูลไปเก็บใน MS SQL Server
ในตัวอย่างนี้ได้สร้าง Database ชื่อ test ไว้ใน MS SQL Server และสร้างตารางชื่อ t1 ซึ่งมีคอลัมน์แสดงในรูป คอลัมน์ “id” เป็นข้อมูลแบบ auto increment


MS SQL Server มี instance name ดังแสดง คือชื่อเครื่องคอมพิวเตอร์ 
และเวลาเรียกใช้สามารถใช้  “(local)” เป็นชื่อเรียกอ้างอิงได้เช่นกัน
ถ้าเราติดตั้ง MS SQL Express แบบดีฟอล์ตก็อาจจะมีชื่อ Instance อีกแบบเช่น “ACER\SQLEXPRESS”
ซึ่งสามารถเรียกอ้างอิงได้ทำนองเดียวกันว่า  “(local)\SQLEXPRESS” 


เปิดโปรเจ็คที่ดาวน์โหลดมาแล้วทำการตรวจสอบว่ามี COPC32 ใน Toolbox
ของ Visual Studio ดังรูป ถ้าไม่มีให้คลิ้กขวาที่ Toolbox เลือก Choose Item
แล้วเลือก COPC32 ในแท็ป COM Components


ในตัวอย่างจะใช้ Timer2 แสดงค่าจาก OPC tag บน Label สามตัวทุกๆ 1วินาที
และใช้ Timer ชื่อTimer1 เพื่อเก็บข้อมูลไว้ใน SQL Serverทุกๆ 5 วินาที


คลิกที่ไอคอนของ COPC32 บริเวณลูกศรเล็กๆ เลือก ActiveX-Properties
เพื่อเข้าไปกำหนด OPC Server, OPC tag ที่ต้องการ
เปิดดูโค้ดของTimer2จะพบโค้ดการเอาข้อมูลOPC tagมาเก็บไว้ในตัวแปรแบบGlobalชื่อ v(0), v(1)และ v(2) ก่อนที่จะนำค่าของตัวแปรดังกล่าวไปแสดงในLabel (ที่ทำเช่นนี้ก็เพื่อไม่ต้องเรียกใช้OPCหลายรอบ สามารถใช้ค่าในตัวแปรแทนได้ จะทำให้OPCไม่ทำงานหนัก)

    Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
        For i = 0 To 2
            v(i) = Axcopc1.GetVl(i)
        Next

        Label1.Text = v(0).ToString()
        Label2.Text = v(1).ToString()
        Label3.Text = v(2).ToString()
    End Sub

ในโค้ดของTimer1จะเป็นการใช้คำสั่งshell เพื่อเรียกใช้ SQLCMD.exe ซึ่งเป็นโปรแกรมหนึ่งของMS SQL Management Studio เราเรียกใช้SQLCMD.exeเพื่อส่งคำสั่งInsertข้อมูลไปยังตารางt1ในMS SQL Server (ดังนั้นถ้ามีตารางและDatabaseชื่อต่างจากนี้ก็ให้แก้ไขให้ถูกต้องด้วย)
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        Shell(“C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\110\Tools\Binn\SQLCMD.exe -S (local) -d test -Q “”insert into t1 (v1,v2,v3,Time_Date) values (” & v(0) & “,” & v(1) & “,” & v(2) & “,getdate()) ”””)
End Sub
อากูเมนต์ –S หมายถึง Server Name โปรดตรวจสอบว่ามี “\SQLEXPRESS” ตามหลังชื่อคอมพิวเตอร์หรือไม่ในMS SQL Management Studio ถ้ามี ก็ให้เปลี่ยนจาก (local) เป็น  (local)\SQLEXPRESS
–d หมายถึง Database Name ในตัวอย่างนี้Database nameคือtest
-Q หมายถึงคำสั่ง SQL query ตัวอย่างนี้เป็นการใช้Insert command เพื่อส่งค่า v(0), v(1), v(2) และวันเวลาปัจจุบันลงในตาราง t1 ในคอลัมน์ที่เกี่ยวข้อง โดยเราต้องเรียงลำดับตามคำสั่งInsert เช่นในตัวอย่างเป็นการใส่ข้อมูลในคอลัมน์ v1, v2,v3และTime_Dateตามลำดับ ข้อมูลที่จะใส่ก็ต้องเรียงตามคอลัมน์ดังกล่าวเพื่อให้ใส่ข้อมูลตรงตามคอลัมน์