Para quem está usando SQL Server Azure (funciona também com instância local), segue dica de um query que retorna o horário ajustado de acordo com a timezone informada no formato float.
No exemplo ultilizaremos a timezone -3:
declare @HorDif float = -3
select DATEADD(HH, @HorDif, GETUTCDATE ()) as Resultado
Solucionando problemas que os clientes não sabem que têm, de uma forma que eles jamais entenderiam...
domingo, 7 de agosto de 2016
SQL Server - Controlar ID de autoincremento (Alternativa ao autoinc do SQL)
Se você precisa controlar suas IDs (identidade, identity, autoinc), segue uma dica de como fazer isso sem dar locks em tabelas, o que seria um grande problema de desempenho.
1 - Crie a tabela que armazenará os últimos IDs usados:
CREATE TABLE [dbo].[IDs]
(
IDNom nvarchar(255) NOT NULL,
IDUlt int NULL,
CONSTRAINT [PK_tblIDs] PRIMARY KEY CLUSTERED
(
[IDNom] ASC
) WITH
(
PAD_INDEX = OFF
, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF
, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON
, FILLFACTOR = 100
)
);
GO
2 Crie a StoredProcedure que vai retornar o novo ID a cada solicitação e registrar seu uso na tabela anterior (SP criada por Max Vernon e Mike Defehr e fiz algumas alterações):
CREATE PROCEDURE [dbo].[ProxID](
@IDName nvarchar(255)
)
AS
BEGIN
DECLARE @Retry int;
DECLARE @EN int, @ES int, @ET int;
SET @Retry = 5;
DECLARE @NewID int;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET NOCOUNT ON;
WHILE @Retry > 0
BEGIN
BEGIN TRY
UPDATE dbo.IDs
SET @NewID = IDUlt = IDUlt + 1
WHERE IDNom = @IDName;
IF @NewID IS NULL
BEGIN
SET @NewID = 1;
INSERT INTO IDs (IDNom, IDUlt) VALUES (@IDName, @NewID);
END
SET @Retry = -2; /* no need to retry since the operation completed */
END TRY
BEGIN CATCH
IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
SET @Retry = @Retry - 1;
ELSE
BEGIN
SET @Retry = -1;
SET @EN = ERROR_NUMBER();
SET @ES = ERROR_SEVERITY();
SET @ET = ERROR_STATE()
RAISERROR (@EN,@ES,@ET);
END
END CATCH
END
IF @Retry = 0 /* must have deadlock'd 5 times. */
BEGIN
SET @EN = 1205;
SET @ES = 13;
SET @ET = 1
RAISERROR (@EN,@ES,@ET);
END
ELSE
SELECT @NewID AS NovoID;
END
GO
Pronto!
Agora é só usar a SP passando como parâmetro um nome qualquer (no meu caso uso o nome da tabela).
Se não existir, a SP cria automaticamente e retorna o valor 1.
Se já existir ela retorna o novo valor na sequencia.
Exemplo:
1 - Crie a tabela que armazenará os últimos IDs usados:
CREATE TABLE [dbo].[IDs]
(
IDNom nvarchar(255) NOT NULL,
IDUlt int NULL,
CONSTRAINT [PK_tblIDs] PRIMARY KEY CLUSTERED
(
[IDNom] ASC
) WITH
(
PAD_INDEX = OFF
, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF
, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON
, FILLFACTOR = 100
)
);
GO
2 Crie a StoredProcedure que vai retornar o novo ID a cada solicitação e registrar seu uso na tabela anterior (SP criada por Max Vernon e Mike Defehr e fiz algumas alterações):
CREATE PROCEDURE [dbo].[ProxID](
@IDName nvarchar(255)
)
AS
BEGIN
DECLARE @Retry int;
DECLARE @EN int, @ES int, @ET int;
SET @Retry = 5;
DECLARE @NewID int;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET NOCOUNT ON;
WHILE @Retry > 0
BEGIN
BEGIN TRY
UPDATE dbo.IDs
SET @NewID = IDUlt = IDUlt + 1
WHERE IDNom = @IDName;
IF @NewID IS NULL
BEGIN
SET @NewID = 1;
INSERT INTO IDs (IDNom, IDUlt) VALUES (@IDName, @NewID);
END
SET @Retry = -2; /* no need to retry since the operation completed */
END TRY
BEGIN CATCH
IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
SET @Retry = @Retry - 1;
ELSE
BEGIN
SET @Retry = -1;
SET @EN = ERROR_NUMBER();
SET @ES = ERROR_SEVERITY();
SET @ET = ERROR_STATE()
RAISERROR (@EN,@ES,@ET);
END
END CATCH
END
IF @Retry = 0 /* must have deadlock'd 5 times. */
BEGIN
SET @EN = 1205;
SET @ES = 13;
SET @ET = 1
RAISERROR (@EN,@ES,@ET);
END
ELSE
SELECT @NewID AS NovoID;
END
GO
Pronto!
Agora é só usar a SP passando como parâmetro um nome qualquer (no meu caso uso o nome da tabela).
Se não existir, a SP cria automaticamente e retorna o valor 1.
Se já existir ela retorna o novo valor na sequencia.
Exemplo:
quarta-feira, 8 de junho de 2016
Função para formatar números de telefone com ou sem DDD e com ou sem nono dígito
Função Delphi para formatar números de telefone com ou sem DDD e com ou sem nono dígito:
function FormataFone(sTelefone: String): String;
var
s: string;
tam: Integer;
begin
s := FilterChars(sTelefone, ['0'..'9']);
// Retira zero inicial se houver
if Copy(s,1,1) = '0' then
begin
s := Copy(s,2,Length(s)-1);
end;
case Length(s) of
8: // 4455-6677
begin
Result:= Copy(s,1,4)+'-'+Copy(s,5,4);
end;
9: / / 9 8877-6655
begin
Result:= Copy(s,1,1) + ' ' + Copy(s,2,4)+'-'+Copy(s,6,4);
end;
10: // (85) 8877-6655
begin
Result:= '('+Copy(s,1,2)+') '+Copy(s,3,4)+'-'+Copy(s,7,4);
end;
11: // (85) 9 8877-6655
begin
Result:= '('+Copy(s,1,2)+') ' + Copy(s,3,1) + ' ' +Copy(s,4,4)+'-'+Copy(s,8,4);
end;
else
Result:= s;
end;
function FormataFone(sTelefone: String): String;
var
s: string;
tam: Integer;
begin
s := FilterChars(sTelefone, ['0'..'9']);
// Retira zero inicial se houver
if Copy(s,1,1) = '0' then
begin
s := Copy(s,2,Length(s)-1);
end;
case Length(s) of
8: // 4455-6677
begin
Result:= Copy(s,1,4)+'-'+Copy(s,5,4);
end;
9: / / 9 8877-6655
begin
Result:= Copy(s,1,1) + ' ' + Copy(s,2,4)+'-'+Copy(s,6,4);
end;
10: // (85) 8877-6655
begin
Result:= '('+Copy(s,1,2)+') '+Copy(s,3,4)+'-'+Copy(s,7,4);
end;
11: // (85) 9 8877-6655
begin
Result:= '('+Copy(s,1,2)+') ' + Copy(s,3,1) + ' ' +Copy(s,4,4)+'-'+Copy(s,8,4);
end;
else
Result:= s;
end;
terça-feira, 26 de abril de 2016
FastReport - Imprimir ReportSummary no rodapé da página (bottom)
Para imprimir o ReportSummary basta usar o seguinte comando no evento OnBeforePrint:
procedure ReportSummary1OnBeforePrint(Sender: TfrxComponent);
begin
Engine.CurY := Engine.CurY + Engine.FreeSpace - ReportSummary1.Height - 1;
end;
Resultado:
Esta dica serve para qualquer tipo de banda. A propriedade Engine.CurY na verdade é a altura que de deve ser utilizada para a impressão do próximo componente.
procedure ReportSummary1OnBeforePrint(Sender: TfrxComponent);
begin
Engine.CurY := Engine.CurY + Engine.FreeSpace - ReportSummary1.Height - 1;
end;
Resultado:
Esta dica serve para qualquer tipo de banda. A propriedade Engine.CurY na verdade é a altura que de deve ser utilizada para a impressão do próximo componente.
sexta-feira, 22 de abril de 2016
Delphi - Arredondamento de valores
Essa função de arredondamento de valor monetário tem opcão de arredondar para mais ou para menos:
function ArredDinheiro(Fracao: Double; Cima : Boolean): Double;
var
Sinal : integer;
begin
Fracao := Fracao * 100;
// Sng
if Frac(Fracao) < 0 then
Sinal := -1
else if Frac(Fracao) = 0 then
Sinal := 0
else
Sinal := 1;
if Cima then
begin
Result := (Int(Fracao) + (Sinal));
end
else
begin
Result := Int(Fracao);
end;
Result := result / 100;
end;
Outras funções de arredondamento que podem ser úteis:
function Sgn(X: Extended): Integer;
{ Retorna -1, 0 or 1 de acordo com o sinal do argumento }
begin
if X < 0 then
Result := -1
else
if X = 0 then
Result := 0
else
Result := 1;
end;
function RoundUp(X: Extended): Extended;
{ Retorna o primeiro inteiro maior que ou igual a um
dado número em valor absoluto (o sinal e preservado).
RoundUp(3,3) = 4 RoundUp(-3,3) = -4 }
begin
Result := Int(X) + Sgn(Frac(X));
end;
function RoundDn(X: Extended): Extended;
{ Retorna o primeiro inteiro menor que ou
igual a um dado número em valor absoluto (o sinal é preservado).
RoundDn(3,7) = 3 RoundDn(-3,7) = -3
begin
Result := Int(X);
end;
function RoundN(X: Extended): Extended;
{ Arredonda um número "normalmente": caso a parte de fração
seja >= 0,5 o número será arredondado “para cima” (ver RoundUp)
caso contrário, se a parte de fração for < 0,5, o
número será arredondado “para baixo” (ver RoundDn).
RoundN(3,5) = 4 RoundN(-3,5) = -4
RoundN(3,1) = 3 RoundN(-3,1) = -3 }
begin
(*
if Abs(Frac(X)) >= 0.5 then
Result := RoundUp(X)
else
Result := RoundDn(X);
*)
Result := Int(X) + Int(Frac(X) * 2);
end;
function Fix(X: Extended): Extended;
{ Retorna o primeiro inteiro menor que ou
igual a um dado número.
Int(3,7) = 3 Int(-3,7) = -3
Fix(3,7) = 3 Fix(-3,1) = -4 }
begin
if (X >= 0) or (Frac(X) = 0) then
Result := Int(X)
else
Result := Int(X) - 1;
end;
function RoundDnX(X: Extended): Extended;
{ Retorna o primeiro inteiro menor que ou
igual a um dado número.
RoundDnX(3,7) = 3 RoundDnX(-3,7) = -3
RoundDnX(3,7) = 3 RoundDnX(-3,1) = -4 }
begin
Result := Fix(X);
end;
function RoundUpX(X: Extended): Extended;
{ Retorna o primeiro inteiro maior que ou
igual a um dado número.
RoundUpX(3,1) = 4 RoundUpX(-3,7) = -3 }
begin
Result := Fix(X) + Abs(Sgn(Frac(X)))
end;
function RoundX(X: Extended): Extended;
{ Arredonda o número "normalmente", porém levando em conta o sinal:
se a parte de fração for >= 0,5, o número
será arredondado “para cima” (ver RoundUpX)
caso contrario, se a parte de fração for < 0,5,
o número será arredondado “para baixo” (ver RoundDnX).
RoundX(3,5) = 4 RoundX(-3,5) = -3 }
begin
(*
if Abs(Frac(X)) >= 0,5 then
Result := RoundUpX(X)
else
Result := RoundDnX(X);
*)
Result := Fix(X + 0.5);
end;
SQL Server - Comparar dois bancos e listar diferenças
Para quem tem dificuldades em manter atualizadas as estruturas de seus clientes, vou postar uma query que compara dois Bancos de Dados. Vejam a query e observem os comentários:
-- BANCO A
use Quantum
SELECT CONCAT(t.name, '.', c.name, ' (', tp.name, ', ', c.length, ', ', c.isnullable, ')') TabelaCampoTipoTamanhoNull, t.name Tabela, c.name Campo, tp.name Tipo, c.length Tamanho, c.isnullable PerniteNull
into #EstruturaA
FROM SYSCOLUMNS c
inner join SYSTYPES tp on tp.xtype = c.xtype
where t.xtype = 'U'
go
-- BANCO B
use QuantumNovo
SELECT CONCAT(t.name, '.', c.name, ' (', tp.name, ', ', c.length, ', ', c.isnullable, ')') TabelaCampoTipoTamanhoNull, t.name Tabela, c.name Campo, tp.name Tipo, c.length Tamanho, c.isnullable PerniteNull
into #EstruturaB
FROM SYSCOLUMNS c
inner join SYSTYPES tp on tp.xtype = c.xtype
where t.xtype = 'U'
go
-- Lista as Tabelas e Campos que só existem no A, e logicamente não existem no B
SELECT TabelaCampoTipoTamanhoNull ExisteSomenteNoBancoB, Tabela, Campo, Tipo, Tamanho, PerniteNull
FROM #EstruturaB
where TabelaCampoTipoTamanhoNull
not in (SELECT TabelaCampoTipoTamanhoNull
FROM #EstruturaA)
-- Lista as Tabelas e Campos que só existem no B, e logicamente não existem no A
SELECT TabelaCampoTipoTamanhoNull ExisteSomenteNoBancoA, Tabela, Campo, Tipo, Tamanho, PerniteNull
FROM #EstruturaA
where TabelaCampoTipoTamanhoNull
not in (SELECT TabelaCampoTipoTamanhoNull
FROM #EstruturaB)
SQL Server 2012 e 2014 - Pulando (saltando) sequencia de autoincremento (identity)
A partir do SQL Server 2012 notei existe um bug na sequencia de autoincremrnto dos campos IDENTITY. Notei isso em 25/03/2012, ou seja, logo que lançaram o 2012. Postei no MSDN a questão e mesmo a confirmação do bug por parte de usuários experientes e certificados, pediram pra aguardar que muito possivelmente viria uma correção (ver post).
Mas veio o 2012 SP1 e o 2014 e o problema continua. Exemplo de um caso já no MSSQL 2014, onde o identity salta do 9 para o 1009:
Com o intuito de ajudar os que passam pelo mesmo problema e ainda aguardam uma solução por parte da Microsoft, vou postar uma solução paliativa (gambiarra) para evitar que o salto aconteça.
Basta executar a query abaixo e não haverá mais saltos do identity em todo o servidor (todos os bancos e tabelas, mesmo os criados depois).
Caso prefira troque o nome da Stored Procedure de sp_FixSeeds2012 para sp_FixSeeds[e o numero da sua versao], mas não interfere no funcionamento. Portanto aconselho não perder tempo com isso.
USE master; GO CREATE PROCEDURE sp_FixSeeds2012 AS BEGIN --foreach database DECLARE @DatabaseName varchar(255) DECLARE DatabasesCursor CURSOR READ_ONLY FOR SELECT name FROM sys.databases where name not in ('master','tempdb','model','msdb') and sys.databases.state_desc = 'online' OPEN DatabasesCursor FETCH NEXT FROM DatabasesCursor INTO @DatabaseName WHILE @@FETCH_STATUS = 0 BEGIN EXEC ('USE '+@DatabaseName + ' --foreach identity column DECLARE @tableName varchar(255) DECLARE @columnName varchar(255) DECLARE @schemaName varchar(255) DECLARE IdentityColumnCursor CURSOR READ_ONLY FOR select TABLE_NAME , COLUMN_NAME, TABLE_SCHEMA from INFORMATION_SCHEMA.COLUMNS where COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, ''IsIdentity'') = 1 OPEN IdentityColumnCursor FETCH NEXT FROM IdentityColumnCursor INTO @tableName, @columnName, @schemaName WHILE @@FETCH_STATUS = 0 BEGIN print ''['+@DatabaseName+'].[''+@tableName+''].[''+@schemaName+''].[''+@columnName+'']'' EXEC (''declare @MAX int = 0 select @MAX = max(''+@columnName+'') from ['+@DatabaseName+'].[''+@schemaName+''].[''+@tableName+''] if (@MAX IS NULL) BEGIN SET @MAX = 0 END DBCC CHECKIDENT(['+@DatabaseName+'.''+@schemaName+''.''+@tableName+''],RESEED,@MAX)'') FETCH NEXT FROM IdentityColumnCursor INTO @tableName, @columnName, @schemaName END CLOSE IdentityColumnCursor DEALLOCATE IdentityColumnCursor') FETCH NEXT FROM DatabasesCursor INTO @DatabaseName END CLOSE DatabasesCursor DEALLOCATE DatabasesCursor END GO EXEC sp_configure 'show advanced options', 1 ; GO RECONFIGURE GO EXEC sp_configure 'scan for startup procs', 1 ; GO RECONFIGURE GO EXEC sp_procoption @ProcName = 'sp_FixSeeds2012' , @OptionName = 'startup' , @OptionValue = 'true' GO
Assinar:
Postagens (Atom)
Postgres no docker
-- Criar compose nano docker-compose.yml version: '3.7' ### services: db: image: postgres volumes: - postgres_data:...
-
Aqui partimos do presuposto que você já tem os fontes do FastReport. Segue tutorial de instalação: Desinstale qualquer versão anterior em...
-
A partir do SQL Server 2012 notei existe um bug na sequencia de autoincremrnto dos campos IDENTITY. Notei isso em 25/03/2012, ou seja, lo...
-
Gostaria de falar sobre o aumento nos preços das uma licenças do RAD Studio / Delphi em todas as versões. Nos últimos anos, por muitas vez...

