Eu tenho algum procedimento armazenado com o esquema "SalesTraining". Desejo alterar todos os esquemas de SalesTraining para "Vendas". Existe alguma maneira melhor do que recriar os procedimentos armazenados? obrigado
Eu tenho a consulta abaixo:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
A consulta acima é concluída em três segundos.
Se a consulta acima retornar algum valor, queremos que o procedimento armazenado EXIT, então eu o reescrevo como abaixo:
If Exists(
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End
No entanto, isso está demorando 10 minutos.
Posso reescrever a consulta acima como abaixo, que também é concluída em menos de 3 segundos:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End
O problema com a reescrita acima é que a consulta acima faz parte de um procedimento armazenado maior e retorna vários conjuntos de resultados. Em C#, iteramos cada conjunto de resultados e fazemos algum processamento.
O acima retorna um conjunto de resultados vazio, portanto, se eu seguir essa abordagem, terei que alterar meu C# e fazer a implantação novamente.
Então minha pergunta é,
por que usar apenas
IF EXISTS
muda o plano para levar tanto tempo?
Abaixo estão os detalhes que podem ajudá-lo e deixe-me saber se você precisar de algum detalhe:
- Criar script de tabela e estatísticas para obter o mesmo plano que o meu
- Plano de execução lenta
Plano de execução rápida
Plano lento usando Brentozar Cole o plano
Plano rápido usando Brentozar Cole o plano
Nota: Ambas as consultas são iguais (usando parâmetros), a única diferença é EXISTS
(posso ter cometido alguns erros ao anonimizar).
Os scripts de criação da tabela estão abaixo:
http://pastebin.com/CgSHeqXc -- estatísticas de mesa pequena
http://pastebin.com/GUu9KfpS -- estatísticas de mesa grande
Fiquei com a impressão de que, ao usar o LIKE
operador em todas as otimizações para cenários desconhecidos, tanto o legado quanto os novos CEs usam uma estimativa de 9% (supondo que estatísticas relevantes estejam disponíveis e o otimizador de consulta não precise recorrer a suposições de seletividade).
Ao executar a consulta abaixo no banco de dados de crédito, obtenho diferentes estimativas nos diferentes CEs. Sob o novo CE, recebo uma estimativa de 900 linhas que eu esperava, sob o CE herdado, recebo uma estimativa de 241,416 e não consigo descobrir como essa estimativa é derivada. Alguém é capaz de lançar alguma luz?
-- New CE (Estimate = 900)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName;
-- Forcing Legacy CE (Estimate = 241.416)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName
OPTION (
QUERYTRACEON 9481,
QUERYTRACEON 9292,
QUERYTRACEON 9204,
QUERYTRACEON 3604
);
No meu cenário, já tenho o banco de dados de crédito definido para o nível de compatibilidade 120, por isso na segunda consulta estou usando sinalizadores de rastreamento para forçar o CE herdado e também para fornecer informações sobre quais estatísticas são usadas/consideradas pelo otimizador de consulta. Posso ver que as estatísticas da coluna 'sobrenome' estão sendo usadas, mas ainda não consigo descobrir como a estimativa de 241,416 é derivada.
Não consegui encontrar nada on-line além deste artigo de Itzik Ben-Gan , que afirma "Ao usar o predicado LIKE em todas as otimizações para cenários desconhecidos, tanto o legado quanto os novos CEs usam uma estimativa de 9 por cento". As informações nesse post parecem estar incorretas.
Estou lutando para minimizar o custo da operação de classificação em um plano de consulta com o aviso Operator used
tempdbto spill data during execution with spill level 2
Encontrei várias postagens relacionadas a dados de derramamento durante a execução com nível de derramamento 1 , mas não nível 2. O nível 1 parece ser causado por estatísticas desatualizadas , e o nível 2? Não encontrei nada relacionado a level 2
.
Achei este artigo muito interessante relacionado a avisos de classificação:
Nunca ignore um aviso de classificação no SQL Server
Meu servidor SQL?
Microsoft SQL Server 2014 (SP2) (KB3171021) - 12.0.5000.0 (X64) 17 de junho de 2016 19:14:09 Copyright (c) Microsoft Corporation Enterprise Edition (64 bits) no Windows NT 6.3 (Build 9600: ) (Hypervisor)
Meu hardware?
executando a consulta abaixo para encontrar o harware:
-- Informações de hardware do SQL Server 2012
SELECT cpu_count AS [Logical CPU Count], hyperthread_ratio AS [Hyperthread Ratio],
cpu_count/hyperthread_ratio AS [Physical CPU Count],
physical_memory_kb/1024 AS [Physical Memory (MB)], affinity_type_desc,
virtual_machine_type_desc, sqlserver_start_time
FROM sys.dm_os_sys_info WITH (NOLOCK) OPTION (RECOMPILE);
memória atualmente alocada
SELECT
(physical_memory_in_use_kb/1024) AS Memory_usedby_Sqlserver_MB,
(locked_page_allocations_kb/1024) AS Locked_pages_used_Sqlserver_MB,
(total_virtual_address_space_kb/1024) AS Total_VAS_in_MB,
process_physical_memory_low,
process_virtual_memory_low
FROM sys.dm_os_process_memory;
quando executo minha consulta com escopo de um ano não recebo nenhum tipo de aviso, conforme a figura abaixo:
Mas quando o executo apenas para escopo de 1 dia, recebo este aviso on the sort operator
:
esta é a consulta:
DECLARE @FromDate SMALLDATETIME = '19-OCT-2016 11:00'
DECLARE @ToDate SMALLDATETIME = '20-OCT-2016 12:00'
SELECT DISTINCT
a.strAccountCode ,
a.strAddressLine6 ,
a.strPostalCode ,
CASE WHEN a.strCountryCode IN ('91','92') THEN 'GB-Int'
ELSE a.strCountryCode
END AS [strCountryCode]
FROM Bocss2.dbo.tblBAccountParticipant AS ap
INNER JOIN Bocss2.dbo.tblBAccountParticipantAddress AS apa ON ap.lngParticipantID = apa.lngParticipantID
AND apa.sintAddressTypeID = 2
INNER JOIN Bocss2.dbo.tblBAccountHolder AS ah ON ap.lngParticipantID = ah.lngParticipantID
INNER JOIN Bocss2.dbo.tblBAddress AS a ON apa.lngAddressID = a.lngAddressID
AND a.blnIsCurrent = 1
INNER JOIN Bocss2.dbo.tblBOrder AS o ON ap.lngParticipantID = o.lngAccountParticipantID
AND o.sdtmOrdCreated >= @FromDate
AND o.sdtmOrdCreated < @ToDate
OPTION(RECOMPILE)
o plano de consulta usando pastetheplan
Dúvidas: 1) no plano de consulta vejo isso:
StatementOptmEarlyAbortReason="GoodEnoughPlanFound" CardinalityEstimationModelVersion="70"
por que 70? Estou usando o sql server 2014
2) como faço para me livrar desse operador de classificação (se possível)?
3) Eu vi a expectativa de vida da página muito baixa, além de adicionar mais memória a este servidor, há alguma outra coisa que eu possa dar uma olhada para ver se posso evitar esse aviso?
Felicidades
Atualização após a resposta de Shanky e Paul White
Eu verifiquei minhas estatísticas de acordo com o script abaixo e elas parecem estar corretas e atualizadas.
estes são todos os índices e tabelas usados nesta consulta.
DBCC SHOW_STATISTICS ('dbo.tblBAddress','IDXF_tblBAddress_lngAddressID__INC')
GO
DBCC SHOW_STATISTICS ('dbo.tblBOrder','IX_tblBOrder_sdtmOrdCreated_INCL')
GO
DBCC SHOW_STATISTICS ('dbo.tblBAccountHolder','PK_tblAccountHolder')
GO
DBCC SHOW_STATISTICS ('dbo.tblBAccountParticipant','PK_tblBAccountParticipants')
GO
DBCC SHOW_STATISTICS ('dbo.tblBAccountParticipantAddress','IDXF_tblBAccountParticipantAddress_lngParticipantID')
GO
isso é o que eu tenho retornado:
Este é um resultado parcial, mas eu revisei todos eles.
Para atualização de estatísticas, atualmente tenho Ola Hallengren
o Index Optimize Job - programado para ser executado uma vez por semana - aos domingos
EXECUTE [dbo].[IndexOptimize]
@Databases = 'USER_DATABASES,-%Archive',
@Indexes = 'ALL_INDEXES' ,
@FragmentationLow = NULL,
@FragmentationMedium = NULL,
@FragmentationHigh = NULL,
@PageCountLevel=1000,
@StatisticsSample =100
,@UpdateStatistics = 'Index',
@OnlyModifiedStatistics = 'Y',
@TimeLimit=10800,
@LogToTable = 'Y'
Embora as estatísticas pareçam estar atualizadas Depois de executar o script a seguir, não recebi mais nenhum aviso no operador de classificação.
UPDATE STATISTICS [Bocss2].[dbo].[tblBOrder] WITH FULLSCAN
--1 hour 04 min 14 sec
UPDATE STATISTICS [Bocss2].[dbo].tblBAddress WITH FULLSCAN
-- 45 min 29 sec
UPDATE STATISTICS [Bocss2].[dbo].tblBAccountHolder WITH FULLSCAN
-- 26 SEC
UPDATE STATISTICS [Bocss2].[dbo].tblBAccountParticipant WITH FULLSCAN
-- 4 min
UPDATE STATISTICS [Bocss2].[dbo].tblBAccountParticipantAddress WITH FULLSCAN
-- 7 min 3 sec
Eu tenho a MERGE
declaração abaixo que é emitida contra o banco de dados:
MERGE "MySchema"."Point" AS t
USING (
SELECT "ObjectId", "PointName", z."Id" AS "LocationId", i."Id" AS "Region"
FROM @p1 AS d
JOIN "MySchema"."Region" AS i ON i."Name" = d."Region"
LEFT JOIN "MySchema"."Location" AS z ON z."Name" = d."Location" AND z."Region" = i."Id"
) AS s
ON s."ObjectId" = t."ObjectId"
WHEN NOT MATCHED BY TARGET
THEN INSERT ("ObjectId", "Name", "LocationId", "Region") VALUES (s."ObjectId", s."PointName", s."LocationId", s."Region")
WHEN MATCHED
THEN UPDATE
SET "Name" = s."PointName"
, "LocationId" = s."LocationId"
, "Region" = s."Region"
OUTPUT $action, inserted.*, deleted.*;
No entanto, isso faz com que a sessão seja encerrada com o seguinte erro:
Msg 0, Nível 11, Estado 0, Linha 67 Ocorreu um erro grave no comando atual. Os resultados, se existirem, deveriam ser descartados.
Msg 0, Level 20, State 0, Line 67 Ocorreu um erro grave no comando atual. Os resultados, se existirem, deveriam ser descartados.
Eu coloquei um pequeno script de teste que produz o erro:
USE master;
GO
IF DB_ID('TEST') IS NOT NULL
DROP DATABASE "TEST";
GO
CREATE DATABASE "TEST";
GO
USE "TEST";
GO
SET NOCOUNT ON;
IF SCHEMA_ID('MySchema') IS NULL
EXECUTE('CREATE SCHEMA "MySchema"');
GO
IF OBJECT_ID('MySchema.Region', 'U') IS NULL
CREATE TABLE "MySchema"."Region" (
"Id" TINYINT IDENTITY NOT NULL CONSTRAINT "PK_MySchema_Region" PRIMARY KEY,
"Name" VARCHAR(8) NOT NULL CONSTRAINT "UK_MySchema_Region" UNIQUE
);
GO
INSERT [MySchema].[Region] ([Name])
VALUES (N'A'), (N'B'), (N'C'), (N'D'), (N'E'), ( N'F'), (N'G');
IF OBJECT_ID('MySchema.Location', 'U') IS NULL
CREATE TABLE "MySchema"."Location" (
"Id" SMALLINT IDENTITY NOT NULL CONSTRAINT "PK_MySchema_Location" PRIMARY KEY,
"Region" TINYINT NOT NULL CONSTRAINT "FK_MySchema_Location_Region" FOREIGN KEY REFERENCES "MySchema"."Region" ("Id"),
"Name" VARCHAR(128) NOT NULL,
CONSTRAINT "UK_MySchema_Location" UNIQUE ("Region", "Name")
);
GO
IF OBJECT_ID('MySchema.Point', 'U') IS NULL
CREATE TABLE "MySchema"."Point" (
"ObjectId" BIGINT NOT NULL CONSTRAINT "PK_MySchema_Point" PRIMARY KEY,
"Name" VARCHAR(64) NOT NULL,
"LocationId" SMALLINT NULL CONSTRAINT "FK_MySchema_Point_Location" FOREIGN KEY REFERENCES "MySchema"."Location"("Id"),
"Region" TINYINT NOT NULL CONSTRAINT "FK_MySchema_Point_Region" FOREIGN KEY REFERENCES "MySchema"."Region" ("Id"),
CONSTRAINT "UK_MySchema_Point" UNIQUE ("Name", "Region", "LocationId")
);
GO
-- CONTAINS HISTORIC Point DATA
IF OBJECT_ID('MySchema.PointHistory', 'U') IS NULL
CREATE TABLE "MySchema"."PointHistory" (
"Id" BIGINT IDENTITY NOT NULL CONSTRAINT "PK_MySchema_PointHistory" PRIMARY KEY,
"ObjectId" BIGINT NOT NULL,
"Name" VARCHAR(64) NOT NULL,
"LocationId" SMALLINT NULL,
"Region" TINYINT NOT NULL
);
GO
CREATE TYPE "MySchema"."PointTable" AS TABLE (
"ObjectId" BIGINT NOT NULL PRIMARY KEY,
"PointName" VARCHAR(64) NOT NULL,
"Location" VARCHAR(16) NULL,
"Region" VARCHAR(8) NOT NULL,
UNIQUE ("PointName", "Region", "Location")
);
GO
DECLARE @p1 "MySchema"."PointTable";
insert into @p1 values(10001769996,N'ABCDEFGH',N'N/A',N'E')
MERGE "MySchema"."Point" AS t
USING (
SELECT "ObjectId", "PointName", z."Id" AS "LocationId", i."Id" AS "Region"
FROM @p1 AS d
JOIN "MySchema"."Region" AS i ON i."Name" = d."Region"
LEFT JOIN "MySchema"."Location" AS z ON z."Name" = d."Location" AND z."Region" = i."Id"
) AS s
ON s."ObjectId" = t."ObjectId"
WHEN NOT MATCHED BY TARGET
THEN INSERT ("ObjectId", "Name", "LocationId", "Region") VALUES (s."ObjectId", s."PointName", s."LocationId", s."Region")
WHEN MATCHED
THEN UPDATE
SET "Name" = s."PointName"
, "LocationId" = s."LocationId"
, "Region" = s."Region"
OUTPUT $action, inserted.*, deleted.*;
Se eu remover a OUTPUT
cláusula, o erro não ocorrerá. Além disso, se eu remover a deleted
referência, o erro não ocorrerá. Então, examinei os documentos do MSDN em busca da OUTPUT
cláusula que declara:
DELETED não pode ser usado com a cláusula OUTPUT na instrução INSERT.
O que faz sentido para mim, mas o ponto principal MERGE
é que você pode não saber com antecedência.
Além disso, o script abaixo funciona perfeitamente, independentemente da ação executada:
USE tempdb;
GO
CREATE TABLE dbo.Target(EmployeeID int, EmployeeName varchar(10),
CONSTRAINT Target_PK PRIMARY KEY(EmployeeID));
CREATE TABLE dbo.Source(EmployeeID int, EmployeeName varchar(10),
CONSTRAINT Source_PK PRIMARY KEY(EmployeeID));
GO
INSERT dbo.Target(EmployeeID, EmployeeName) VALUES(100, 'Mary');
INSERT dbo.Target(EmployeeID, EmployeeName) VALUES(101, 'Sara');
INSERT dbo.Target(EmployeeID, EmployeeName) VALUES(102, 'Stefano');
GO
INSERT dbo.Source(EmployeeID, EmployeeName) Values(103, 'Bob');
INSERT dbo.Source(EmployeeID, EmployeeName) Values(104, 'Steve');
GO
-- MERGE statement with the join conditions specified correctly.
USE tempdb;
GO
BEGIN TRAN;
MERGE Target AS T
USING Source AS S
ON (T.EmployeeID = S.EmployeeID)
WHEN NOT MATCHED BY TARGET AND S.EmployeeName LIKE 'S%'
THEN INSERT(EmployeeID, EmployeeName) VALUES(S.EmployeeID, S.EmployeeName)
WHEN MATCHED
THEN UPDATE SET T.EmployeeName = S.EmployeeName
WHEN NOT MATCHED BY SOURCE AND T.EmployeeName LIKE 'S%'
THEN DELETE
OUTPUT $action, inserted.*, deleted.*;
ROLLBACK TRAN;
GO
Além disso, tenho outras consultas que usam o OUTPUT
da mesma forma que a que está gerando um erro e funcionam perfeitamente bem - a única diferença entre elas são as tabelas que fazem parte do MERGE
.
Isso está causando grandes problemas na produção para nós. Reproduzi esse erro no SQL2014 e no SQL2016 tanto na VM quanto no físico com 128 GB de RAM, 12 núcleos de 2,2 GHZ, Windows Server 2012 R2.
O plano de execução estimado gerado a partir da consulta pode ser encontrado aqui:
Tarefa
Arquive todos, exceto um período contínuo de 13 meses, de um grupo de tabelas grandes. Os dados arquivados devem ser armazenados em outro banco de dados.
- O banco de dados está no modo de recuperação simples
- As tabelas têm 50 mil linhas a vários bilhões e, em alguns casos, ocupam centenas de GB cada.
- As tabelas atualmente não estão particionadas
- Cada tabela tem um índice clusterizado em uma coluna de data cada vez maior
- Cada tabela possui adicionalmente um índice não clusterizado
- Todas as alterações de dados nas tabelas são inserções
- O objetivo é minimizar o tempo de inatividade do banco de dados primário.
- O servidor é 2008 R2 Enterprise
A tabela "arquivo" terá cerca de 1,1 bilhão de linhas, a tabela "ao vivo" cerca de 400 milhões. Obviamente, a tabela de arquivo aumentará com o tempo, mas espero que a tabela ao vivo também aumente razoavelmente rápido. Digamos 50% nos próximos anos, pelo menos.
Eu tinha pensado nos bancos de dados estendidos do Azure, mas infelizmente estamos no 2008 R2 e provavelmente ficaremos lá por um tempo.
Plano atual
- Criar um novo banco de dados
- Crie novas tabelas particionadas por mês (usando a data modificada) no novo banco de dados.
- Mova os dados dos 12 a 13 meses mais recentes para as tabelas particionadas.
- Faça uma troca de renomeação dos dois bancos de dados
- Exclua os dados movidos do banco de dados agora "arquivado".
- Particione cada uma das tabelas no banco de dados "arquivo".
- Use trocas de partição para arquivar os dados no futuro.
- Percebo que terei que trocar os dados a serem arquivados, copiar essa tabela para o banco de dados de arquivo e, em seguida, trocá-la na tabela de arquivo. Isso é aceitável.
Problema: estou tentando mover os dados para as tabelas particionadas iniciais (na verdade, ainda estou fazendo uma prova de conceito nela). Estou tentando usar o TF 610 (conforme o Guia de desempenho de carregamento de dados ) e uma INSERT...SELECT
instrução para mover os dados inicialmente pensando que seriam minimamente registrados. Infelizmente, toda vez que tento, é totalmente logado.
Neste ponto, estou pensando que minha melhor aposta pode ser mover os dados usando um pacote SSIS. Estou tentando evitar isso, pois estou trabalhando com 200 tabelas e tudo o que posso fazer por script, posso gerar e executar facilmente.
Há algo que estou perdendo em meu plano geral e o SSIS é minha melhor aposta para mover os dados rapidamente e com uso mínimo do log (preocupações de espaço)?
Código de demonstração sem dados
-- Existing structure
USE [Audit]
GO
CREATE TABLE [dbo].[AuditTable](
[Col1] [bigint] NULL,
[Col2] [int] NULL,
[Col3] [int] NULL,
[Col4] [int] NULL,
[Col5] [int] NULL,
[Col6] [money] NULL,
[Modified] [datetime] NULL,
[ModifiedBy] [varchar](50) NULL,
[ModifiedType] [char](1) NULL
);
-- ~1.4 bill rows, ~20% in the last year
CREATE CLUSTERED INDEX [AuditTable_Modified] ON [dbo].[AuditTable]
( [Modified] ASC )
GO
-- New DB & Code
USE Audit_New
GO
CREATE PARTITION FUNCTION ThirteenMonthPartFunction (datetime)
AS RANGE RIGHT FOR VALUES ('20150701', '20150801', '20150901', '20151001', '20151101', '20151201',
'20160101', '20160201', '20160301', '20160401', '20160501', '20160601',
'20160701')
CREATE PARTITION SCHEME ThirteenMonthPartScheme AS PARTITION ThirteenMonthPartFunction
ALL TO ( [PRIMARY] );
CREATE TABLE [dbo].[AuditTable](
[Col1] [bigint] NULL,
[Col2] [int] NULL,
[Col3] [int] NULL,
[Col4] [int] NULL,
[Col5] [int] NULL,
[Col6] [money] NULL,
[Modified] [datetime] NULL,
[ModifiedBy] [varchar](50) NULL,
[ModifiedType] [char](1) NULL
) ON ThirteenMonthPartScheme (Modified)
GO
CREATE CLUSTERED INDEX [AuditTable_Modified] ON [dbo].[AuditTable]
(
[Modified] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON ThirteenMonthPartScheme (Modified)
GO
CREATE NONCLUSTERED INDEX [AuditTable_Col1_Col2_Col3_Col4_Modified] ON [dbo].[AuditTable]
(
[Col1] ASC,
[Col2] ASC,
[Col3] ASC,
[Col4] ASC,
[Modified] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON ThirteenMonthPartScheme (Modified)
GO
Mover código
USE Audit_New
GO
DBCC TRACEON(610);
INSERT INTO AuditTable
SELECT * FROM Audit.dbo.AuditTable
WHERE Modified >= '6/1/2015'
ORDER BY Modified
Sou iniciante em T-SQL. Quero decidir se uma string de entrada é um palíndromo, com saída = 0 se não for e saída = 1 se for. Ainda estou descobrindo a sintaxe. Não estou nem recebendo uma mensagem de erro. Estou procurando soluções diferentes e algum feedback, para obter uma melhor compreensão e conhecimento de como o T-SQL funciona, para me tornar melhor nisso - ainda sou um estudante.
A ideia-chave, a meu ver, é comparar os caracteres mais à esquerda e à direita entre si, para verificar a igualdade e, em seguida, comparar o segundo caractere da esquerda com o 2º do último, etc. Fazemos um loop: Se os personagens forem iguais entre si, continuamos. Se chegarmos ao fim, produzimos 1, caso contrário, produzimos 0.
Você poderia criticar:
CREATE function Palindrome(
@String Char
, @StringLength Int
, @n Int
, @Palindrome BIN
, @StringLeftLength Int
)
RETURNS Binary
AS
BEGIN
SET @ n=1
SET @StringLength= Len(String)
WHILE @StringLength - @n >1
IF
Left(String,@n)=Right(String, @StringLength)
SET @n =n+1
SET @StringLength =StringLength -1
RETURN @Binary =1
ELSE RETURN @Palindrome =0
END
Acho que estou no caminho certo, mas ainda estou muito longe. Alguma ideia?
Eu tenho um índice espacial para o qual DBCC CHECKDB
relata corrupções:
DBCC CHECKDB(MyDB)
WITH EXTENDED_LOGICAL_CHECKS, DATA_PURITY, NO_INFOMSGS, ALL_ERRORMSGS, TABLERESULTS
O índice espacial, índice XML ou exibição indexada 'sys.extended_index_xxx_384000' (objeto ID xxx) não contém todas as linhas que a definição de exibição produz. Isso não representa necessariamente um problema de integridade com os dados desse banco de dados.
O índice espacial, índice XML ou exibição indexada 'sys.extended_index_xxx_384000' (objeto ID xxx) contém linhas que não foram produzidas pela definição de exibição. Isso não representa necessariamente um problema de integridade com os dados desse banco de dados.
CHECKDB encontrou 0 erros de alocação e 2 erros de consistência na tabela 'sys.extended_index_xxx_384000' (objeto ID xxx).
O nível de reparo é repair_rebuild
.
Eliminar e recriar o índice não remove esses relatórios de corrupção. Sem EXTENDED_LOGICAL_CHECKS
, mas com DATA_PURITY
o erro não é relatado.
Além disso, CHECKTABLE
leva 45 minutos para esta tabela, embora seu CI tenha 30 MB de tamanho e haja cerca de 30 mil linhas. Todos os dados nessa tabela são geography
dados pontuais.
Esse comportamento é esperado em alguma circunstância? Ele diz "Isso não representa necessariamente um problema de integridade". O que eu deveria fazer? CHECKDB
está falhando, o que é um problema.
Este script reproduz o problema:
CREATE TABLE dbo.Cities(
ID int NOT NULL,
Position geography NULL,
CONSTRAINT PK_Cities PRIMARY KEY CLUSTERED
(
ID ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
INSERT dbo.Cities (ID, Position) VALUES (20171, 0xE6100000010C4E2B85402E424A40A07312A518C72A40)
GO
CREATE SPATIAL INDEX IX_Cities_Position ON dbo.Cities
(
Position
)USING GEOGRAPHY_AUTO_GRID
WITH (
CELLS_PER_OBJECT = 16, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Esta é a versão 12.0.4427.24 (SQL Server 2014 SP1 CU3).
Eu criei o script da tabela com esquema e dados, banco de dados fresco, execute. Mesmo erro. CHECKDB também tem esse tempo de execução incrível de 45min. Eu capturei o plano de consulta CHECKDB usando o SQL Profiler. Ele tem uma junção de loop equivocada, aparentemente causando tempo de execução excessivo. O plano tem tempo de execução quadrático no número de linhas da tabela! Junções de loop de varredura duplamente aninhadas.
Limpar todos os índices não espaciais não altera nada.
Estou desenvolvendo um banco de dados SQL Server 2012 e tenho uma pergunta sobre um relacionamento um para zero ou um.
Eu tenho duas tabelas, Codes
e HelperCodes
. Um código pode ter zero ou um código auxiliar. Este é o script sql para criar essas duas tabelas e seus relacionamentos:
CREATE TABLE [dbo].[Code]
(
[Id] NVARCHAR(20) NOT NULL,
[Level] TINYINT NOT NULL,
[CommissioningFlag] TINYINT NOT NULL,
[SentToRanger] BIT NOT NULL DEFAULT 0,
[LastChange] NVARCHAR(50) NOT NULL,
[UserName] NVARCHAR(50) NOT NULL,
[Source] NVARCHAR(50) NOT NULL,
[Reason] NVARCHAR(200) NULL,
[HelperCodeId] NVARCHAR(20) NULL,
CONSTRAINT [PK_Code] PRIMARY KEY CLUSTERED
(
[Id] ASC
),
CONSTRAINT [FK_Code_LevelConfiguration]
FOREIGN KEY ([Level])
REFERENCES [dbo].[LevelConfiguration] ([Level]),
CONSTRAINT [FK_Code_HelperCode]
FOREIGN KEY ([HelperCodeId])
REFERENCES [dbo].[HelperCode] ([HelperCodeId])
)
CREATE TABLE [dbo].[HelperCode]
(
[HelperCodeId] NVARCHAR(20) NOT NULL,
[Level] TINYINT NOT NULL,
[CommissioningFlag] TINYINT NOT NULL,
[LastChange] NVARCHAR(50) NOT NULL,
CONSTRAINT [PK_HelperCode] PRIMARY KEY CLUSTERED
(
[HelperCodeId] ASC
),
CONSTRAINT [FK_HelperCode_LevelConfiguration]
FOREIGN KEY ([Level])
REFERENCES [dbo].[LevelConfiguration] ([Level])
)
Isso é correto?
Um Code e um HelperCode são entidades diferentes. Um HelperCode pode ser usado (nenhum código faz referência a ele) ou usado (apenas um código faz referência a ele).
Talvez Code.HelperCodeId deve fazer parte da chave primária da tabela de códigos. Mas não tenho certeza se uma coluna nula pode fazer parte de uma primária. Fazendo isso, quero evitar que dois ou mais Codes façam referência ao mesmo HelperCode.