Aaron的帖子回答了向表中添加實習的問題,但之後您需要修改應用程序代碼和存儲過程以使用新的模式。
......你可能會這麼想。實際上,您可以創建一個返回與舊架構匹配的數據的VIEW
,您也可以在該視圖上支持INSERT
操作,這些操作在Messages
和HugeTable
表中轉換爲子操作。爲便於閱讀,我將使用名稱InternedStrings
和ExceptionLogs
作爲表格。
因此,如果舊錶是這樣的:
CREATE TABLE ExceptionLogs (
LogId int IDENTITY(1,1) NOT NULL PRIMARY KEY,
Message nvarchar(1024) NOT NULL,
ExceptionType nvarchar(512) NOT NULL,
StackTrace nvarchar(4096) NOT NULL
)
而新表是:
CREATE TABLE InternedStrings (
StringId int IDENTITY(1,1) NOT NULL PRIMARY KEY,
Value nvarchar(max) NOT NULL
)
CREATE TABLE ExceptionLogs2 (-- note the new name
LogId int IDENTITY(1,1) NOT NULL PRIMARY KEY,
Message int NOT NULL,
ExceptionType int NOT NULL,
StackTrace int NOT NULL
)
添加一個索引InternedStrings
,使值查找速度快:
CREATE UNIQUE NONCLUSTERED INDEX IX_U_InternedStrings_Value ON InternedStrings (Value ASC)
然後你也會有一個VIEW
:
CREATE VIEW ExeptionLogs AS
SELECT
LogId,
MessageStrings .Value AS Message,
ExceptionTypeStrings.Value AS ExceptionType,
StackTraceStrings .Value AS StackTrace
FROM
ExceptionLogs2
INNER JOIN InternedStrings AS MessageStrings ON
MessageStrings.StringId = ExceptionLogs2.Message
INNER JOIN InternedStrings AS ExceptionTypeStrings ON
ExceptionTypeStrings.StringId = ExceptionLogs2.ExceptionType
INNER JOIN InternedStrings AS StackTraceStrings ON
StackTraceStrings.StringId = ExceptionLogs2.StackTrace
,並從未修改的客戶處理INSERT
操作:
CREATE TRIGGER ExceptionLogsInsertHandler
ON ExceptionLogs INSTEAD OF INSERT AS
DECLARE @messageId int = SELECT StringId FROM InternedStrings WHERE Value = inserted.Message
IF @messageId IS NULL
BEGIN
INSERT INTO InternedStrings (Text) VALUES (inserted.Message)
SET @messageId = SCOPE_IDENTITY()
END
DECLARE @exceptionTypeId int = SELECT StringId FROM InternedStrings WHERE Value = inserted.ExceptionType
IF @exceptionTypeId IS NULL
BEGIN
INSERT INTO InternedStrings (Text) VALUES (inserted.ExceptionType)
SET @exceptionTypeId = SCOPE_IDENTITY()
END
DECLARE @stackTraceId int = SELECT StringId FROM InternedStrings WHERE Value = inserted.StackTrace
IF @stackTraceId IS NULL
BEGIN
INSERT INTO InternedStrings (Text) VALUES (inserted.StackTrace)
SET @stackTraceId = SCOPE_IDENTITY()
END
INSERT INTO ExceptionLogs2 (Message, ExceptionType, StackTrace)
VALUES (@messageId, @exceptionTypeId, @stackTraceId)
注意這個TRIGGER
可以改進:它僅支持單行插入,而並不完全是併發安全,但因爲之前的數據贏得不會發生變化,這意味着在InternedStrings
表中存在數據複製的輕微風險 - 並且由於UNIQUE
索引,插入將失敗。有幾種可能的方法來處理這種情況,例如使用TRANSACTION
並將查詢更改爲使用holdlock
和updlock
。
我看不出規範化數據和字符串實習(使用重複值的引用而不是重複值)之間的任何概念區別,除了一個是自動的,另一個是手動的。我敢打賭,如果你測試@Aaron建議你看到非常大的尺寸差異。 –
尺寸的變化將取決於您對信息/類型等的選擇性。 –
沒有任何概念上的差異,但有很多實際的差異: *模式和查詢更改 *曾經是簡單的插入將成爲N個查詢,並可能有N個插入。 我認爲重複的字符串已經足夠普遍,具有內置功能可能會有用。 – Olmo