私はこの質問が何度も尋ねられ、解決策を知っていますが、問題の根本的な原因を理解しようとしています:
データベースのバックアップを実行する次のコードがあります。
DECLARE @Filename VARCHAR(256)
DECLARE @FileDate VARCHAR(15)
DECLARE @Path VARCHAR(50)
DECLARE @Name VARCHAR(50)
-- specify database backup directory
SET @Path = '\MyPath'
-- specify filename date
SELECT @FileDate = CONVERT(VARCHAR(20), GETDATE(), 112) + '_' + REPLACE(CONVERT(VARCHAR(20), GETDATE(), 108),':','')
DECLARE db_cursor CURSOR FOR
SELECT [name]
FROM master.sys.databases
WHERE [name] NOT IN ('master', 'msdb', 'model', 'tempdb')
AND [state_desc] = 'ONLINE'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @Name
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Filename = @Path + @Name + '_Full_Backup_' + @FileDate + '.bak'
BACKUP DATABASE @Name
TO DISK = @Filename
WITH CHECKSUM,
COMPRESSION
FETCH NEXT FROM db_cursor INTO @Name
END
CLOSE db_cursor
DEALLOCATE db_cursor
場合によっては、一部のデータベースのみがバックアップされ、返されたすべての行に対してカーソルが繰り返されていないか、クエリ自体がすべてのデータベースの名前を返さないことを示唆しています。
私は理解しようとしていますなぜこれが起こります。私は修正がSTATIC
カーソルを使用することであることを知っています、問題は基礎となるクエリの結果にあることを示唆しています
SELECT [name]
FROM master.sys.databases
WHERE [name] NOT IN ('master', 'msdb', 'model', 'tempdb')
AND [state_desc] = 'ONLINE'
変更されますが、何が変更されるかわかりません(データベース名は変更されず、エラーログはデータベースの状態が変更されたことを示唆していません)。
sys.databases
は複雑なビューです。 SQL Server 2016の場合:
CREATE VIEW sys.databases AS
SELECT d.name,
d.id AS database_id,
r.indepid AS source_database_id,
d.sid AS owner_sid,
d.crdate AS create_date,
d.cmptlevel AS compatibility_level,
-- coll.value = null means that a collation was not specified for the DB and the server default is used instead
convert(sysname, case when serverproperty('EngineEdition') = 5 AND d.id = 1 then serverproperty('collation')
else CollationPropertyFromID(convert(int, isnull(coll.value, p.cid)), 'name') end) AS collation_name,
iif ((serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x10000000) = 1), cast (3 as tinyint), p.user_access) AS user_access,
iif ((serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x10000000) = 1), 'NO_ACCESS', ua.name) AS user_access_desc,
sysconv(bit, d.status & 0x400) AS is_read_only, -- DBR_RDONLY
sysconv(bit, d.status & 1) AS is_auto_close_on, -- DBR_CLOSE_ON_EXIT
sysconv(bit, d.status & 0x400000) AS is_auto_shrink_on, -- DBR_AUTOSHRINK
case when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x00000020) = 1) then cast (1 as tinyint) -- RESTORING
when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x00000080) = 1) then cast (7 as tinyint) -- COPYING
when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x00000100) = 1) then cast (4 as tinyint) -- SUSPECT
when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x08000000) = 1) then cast (8 as tinyint) -- QUORUM_RECOVERY_PENDING
when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x04000000) = 1) then cast (9 as tinyint) -- CREATING
else p.state
end AS state, -- 7 is COPYING and 4 is SUSPECT state for database copy (UNDO: Need to have a clean way to set states in dbtable for a user db)
case when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x00000020) = 1) then 'RESTORING'
when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x00000080) = 1) then 'COPYING'
when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x00000100) = 1) then 'SUSPECT'
when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x08000000) = 1) then CONVERT(nvarchar(60), N'QUORUM_RECOVERY_PENDING')
when (serverproperty('EngineEdition') = 5) AND (sysconv(bit, d.status & 0x04000000) = 1) then 'CREATING'
else st.name
end AS state_desc,
sysconv(bit, d.status & 0x200000) AS is_in_standby, -- DBR_STANDBY
case when serverproperty('EngineEdition') = 5 then convert(bit, 0) else p.is_cleanly_shutdown end AS is_cleanly_shutdown,
sysconv(bit, d.status & 0x80000000) AS is_supplemental_logging_enabled, -- DBR_SUPPLEMENT_LOG
case when (serverproperty('EngineEdition') = 5) then sysconv(tinyint, sysconv(bit,(d.status & 0x00100000)))
else p.snapshot_isolation_state end AS snapshot_isolation_state,
case when (serverproperty('EngineEdition') = 5) and (sysconv(bit, d.status & 0x00100000) = 1) then 'ON'
when (serverproperty('EngineEdition') = 5) and (sysconv(bit, d.status & 0x00100000) = 0) then 'OFF'
else si.name end AS snapshot_isolation_state_desc,
sysconv(bit, d.status & 0x800000) AS is_read_committed_snapshot_on, -- DBR_READCOMMITTED_SNAPSHOT
case when (serverproperty('EngineEdition') = 5)
then case
when sysconv(bit,(d.status & 0x00000008)) = 1
then cast(3 as tinyint)
when sysconv(bit,(d.status & 0x00000004)) = 1
then cast(2 as tinyint)
else
cast(1 as tinyint)
end
else p.recovery_model
end AS recovery_model,
case when (serverproperty('EngineEdition') = 5)
then case
when sysconv(bit,(d.status & 0x00000008)) = 1
then CONVERT(nvarchar(60), N'SIMPLE')
when sysconv(bit,(d.status & 0x00000004)) = 1
then CONVERT(nvarchar(60), N'BULK_LOGGED')
else
CONVERT(nvarchar(60), N'FULL')
end
else ro.name
end AS recovery_model_desc,
p.page_verify_option, pv.name AS page_verify_option_desc,
sysconv(bit, d.status2 & 0x1000000) AS is_auto_create_stats_on, -- DBR_AUTOCRTSTATS
sysconv(bit, d.status2 & 0x00400000) AS is_auto_create_stats_incremental_on, -- DBR_AUTOCRTSTATSINC
sysconv(bit, d.status2 & 0x40000000) AS is_auto_update_stats_on, -- DBR_AUTOUPDSTATS
sysconv(bit, d.status2 & 0x80000000) AS is_auto_update_stats_async_on, -- DBR_AUTOUPDSTATSASYNC
sysconv(bit, d.status2 & 0x4000) AS is_ansi_null_default_on, -- DBR_ANSINULLDFLT
sysconv(bit, d.status2 & 0x4000000) AS is_ansi_nulls_on, -- DBR_ANSINULLS
sysconv(bit, d.status2 & 0x2000) AS is_ansi_padding_on, -- DBR_ANSIPADDING
sysconv(bit, d.status2 & 0x10000000) AS is_ansi_warnings_on, -- DBR_ANSIWARNINGS
sysconv(bit, d.status2 & 0x1000) AS is_arithabort_on, -- DBR_ARITHABORT
sysconv(bit, d.status2 & 0x10000) AS is_concat_null_yields_null_on, -- DBR_CATNULL
sysconv(bit, d.status2 & 0x800) AS is_numeric_roundabort_on, -- DBR_NUMEABORT
sysconv(bit, d.status2 & 0x800000) AS is_quoted_identifier_on, -- DBR_QUOTEDIDENT
sysconv(bit, d.status2 & 0x20000) AS is_recursive_triggers_on, -- DBR_RECURTRIG
sysconv(bit, d.status2 & 0x2000000) AS is_cursor_close_on_commit_on, -- DBR_CURSCLOSEONCOM
sysconv(bit, d.status2 & 0x100000) AS is_local_cursor_default, -- DBR_DEFLOCALCURS
sysconv(bit, d.status2 & 0x20000000) AS is_fulltext_enabled, -- DBR_FTENABLED
sysconv(bit, d.status2 & 0x200) AS is_trustworthy_on, -- DBR_TRUSTWORTHY
sysconv(bit, d.status2 & 0x400) AS is_db_chaining_on, -- DBR_DBCHAINING
sysconv(bit, d.status2 & 0x08000000) AS is_parameterization_forced, -- DBR_UNIVERSALAUTOPARAM
sysconv(bit, d.status2 & 64) AS is_master_key_encrypted_by_server, -- DBR_MASTKEY
sysconv(bit, d.status2 & 0x00000010) AS is_query_store_on, -- DBR_QDSENABLED
sysconv(bit, d.category & 1) AS is_published,
sysconv(bit, d.category & 2) AS is_subscribed,
sysconv(bit, d.category & 4) AS is_merge_published,
sysconv(bit, d.category & 16) AS is_distributor,
sysconv(bit, d.category & 32) AS is_sync_with_backup,
d.svcbrkrguid AS service_broker_guid,
sysconv(bit, case when d.scope = 0 then 1 else 0 end) AS is_broker_enabled,
p.log_reuse_wait, lr.name AS log_reuse_wait_desc,
sysconv(bit, d.status2 & 4) AS is_date_correlation_on, -- DBR_DATECORRELATIONOPT
sysconv(bit, d.category & 64) AS is_cdc_enabled,
case
when (d.id = db_id('tempdb')) then sysconv(bit, p.is_db_encrypted)
else sysconv(bit, d.status2 & 0x100) -- DBR_ENCRYPTION
end AS is_encrypted,
convert(bit, d.status2 & 0x8) AS is_honor_broker_priority_on, -- DBR_HONORBRKPRI
sgr.guid AS replica_id,
sgr2.guid AS group_database_id,
ssr.indepid AS resource_pool_id,
default_language_lcid = case when ((d.status2 & 0x80000)=0x80000 AND p.containment = 1) then convert(smallint, p.default_language) else null end,
default_language_name = case when ((d.status2 & 0x80000)=0x80000 AND p.containment = 1) then convert(sysname, sld.name) else null end,
default_fulltext_language_lcid = case when ((d.status2 & 0x80000)=0x80000 AND p.containment = 1) then convert(int, p.default_fulltext_language) else null end,
default_fulltext_language_name = case when ((d.status2 & 0x80000)=0x80000 AND p.containment = 1) then convert(sysname, slft.name) else null end,
is_nested_triggers_on = case when ((d.status2 & 0x80000)=0x80000 AND p.containment = 1) then convert(bit, p.allow_nested_triggers) else null end,
is_transform_noise_words_on = case when ((d.status2 & 0x80000)=0x80000 AND p.containment = 1) then convert(bit, p.transform_noise_words) else null end,
two_digit_year_cutoff = case when ((d.status2 & 0x80000)=0x80000 AND p.containment = 1) then convert(smallint, p.two_digit_year_cutoff) else null end,
containment = sysconv(tinyint, (d.status2 & 0x80000)/0x80000), -- DBR_IS_CDB
containment_desc = convert(nvarchar(60), cdb.name),
p.recovery_seconds AS target_recovery_time_in_seconds,
p.delayed_durability,
case when (p.delayed_durability = 0) then CAST('DISABLED' AS nvarchar(60)) -- LCOPT_DISABLED
when (p.delayed_durability = 1) then CAST('ALLOWED' AS nvarchar(60)) -- LCOPT_ALLOWED
when (p.delayed_durability = 2) then CAST('FORCED' AS nvarchar(60)) -- LCOPT_FORCED
else NULL
end AS delayed_durability_desc,
convert(bit, d.status2 & 0x80) AS
is_memory_optimized_elevate_to_snapshot_on, -- DBR_HKELEVATETOSNAPSHOT
sysconv(bit, d.category & 0x100) AS is_federation_member,
convert(bit, isnull(rda.value, 0)) AS is_remote_data_archive_enabled,
convert(bit, p.is_mixed_page_allocation_on) AS is_mixed_page_allocation_on
FROM sys.sysdbreg d OUTER APPLY OpenRowset(TABLE DBPROP, (case when serverproperty('EngineEdition') = 5 then DB_ID() else d.id end)) p
LEFT JOIN sys.syssingleobjrefs r ON r.depid = d.id AND r.class = 96 AND r.depsubid = 0 -- SRC_VIEWPOINTDB
LEFT JOIN sys.syspalvalues st ON st.class = 'DBST' AND st.value = p.state
LEFT JOIN sys.syspalvalues ua ON ua.class = 'DBUA' AND ua.value = p.user_access
LEFT JOIN sys.syspalvalues si ON si.class = 'DBSI' AND si.value = p.snapshot_isolation_state
LEFT JOIN sys.syspalvalues ro ON ro.class = 'DBRO' AND ro.value = p.recovery_model
LEFT JOIN sys.syspalvalues pv ON pv.class = 'DBPV' AND pv.value = p.page_verify_option
LEFT JOIN sys.syspalvalues lr ON lr.class = 'LRWT' AND lr.value = p.log_reuse_wait
LEFT JOIN sys.syssingleobjrefs agdb ON agdb.depid = d.id AND agdb.class = 104 AND agdb.depsubid = 0 -- SRC_AVAILABILITYGROUP
LEFT JOIN master.sys.syssingleobjrefs ssr ON ssr.class = 108 AND ssr.depid = d.id -- SRC_RG_DB_TO_POOL
LEFT JOIN master.sys.sysclsobjs ag ON ag.id = agdb.indepid AND ag.class = 67 -- SOC_AVAILABILITY_GROUP
LEFT JOIN master.sys.sysguidrefs sgr ON sgr.class = 8 AND sgr.id = ag.id AND sgr.subid = 1 -- GRC_AGGUID / AGGUID_REPLICA_ID
LEFT JOIN master.sys.sysguidrefs sgr2 ON sgr2.class = 9 AND sgr2.id = ag.id AND sgr2.subid = d.id -- GRC_AGDBGUID
LEFT JOIN sys.syspalvalues cdb ON cdb.class = 'DCDB' AND cdb.value = CASE WHEN (d.status2 & 0x80000)=0x80000 THEN 1 ELSE 0 END
LEFT JOIN sys.syslanguages sld ON sld.lcid = p.default_language
LEFT JOIN sys.fulltext_languages slft ON slft.lcid = p.default_fulltext_language
LEFT JOIN sys.sysobjvalues coll ON coll.valclass = 102 AND coll.subobjid = 0 AND coll.objid = d.id -- SVC_DATACOLLATION
LEFT JOIN sys.sysobjvalues rda ON rda.valclass = 116 AND rda.objid = d.id AND rda.valnum = 0 -- SVC_STRETCH & STRETCH_DB_IS_STRETCHED
WHERE d.id < 0x7fff
AND has_access('DB', (case when serverproperty('EngineEdition') = 5 then DB_ID() else d.id end)) = 1
特定のカーソルオプションを指定しないことにより、このビューでlocal, dynamic, updatable, optimistic, forward-only
カーソルを暗黙的に要求しています。
SQL Serverは動的カーソルプランを作成できないため、カーソルをローカルのkeyset、更新可能、オプティミスティック、前方専用カーソルに変換します。
キーセットとは、基になるシステムテーブルの行を見つけるために必要な最小限のキーが、カーソルが開かれたときにtempdbに格納されることを意味します。基になるシステムテーブルでこれらのキー値のいずれかが変更された場合、行は返されず、@@FETCH_STATUS
は-2を返します。 @@FETCH_STATUS = 0
が失敗し、カーソルからのデータベースがそれ以上処理されないため、ループは途中で終了します。キーの変更を引き起こす可能性のある例は、データベースのALLOW_SNAPSHOT_ISOLATION
状態の変更です。
同時キー変更が可能な場合、キーセットカーソルは賢明ではありません。基本となるテーブルは制御しないため、sys.databases
でキーセットカーソルを使用すると問題が発生します。最後に到達するまで成功すると想定するのではなく、@@FETCH_STATUS
の失敗をチェックすることも重要です。
オプティミスティックカーソルは、チェックサムを使用して、カーソルが開いてからの行の変更を検出するため、更新が失われることはありません。カーソルを介して行を更新していないので、これはここでは直接重要ではありませんが、SQL Serverはそれを認識していません。チェックサムを収集、保存、比較する計画を立てる必要があります。
補足:データベース名とstate_desc
列のみに関心があるため、オープンおよびフェッチ実行プランで行われる作業の多くは冗長です。オプティマイザには外部結合の削除を許可するのに十分なキー関係情報がないため、冗長なシステムテーブルアクセスのほとんどは削除できません。 DISTINCT name
を追加することでこれを回避できます(そのため、外部結合は行を複製できません)が、カーソルを強制的に静的(スナップショット)にダウングレードします。それでも、DISTINCT
を追加した場合の実行プランへの影響を確認したい場合があります。
ここでのレッスンは、デフォルトに依存せずに、必要なカーソルのタイプを明示的に指定することです。このカーソルの使用は、静的(スナップショット)タイプを義務付けます。