?
CS模式下,數(shù)據(jù)是放在客戶本地的服務(wù)器上。而大部分客戶并沒有專業(yè)能力去維護(hù)他們的SQL Server,老版本數(shù)據(jù)庫系統(tǒng)設(shè)置更是較舊。因此,總是遇到客戶反饋的損壞問題。客戶 alter 或 drop 某個(gè)存儲(chǔ)過程、或者打開存儲(chǔ)過程列表時(shí),執(zhí)行中止并提示“架構(gòu)損壞”。
-- checkdb 中斷報(bào)錯(cuò)
DBCC CHECKDB(DBName)
-- 類似的,修復(fù)也報(bào)錯(cuò)
DBCC CHECKDB (dbname, REPAIR_ALLOW_DATA_LOSS);
CHECKDB 在數(shù)據(jù)庫 'dbname' 中發(fā)現(xiàn) 0 個(gè)分配錯(cuò)誤和 0 個(gè)一致性錯(cuò)誤。
DBCC 執(zhí)行完畢。如果 DBCC 輸出了錯(cuò)誤信息,請(qǐng)與系統(tǒng)管理員聯(lián)系。
消息 211,級(jí)別 23,狀態(tài) 16,第 1 行
可能發(fā)生了架構(gòu)損壞。請(qǐng)運(yùn)行 DBCC CHECKCATALOG。
除了這些錯(cuò)誤信息,完全不知道哪些表有問題。又對(duì)這個(gè)庫的所有表都 checktable ,也無報(bào)錯(cuò)。可以確認(rèn),當(dāng)前的表結(jié)構(gòu)及數(shù)據(jù)是沒問題的,斷定是當(dāng)前數(shù)據(jù)庫的系統(tǒng)表出現(xiàn)了問題。
好吧,打開 Profiler 跟著(RPC:Startding、RPC:Completed、SP:Startding、SP:Completed、SP:StmtStartding、SP:StmtCompleted、SQL:…),甚至還跟蹤了鎖的請(qǐng)求及釋放(有點(diǎn)多余了)。然后刪除某報(bào)錯(cuò)的存儲(chǔ)過程,跟蹤到以下SQL:

圖一
把以上跟蹤出現(xiàn)的涉及表查詢一遍:
select * from sys.all_objects
select * from sys.database_principals
select * from sys.sql_modules
select * from sys.system_sql_modules
發(fā)現(xiàn)是系統(tǒng)視圖 sys.sql_modules 報(bào)錯(cuò)!該視圖返回函數(shù)、視圖、存儲(chǔ)過程的定義。查看該視圖的定義:
sp_helptext 'sys.sql_modules'
--定義
CREATE VIEW sys.sql_modules AS
SELECT object_id = o.id,
definition = object_definition(o.id),
uses_ansi_nulls = sysconv(bit, o.status & 0x40000), -- OBJMOD_ANSINULLS
uses_quoted_identifier = sysconv(bit, o.status & 0x80000), -- OBJMOD_QUOTEDIDENT
is_schema_bound = sysconv(bit, o.status & 0x20000), -- OBJMOD_SCHEMABOUND
uses_database_collation = sysconv(bit, o.status & 0x100000), -- OBJMOD_USESDBCOLL
is_recompiled = sysconv(bit, o.status & 0x400000), -- OBJMOD_NOCACHE
null_on_null_input = sysconv(bit, o.status & 0x200000), -- OBJMOD_NULLONNULL
execute_as_principal_id = x.indepid
FROM sys.sysschobjs o
LEFT JOIN sys.syssingleobjrefs x ON x.depid = o.id AND x.class = 22 AND x.depsubid = 0 -- SRC_OBJEXECASOWNER
WHERE o.pclass <> 100 -- x_eunc_Server
AND ((o.type = 'TR' AND has_access('TR', o.id, o.pid, o.nsclass) = 1)
OR (type IN ('P','V','FN','IF','TF','RF','IS') AND has_access('CO', o.id) = 1)
OR (type IN ('R','D') AND o.pid = 0))
可以看到2個(gè)系統(tǒng)視圖: sys.sysschobjs、sys.syssingleobjrefs。但是,這2個(gè)系統(tǒng)視圖是無法直接查詢的,難道到這里就終止了嗎?不可能的!~
要查看這些系統(tǒng)視圖,我們需要以專用管理員連接(DAC) 訪問。添加 “-m” 到啟動(dòng)參數(shù),然后重啟服務(wù)。

圖二
直接點(diǎn)擊一個(gè)查詢窗口,以DAC管理員訪問:admin:<instancename>

圖三
好了,進(jìn)入損壞的數(shù)據(jù)庫,查詢系統(tǒng)視圖:
select * from sys.sysschobjs
select * from sys.syssingleobjrefs
其實(shí),這些系統(tǒng)視圖,等價(jià)于我們常看到的那些系統(tǒng)視圖。
Sysobjects = sys.sysschobjs
Syscolumns = sys.syscolpars
Sysindexes = sys.sysidxstats
廢話不多說,再執(zhí)行視圖sys.sql_modules 的定義中的一部分sql:
SELECT object_id = o.id,
definition = object_definition(o.id)
FROM sys.sysschobjs o
LEFT JOIN sys.syssingleobjrefs x ON x.depid = o.id AND x.class = 22 AND x.depsubid = 0
可以確認(rèn)是 object_definition 獲取定義的函數(shù)出錯(cuò)。回頭看看那個(gè)報(bào)錯(cuò)的存儲(chǔ)過程,查看其定義:
select object_definition(id),id from sys.sysschobjs where name='usp_mytest'
果然是報(bào)錯(cuò)的就是它,錯(cuò)誤就是最開始的信息。但是,不確定是否其他對(duì)象也可能出錯(cuò),所以執(zhí)行以下SQL,把所有輸出都執(zhí)行一遍。
select concat('selelct object_definition(',id,')') from sys.sysschobjs
既然確定了該出錯(cuò)的信息,那么就只能把該行數(shù)據(jù)刪掉了!那要?jiǎng)h除哪些表呢?以下這些表,可以都查看一遍,與對(duì)象id相關(guān)的,都可以查詢出來刪掉。
select id,name,type,concat('select * from sys.',name) from sys.sysschobjs WHERE NAME LIKE 'sys%' order by type,NAME
以下幾張表要?jiǎng)h除的:
select id from sys.sysschobjs where name='usp_mytest'
delete from sys.sysschobjs where id=xxxxxxxxxxx
delete from sys.syscolpars where id=xxxxxxxxxxx
delete from sys.syssoftobjrefs where depid=xxxxxxxxxxx
如果只刪除 sys.sysschobjs ,checkdb 的時(shí)候還是報(bào)以下錯(cuò)誤,所以把其他相關(guān)表也刪除。
Attribute (parent_object_id=xxxxxxxxxxx) of row (object_id=xxxxxxxxxxx) in sys.objects does not have a matching row (object_id=xxxxxxxxxxx) in sys.objects.
再執(zhí)行 checkdb,發(fā)現(xiàn)已經(jīng)沒有錯(cuò)誤了。上面提到的一些查詢也操作正常,SSMS存儲(chǔ)過程列表也可以打開了!
-- 可創(chuàng)建原來的存儲(chǔ)過程
-- Create procedure usp_mytest
ALTER DATABASE dbname SET EMERGENCY;
GO
ALTER DATABASE dbname SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
DBCC CHECKDB (dbname) WITH TABLOCK
GO
ALTER DATABASE dbname SET MULTI_USER;
GO
ALTER DATABASE dbname SET ONLINE;
GO
此時(shí),可以把啟動(dòng)參數(shù)“-m”去掉,重啟服務(wù)!至此,完美解決。checkdb 無法修復(fù)的系統(tǒng)對(duì)象,通過手動(dòng)修改解決了!
閱讀原文:原文鏈接
該文章在 2025/1/10 11:08:56 編輯過