Tag Archive ฐานข้อมูล

Byphunsanit

สร้าง Data Dictionary แบบด่วน ๆ

ต้องเขียน Data Dictionary ให้โครงการที่ทำอยู่ แต่มันมีหลายตารางมาก และที่สำคัญคือ แต่ละตารางจะมี relation ยุบยับเต็มไปหมด copy โครงสร้างมาแต่ละตารางมาวางในเอ็กส์เซล์ที่ละตัวก็ตาลาย พลาดได้ง่าย ๆ เลย

วิธีที่คิดออกคือ มันต้องมีโปรแกรมที่ช่วยงานนี้ได้ซิ เราไม่ใช่คนแรกที่ต้องเขียน Data Dictionary ซะหน่อย มีจริง ๆ แต่แพง ไม่ก็ดูแปลก ๆ เปลี่ยนไปใช้อีกวิธีคือ ทำไมไม่ให้ SQL ทำให้ละ search เจอ SQL Server Data Dictionary แต่มันก็ดูยาก เลยต้องเอามาเขียนใหม่ซะเอง

query ที่เขียนขึ้นมาใหม่คือ

SET ANSI_DEFAULTS OFF;
GO

SET NOCOUNT ON;
GO

SET TEXTSIZE 8192;
GO

PRINT '<HTML><body>'
PRINT '<!doctype html>'
PRINT '<html>'
PRINT '<head>'
PRINT '<meta charset="utf-8">'
PRINT '<title>script generates a full data dictionary for all tables in a SQL Server by Pitt Phunsanit</title>'
PRINT '<style>table {background: #7ba0cd;border-collapse: collapse;border-left: solid 1px #7ba0cd;border-right: solid 1px #7ba0cd;font-family: "Calibri", "sans-serif";font-size: 11pt;width: 100%;}table tr {background: #FFFFFF;border-bottom: solid 1px #7ba0cd;}table thead tr {background-color: #4e80bc;color: white;}table tbody tr:nth-of-type(odd) {background: #d2deed;border-bottom: solid 1px #FFFFFF;box-shadow: 0 2px 0 -1px #7ba0cd;}table tbody tr.case,table tfoot tr {background: #d7d7d7;}</style>'
PRINT '</head>'
PRINT '<body>'
PRINT '<h1>' + DB_NAME() + '</h1>'
PRINT '<h3>create date : ' + convert(VARCHAR(10), GETDATE(), 111) + '</h3>'

SELECT html
	,html1
	,html2
FROM (
	SELECT s.[name] AS [schema]
		,CASE
			WHEN s.[name] = 'dbo'
				THEN t.[name]
			ELSE s.[name] + '.' + t.[name]
			END AS [table]
		,NULL AS column_id
		,CAST('<table><cption><b>' + t.[name] + '</b>' + ISNULL(' : '+RTRIM(CAST(ep.[value] AS NVARCHAR(4000))), '') + '</caption>' AS NTEXT) AS html
		,CAST('<thead>' + '<td style="width:100px;"><b>Primary Key</b></td>' + '<td style="width:200px;"><b>Foreign key</b></td>' + '<td style="width:400px;"><b>Column Name</b></td>' + '<td><b>Description</b></td>' AS NTEXT) AS html1
		,CAST('<td style="width:100px;"><b>Data Type</b></td>' + '<td style="width:100px;"><b>Allow Nulls</b></td>' + '<td style="width:100px;"><b>Identity</b></td>' + '<td style="width:100px;"><b>Default Value</b></td>' + '</thead><tbody>' AS NTEXT) AS html2
	FROM sys.tables t
	INNER JOIN sys.schemas s ON s.[schema_id] = t.[schema_id]
	-- get description of table, if available
	LEFT OUTER JOIN sys.extended_properties ep ON ep.major_id = t.[object_id]
		AND ep.minor_id = 0
		AND ep.NAME = 'MS_Description'
		AND ep.class = 1
	WHERE t.is_ms_shipped = 0
		AND NOT EXISTS (
			SELECT *
			FROM sys.extended_properties ms
			WHERE ms.major_id = t.[object_id]
				AND ms.minor_id = 0
				AND ms.class = 1
				AND ms.[name] = 'microsoft_database_tools_support'
			)

	UNION ALL

	SELECT s.[name] AS [schema]
		,CASE
			WHEN s.[name] = 'dbo'
				THEN t.[name]
			ELSE s.[name] + '.' + t.[name]
			END AS [table]
		,c.column_id
		,'' AS html
		,'<tr><td>' + CASE
			WHEN pk.column_id IS NOT NULL
				THEN 'PK'
			ELSE ''
			END + '</td><td>' + CASE
			WHEN fk.primary_table IS NOT NULL
				THEN fk.primary_table + '.' + fk.primary_column
			ELSE ''
			END + '</td><td>' + c.[name] + '</td><td>' + ISNULL(RTRIM(CAST(ep.[value] AS NVARCHAR(4000))), '') + '</td><td>' + CASE
			WHEN uty.[name] IS NOT NULL
				THEN uty.[name]
			ELSE ''
			END + CASE
			WHEN uty.[name] IS NOT NULL
				AND sty.[name] IS NOT NULL
				THEN ' (
			'
			ELSE ''
			END + CASE
			WHEN sty.[name] IS NOT NULL
				THEN sty.[name]
			ELSE ''
			END + CASE
			WHEN sty.[name] IN (
					'CHAR'
					,'NCHAR'
					,'VARCHAR'
					,'NVARCHAR'
					,'BINARY'
					,'VARBINARY'
					)
				THEN ' (
				' + CASE
						WHEN c.max_length = - 1
							THEN 'max'
						ELSE CASE
								WHEN sty.[name] IN (
										'NCHAR'
										,'NVARCHAR'
										)
									THEN CAST(c.max_length / 2 AS VARCHAR(MAX))
								ELSE CAST(c.max_length AS VARCHAR(MAX))
								END
						END + '
				) '
			WHEN sty.[name] IN (
					'NUMERIC'
					,'DECIMAL'
					)
				THEN ' (
				' + CAST(c.precision AS VARCHAR(MAX)) + '
				,' + CAST(c.scale AS VARCHAR(MAX)) + '
				) '
			ELSE ''
			END + CASE
			WHEN uty.[name] IS NOT NULL
				AND sty.[name] IS NOT NULL
				THEN '
			) '
			ELSE ''
			END + '</td><td>' + CASE
			WHEN c.is_nullable = 1
				THEN 'Y'
			ELSE ''
			END + '</td><td>' AS html1
		,CASE
			WHEN c.is_identity = 1
				THEN 'Y'
			ELSE ''
			END + '</td><td>' + ISNULL(dc.[definition], '') + '</td></tr>' AS html2
	FROM sys.columns c
	INNER JOIN sys.tables t ON t.[object_id] = c.[object_id]
	INNER JOIN sys.schemas s ON s.[schema_id] = t.[schema_id]
	-- get name of user data type
	LEFT OUTER JOIN sys.types uty ON uty.system_type_id = c.system_type_id
		AND uty.user_type_id = c.user_type_id
		AND c.user_type_id <> c.system_type_id
	-- get name of system data type
	LEFT OUTER JOIN sys.types sty ON sty.system_type_id = c.system_type_id
		AND sty.user_type_id = c.system_type_id
	-- get description of column, if available
	LEFT OUTER JOIN sys.extended_properties ep ON ep.major_id = t.[object_id]
		AND ep.minor_id = c.column_id
		AND ep.[name] = 'MS_Description'
		AND ep.[class] = 1
	-- get default' s code TEXT
	LEFT OUTER JOIN sys.default_constraints dc ON dc.parent_object_id = t.[object_id]
		AND dc.parent_column_id = c.column_id
	-- check for inclusion in primary key
	LEFT OUTER JOIN (
		SELECT ic.column_id
			,i.[object_id]
		FROM sys.indexes i
		INNER JOIN sys.index_columns ic ON ic.index_id = i.index_id
			AND ic.[object_id] = i.[object_id]
		WHERE i.is_primary_key = 1
		) pk ON pk.column_id = c.column_id
		AND pk.[object_id] = t.[object_id]
	-- check for inclusion in foreign key
	LEFT OUTER JOIN (
		SELECT CASE
				WHEN s.[name] = 'dbo'
					THEN pk.[name]
				ELSE s.[name] + '.' + pk.[name]
				END AS primary_table
			,pkc.[name] AS primary_column
			,fkc.parent_object_id
			,fkc.parent_column_id
		FROM sys.foreign_keys fk
		INNER JOIN sys.tables pk ON fk.referenced_object_id = pk.[object_id]
		INNER JOIN sys.schemas s ON s.[schema_id] = pk.[schema_id]
		INNER JOIN sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.[object_id]
			AND fkc.referenced_object_id = pk.[object_id]
		INNER JOIN sys.columns pkc ON pkc.[object_id] = pk.[object_id]
			AND pkc.column_id = fkc.referenced_column_id
		) fk ON fk.parent_object_id = t.[object_id]
		AND fk.parent_column_id = c.column_id
	WHERE t.is_ms_shipped = 0
		AND NOT EXISTS (
			SELECT *
			FROM sys.extended_properties ms
			WHERE ms.major_id = t.[object_id]
				AND ms.minor_id = 0
				AND ms.class = 1
				AND ms.[name] = 'microsoft_database_tools_support'
			)

	UNION ALL

	SELECT s.[name] AS [schema]
		,CASE
			WHEN s.[name] = 'dbo'
				THEN t.[name]
			ELSE s.[name] + '.' + t.[name]
			END AS [table]
		,999999 AS column_id
		,'' AS html
		,'' AS html1
		,'</tbody></table><br><hr><br>' AS html2
	FROM sys.tables t
	INNER JOIN sys.schemas s ON s.[schema_id] = t.[schema_id]
	WHERE t.is_ms_shipped = 0
		AND NOT EXISTS (
			SELECT *
			FROM sys.extended_properties ms
			WHERE ms.major_id = t.[object_id]
				AND ms.minor_id = 0
				AND ms.class = 1
				AND ms.[name] = 'microsoft_database_tools_support'
			)
	) tmp
ORDER BY tmp.[schema]
	,tmp.[table]
	,tmp.column_id;

PRINT '<a href="https://pitt.plusmagi.com">PlusMagi.com by Pitt Phunsanit ([email protected])</a>'
PRINT '</body>'
PRINT '</html>'

query ตัวนี้มีอะไรพิเศษมากกว่าตัวอื่น ๆ หลายอย่าง คือ

  1. การเอาไปใช้ แทนที่จะดูผลลัพธ์ในรูปแบบ grid ต้องใช้มันในแบบ Results to Text โดยกด (Ctrl+T) แล้วเอาผลที่ได้ (เป็น code html) ให้เอาไป copy เป็นไฟล์ .html แล้วจะ copy ไปลง excel / word อีกต่อหนึ่งก็ได้ ถ้าใช้ grid ก็ได้ code เหมือนกันแต่จะไม่สวย เพราะพวกคำสั่ง print มันจะไม่ทำงาน
  2. ผลลัพท์ต้องแบ่งเป็น column html, html1, html2 เพราะว่าถ้าผลลัพธ์ที่ออกมากมันยาว จะโดนตัดทิ้งไปเฉย ๆ พยามแก้อยู่นาน นานกว่าเขียน code ของมันซะอีก โดยลองใช้ SET TEXTSIZE 8192; และ cast ร่วมกับตัวเลือก Maximum number of characters display in each column ก็ไม่ได้ผล เลยต้องซอย column ออกมาแทน
  3. การเอา code ที่เอาไปใช้ ถ้ามีการจัด code ใหม่ต้องระวังเงื่อนไข อย่าง ‘CHAR’ ถ้าโดนจัดเป็น ‘ CHAR ‘ (มี space) มันจะไม่ทำงาน
  4. การใช้งานถ้าจะให้ข้อมูลออกมาครบต้องทำ table relation และใส่ descriptions ให้ครบ ถึงจะออกมาสวย ๆ ครบ ๆ เหมือนกัน
  5. sub query ด้านล่าง ๆ จริง ๆ คิดว่าถ้าให้มีประสิทธิภาพน่าจะเขียนวิธีอื่น ๆ ได้ แต่ตอนนี้ขอใช้ quick and easy (dirty) ไว้ก่อน
  6. ยังมีจุดที่จะแก้ในครั้งต่อไปอย่าง มันยังมี result header อยู่ทำให้มีเส้น ———————- กับ html html1 html2 เกินมาอยู่ แต่ลบเองไม่กี่วิก็ใช้ได้ละ เพราะงั้นปล่อย ๆ มันไปก่อน
  7. ใครแก้จุดไหนได้ รบกวนบอกผมด้วยละกันครับ

ตัวอย่างผลลัพธ์ที่ได้

DATABASE NAME

create date : 2016/05/30

areas_cities : อำเภอPrimary KeyForeign keyColumn NameDescriptionData TypeAllow NullsIdentityDefault Value

PK city_id int
status tinyint Y
geo_id ภูมิภาค int (‘0’)
province_id int (‘0’)
amphur_code varchar (4) Y

areas_cities_lang : อำเภอ (แปล)Primary KeyForeign keyColumn NameDescriptionData TypeAllow NullsIdentityDefault Value

PK areas_cities.city_id city_id int
PK language char (2)
name nvarchar (600)

จริง ๆ มันมีสีสันด้วยนะ สวยจนเอาไปใช้ได้เลยไม่ต้องแต่งอะไรอีก