Let's say I have a table called [dbo].[Order] in my MSSQL Server database like the following:
CREATE TABLE [dbo].[Order] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[NumberInMonth] INT NOT NULL,
[Amount] DECIMAL (18, 2) NOT NULL,
CONSTRAINT [PK_dbo.Order] PRIMARY KEY CLUSTERED ([Id] ASC)
);
I want to have NumberInMonth column to be:
auto-incremented, so that the database will guarantee that the INSERT operation will create a new record with the value of Number = < previous value of Number > + 1 without any effort on the side of the application code
not unique, so that i could have one order #18 in January and another one order #18 in February (which will differ by Id field)
And also I'd like to have a way to have a schedulled operation which will reset the counter of NumberInMonth every first day of a month
How do I achieve it?
A simple sequence can provide the auto-incrementing functionallity:
CREATE SEQUENCE seq_numberInMonth as int START WITH 1 INCREMENT BY 1;
CREATE TABLE [dbo].[Order] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[NumberInMonth] INT NOT NULL DEFAULT(next value for seq_numberInMonth),
[Amount] DECIMAL (18, 2) NOT NULL,
CONSTRAINT [PK_dbo.Order] PRIMARY KEY CLUSTERED ([Id] ASC)
);
Test:
INSERT INTO [Order] (Amount) VALUES (12.0), (13.0), (14.0)
SELECT *
FROM [Order]
results:
Id NumberInMonth Amount
1 1 12,00
2 2 13,00
3 3 14,00
You can quite easily create a scheduled job to run every 1st of the month and reset the sequence:
ALTER SEQUENCE seq_numberInMonth RESTART WITH 1 ;
Creating the job in t-sql can be done like this: (didn't test it, but it should work according to the following links):
How to: Create a SQL Server Agent Job
Create a Job
USE msdb ;
GO
EXEC dbo.sp_add_job
#job_name = N'seq_numberInMonth reset' ;
GO
EXEC sp_add_jobstep
#job_name = N'seq_numberInMonth reset',
#step_name = N'1st',
#subsystem = N'TSQL',
#command = N'ALTER SEQUENCE seq_numberInMonth RESTART WITH 1 ;',
#retry_attempts = 5,
#retry_interval = 5 ;
GO
EXEC sp_add_schedule #schedule_name = 'every 1st of the month'
, #enabled = 1
, #freq_type = 16
, #freq_interval = 1
GO
EXEC sp_attach_schedule
#job_name = N'seq_numberInMonth reset',
#schedule_name = N'every 1st of the month';
GO
EXEC dbo.sp_add_jobserver
#job_name = N'seq_numberInMonth reset';
GO
Related
I am working in a SQL Server environment with one master server and many target servers. It sometimes happens that for a reason or another one a target server may go out of sync.
I have the choice when that occurs to manually run the following stored procedure to re-sync the target server:
exec sp_resync_targetserver #server_name = 'RMAPP11DV1\PROJECT'
My assignment is to automate the process so that we do not have to manually run it. I should write a script and schedule it as a job that should run a a schedule time to selectively find and re-sync only the target servers that are currently out of sync.
This is my approach so far. It is not working as expected (can not do the re-sync when ran), that is why I need any one output. Thanks in advance:
use msdb
set nocount on;
if exists (select * from tempdb.sys.all_objects where name like '%#targetstatus%') --deleting the table if it already exists
drop table #targetstatus
create table #targetstatus
(
server_id int not null,
server_name nvarchar(300) not null,
location nvarchar(350) null,
time_zone_adjustment int not null,
enlist_date datetime not null,
last_poll_date datetime not null,
status int not null,
unread_instructions int not null,
local_time datetime not null,
enlisted_by_nt_user nvarchar(100) not null,
poll_interval int not null
)
insert into #targetstatus
exec sp_help_targetserver
select * from #targetstatus
if exists (select * from tempdb.sys.all_objects where name like '%#needresync%') --deleting the table if it already exists
drop table #needresync
create table #needresync -- will hold the target servers needing to be re-synced
(
server_id int not null,
server_name nvarchar(300) not null,
location nvarchar(350) null,
time_zone_adjustment int not null,
enlist_date datetime not null,
last_poll_date datetime not null,
status int not null,
unread_instructions int not null,
local_time datetime not null,
enlisted_by_nt_user nvarchar(100) not null,
poll_interval int not null
)
insert into #needresync
select *
from #targetstatus
where status <> 1 -- we only want to run the syncing proc on the target with a status diff of #1
select * from #needresync
declare #target_server varchar(100);
set #target_server = ' '
while #target_server <> ' '
begin
set #target_server = (select max(server_name) from #needresync);
exec msdb.dbo.sp_resync_targetserver #server_name = '#target_server';
-- #target_server = #target_server + 1
end
You are not deleting the row out of #needresync. You must delete each row one by one inside the while loop.
However, a much easier method exists. You can use the systargetservers DMV without using any temp tables at all:
DECLARE #server sysname =
(SELECT TOP 1 FROM dbo.systargetservers WHERE status = 2); -- 2 = Re-sync Pending
WHILE (#server IS NOT NULL)
BEGIN
EXEC sp_resync_targetserver #server;
SET #server =
(SELECT TOP 1 FROM dbo.systargetservers WHERE status = 2);
END;
I created this stored procedure in SQL Server 2017, and apparently it worked at first but suddenly it just stopped working.
This is supposed to validate whether a car has already entered in a parking lot or not. If it hasn't it creates the row and in the vehicle table and creates the row in vehicle payment table. If this is not the case it just creates the row in in the vehicle payment table and updates the state to (meaning it's currently in).
--table names, fields and variables are in spanish
CREATE TABLE Estacionamiento.Vehiculo
(
id INT IDENTITY (1, 1) NOT NULL
CONSTRAINT PK_Vehiculo_Estacionamiento_id
PRIMARY KEY CLUSTERED (id),
tipoVehiculo INT NOT NULL,
placa NVARCHAR(8) NOT NULL UNIQUE,
CONSTRAINT CHK_Formato_Placa_Vehiculo
CHECK (placa LIKE '[A-Z][A-Z][A-Z]-[0-9][0-9][0-9][0-9]'),
estado BIT NOT NULL
)
GO
--Crear la tabla PagoVehiculo
CREATE TABLE Estacionamiento.PagoVehiculo
(
id INT IDENTITY(1, 1) NOT NULL
CONSTRAINT PK_Vehiculo_PagoVehiculo_id
PRIMARY KEY CLUSTERED (id),
vehiculo INT NOT NULL,
fechaHoraEntrada DATETIME NOT NULL,
fechaHoraSalida DATETIME NOT NULL,
total DECIMAL NOT NULL
)
GO
--The problem is here
CREATE PROCEDURE SP_InsercionVehiculosEntradasSalidas
(#placa NVARCHAR(8),
#tipoVehiculo NVARCHAR(15))
AS
BEGIN TRY
DECLARE #placaVehiculo INT
DECLARE #horaEntrada DATETIME
DECLARE #horaSalida DATETIME
IF NOT EXISTS(SELECT * FROM Estacionamiento.Vehiculo WHERE placa = #placa AND tipoVehiculo = #tipoVehiculo)
BEGIN
INSERT INTO Estacionamiento.Vehiculo (placa, tipoVehiculo, estado)
VALUES (#placa, #tipoVehiculo, 1)
SET #placaVehiculo = (SELECT id FROM Estacionamiento.Vehiculo WHERE placa = #placa)
INSERT INTO Estacionamiento.PagoVehiculo (vehiculo, fechaHoraEntrada, fechaHoraSalida, total)
VALUES (#placaVehiculo, GETDATE(), GETDATE(), 0)
END
ELSE IF EXISTS(SELECT * FROM Estacionamiento.Vehiculo WHERE placa = #placa)
BEGIN
SET #placaVehiculo = (SELECT id FROM Estacionamiento.Vehiculo WHERE placa = #placa)
PRINT(#placaVehiculo)
IF (SELECT estado FROM Estacionamiento.Vehiculo WHERE placa = #placa) = 0
BEGIN
INSERT INTO Estacionamiento.PagoVehiculo (vehiculo, fechaHoraEntrada, fechaHoraSalida, total)
VALUES (#placaVehiculo, GETDATE(), GETDATE(), 0)
UPDATE Estacionamiento.Vehiculo
SET estado = 1
END
ELSE IF (SELECT estado FROM Estacionamiento.Vehiculo WHERE placa = #placa) = 1
BEGIN
UPDATE Estacionamiento.PagoVehiculo
SET fechaHoraSalida = GETDATE()
WHERE vehiculo = #placaVehiculo
AND id = (SELECT MAX(id) FROM Estacionamiento.PagoVehiculo)
SET #horaEntrada = (SELECT MAX(fechaHoraEntrada)
FROM Estacionamiento.PagoVehiculo
WHERE vehiculo = #placaVehiculo)
SET #horaSalida = (SELECT MAX(fechaHoraSalida)
FROM Estacionamiento.PagoVehiculo
WHERE vehiculo = #placaVehiculo)
UPDATE Estacionamiento.Vehiculo
SET estado = 0
UPDATE Estacionamiento.PagoVehiculo
SET total = dbo.Fctn_CalculoPagoVehiculo(#horaEntrada, #horaSalida, #tipoVehiculo)
WHERE vehiculo = #placaVehiculo
AND id = (SELECT MAX(id)
FROM Estacionamiento.PagoVehiculo)
END
END
END TRY
BEGIN CATCH
DECLARE #error INT
SET #error = ##ERROR
RETURN #error
END CATCH
GO
I'm expecting to validate and insert and update when needed.
A few things that I am noticing as I read your proc:
In some places you check for a unique car in the vehiculo table by doing :
WHERE placa = #placa AND tipoVehiculo = #tipoVehiculo
and other places by doing just
WHERE placa = #placa
Be consistent in your code depending on which gives you the unique car
When the car is inserted into PagoVehiculo, you are populating both the entry and exit dates with the current date. While not technically wrong, I would expect the exit date to be null until the car exits. That will help with clarity of the data.
There are many places where you check select *, you can instead select 1, which will perform better
There is no ELSE statement to catch unexpected situations and report and issue.
At one point you are trying to get the id of the pagoVehiculo table for a particular car by doing:
id = (SELECT MAX(id) FROM Estacionamiento.PagoVehiculo)
There is no guaranty that will retrieve the correct car (specially since you could have multiple clients writting to the table at the same time. Add an additional clause to ensure you are pulling data for the right car. something like:
id = (SELECT MAX(id) FROM Estacionamiento.PagoVehiculo where vehiculo = #placaVehiculo)
In terms of what is going on with the proc, My guess is that is either failing silently or it is not falling into the IF or the ELSE IF and thus just skipping the whole thing. I would debug this as follows (after reviewing the comments :
Add and ELSE statement in the main If statement to catch unexpected situations
Add a print statement in the IF section , ELSE If and ELSE sections (something like print ('in IF statement') so that you know which branch is executing (or you can just debug it , if you are familiar with SQL Proc debugging)
Remove the try catch section and see if there is an exception that is being catched and not rethrown for some reason.
Oh and I just saw, tipoVehiculo is defined as INT in the table , but you are passing a NVARCHAR(15) in the proc, which is prob why you have the problem.
Take the following script:
EXEC sp_MSforeachtable #command1 = "DROP TABLE ?";
GO
CREATE TABLE _adminServices (
[ServiceID] INT CHECK ([ServiceID] > 0) NOT NULL IDENTITY,
[ServiceName] NVARCHAR(255) DEFAULT NULL,
[ManagerStaffID] INT CHECK ([ManagerStaffID] > 0) DEFAULT NULL,
[ODMStaffID] INT CHECK ([ODMStaffID] > 0) DEFAULT NULL,
[ReferralInactivityDays] INT DEFAULT NULL,
[TargetSupportHours] INT DEFAULT NULL,
[ShowInLists] SMALLINT NOT NULL DEFAULT '1',
[RecordEntryDate] DATETIME2(0) DEFAULT NULL,
[RecordModDate] DATETIME2(0) DEFAULT NULL,
PRIMARY KEY ([ServiceID])
) ;
CREATE INDEX [ManagerStaffID] ON _adminServices ([ManagerStaffID]);
CREATE INDEX [ODMStaffID] ON _adminServices ([ODMStaffID]);
CREATE INDEX [ShowInLists] ON _adminServices ([ShowInLists]);
GO
EXEC sp_MSforeachtable #command1 = 'IF (
select COUNT(TABLE_NAME)
from INFORMATION_SCHEMA.COLUMNS
where COLUMNPROPERTY(object_id(TABLE_SCHEMA+''.''+TABLE_NAME), COLUMN_NAME, ''IsIdentity'') = 1
AND TABLE_SCHEMA+''.''+TABLE_NAME = ''?''
) = 1
BEGIN
SET IDENTITY_INSERT ? ON;
END;';
GO
INSERT INTO _adminServices (ServiceID, ServiceName, ManagerStaffID, ODMStaffID, ReferralInactivityDays, TargetSupportHours, ShowInLists, RecordEntryDate, RecordModDate) VALUES
(1, 'Service 1', 16, 18, 0, NULL, 1, '2017-07-21 11:59:56', '2017-10-25 09:38:02');
GO
When I execute the aforementioned script in SSMS I get the following error:
Msg 544, Level 16, State 1, Line 36
Cannot insert explicit value for identity column in table '_adminServices' when IDENTITY_INSERT is set to OFF.
Can anyone tell me why?
EDIT: My goal is the following: I have multiple tables and multiple inserts.
For the Inserts I have the scripts. Since I don't want to write the SET IDENTITY_INSERT ON and OFF for every table since I just have the INSERT INTO queries on a file, I want a way to do the following:
Delete all the tables in the DB
Create all the tables (I have the SQL for this)
Set all the required identities to ON
Run the Inserts (I have the SQL for this)
Set all the required identities to OFF
sp_MSforeachtable run on another session in relation to your insert
At any time, only one table in a session can have the IDENTITY_INSERT property set to ON.
General scheme of inserting the original value:
CREATE TABLE
SET IDENTITY INSERT ON
INSERT VALUES
SET IDENTITY INSERT OFF
If you table not have single structure sp_MSforeachtable you will not be suitable
I have a problem where a delete is running inside a transaction
Without committing the delete I run another delete in another transaction
If I use "delete where record in (select...)" the delete is blocked by the original transaction
However if I use "delete where record in (actual record)" the delete goes through straight away
CREATE TABLE [dbo].[test1] ([token] [varchar] (10) not null, [superToken] [varchar] (10) not NULL, [nr] INT NOT NULL)
ALTER TABLE [dbo].[test1] ADD CONSTRAINT [PK_test1] PRIMARY KEY CLUSTERED ([token], [nr]) ON [PRIMARY]
insert into dbo.test1 (token,supertoken,nr) values ('1','1', 123)
insert into dbo.test1 (token,supertoken,nr) values ('2','2', 456)
CREATE TABLE [dbo].[test2] ([token] [varchar] (10) not null, [supertoken] [varchar] (10) not NULL)
ALTER TABLE [dbo].[test2] ADD CONSTRAINT [PK_test2] PRIMARY KEY CLUSTERED ([token]) ON [PRIMARY]
insert into dbo.test2 (token, supertoken) values ('1','1')
insert into dbo.test2 (token, supertoken) values ('2','2')
/* Begin Step 1 */
-- run this query in window 1 - do not commit
BEGIN TRANSACTION
DELETE test1 FROM test1 WITH (NOLOCK) WHERE token IN (SELECT token FROM test2 WITH (NOLOCK) WHERE supertoken='1'))
-- run this query in window 2 - will cause blocking
BEGIN TRANSACTION
DELETE test1 FROM test1 WITH (NOLOCK) WHERE token IN (SELECT token FROM test2 WITH (NOLOCK) WHERE supertoken = '2')
-- can now rollback all transations
/* End step 1 */
/* Begin Step 2 */
-- run this query in window 1 - do not commit
BEGIN TRANSACTION
DELETE test1 FROM test1 WITH (NOLOCK) WHERE token IN ('1') -- what sub query in step 1, query 1 returns
-- run this query in window 2 - will NOT cause blocking
BEGIN TRANSACTION
DELETE test1 FROM test1 WITH (NOLOCK) WHERE token IN ('2') -- what sub query in step 1, query 2 returns
-- can now rollback all transations
/* End step 2 */
It looks like you can get around this by saving the parent.Token from table2 into a variable. This looks like it'll work since your tokens appear to be uniqueidentifiers, and because of this the subquery will return at most 1 result:
DECLARE #ChildToken UNIQUEIDENTIFIER
SELECT #ChildToken = parent.Token
FROM Table2 parent
WHERE parent.SuperToken = 'E38D2055-7D52-4C58-A50E-5297B8A58659'
DELETE child
FROM table1 child
WHERE child.Token = #ChildToken
EDIT: I added all of the code for the logon trigger.
Why am I getting some incorrect results when trying to retrieve the total time it takes for a query to run? This is within the context of a logon trigger on SQL Server 2008 R2.
For testing purposes, I want to get the a rough estimate of the total time it takes for a logon trigger to run. Here is a small sample of the results:
LogonId AppId TotalLogonTime (in MS)
101 1 0
253 2 3
289 2 3
985 1 -3
325 1 0
How can the total time evaluate to be negative? Out of the 2.3 million executions so far, there have been 25 that resulted in a time of -3 ms. Here is the code:
CREATE trigger [trgAudit]
on all server
with execute as 'TriggerLogonUser'
for logon
as
begin
set nocount on
set transaction isolation level read committed
declare
#LoginTime datetime
,#IsWindowsUser bit
,#TotalLogonTimeMS int
,#ClientNetAddress varchar(48)
,#MacAddress nchar(12)
,#CurrentUserId int -- UserNames
,#ApplicationId int
,#HonId int --HostName
,#LogonId int --Logon
,#MacId int -- MacAddress
,#CnaId int -- ClientNetAddress
begin try
/*
*** Get the login time, user type, mac address, and client net address
*/
select
#LoginTime = getdate(),
#IsWindowsUser = case
when len(nt_domain) = 0
then 0
else 1
end,
#MacAddress = p.net_address,
#ClientNetAddress = convert(varchar(48),connectionproperty('client_net_address'))
from sys.sysprocesses p
where
p.spid = ##spid
/*
*** Client Net Address
*/
select top 1
#CnaId = CnaId
from master.sysmaintenance.ClientNetAddress with(index(IX_CnaAddress))
where #ClientNetAddress = CnaAddress
--if the ip does not exist, insert it.
if #CnaId is null
begin
insert master.sysmaintenance.ClientNetAddress(CnaAddress)
values (#ClientNetAddress)
select #CnaId = ##identity
end
/*
*** Applications
*/
select top 1
#ApplicationId = AppId
from master.sysmaintenance.Applications with(index(IX_AppName))
where app_name() = AppName
if #ApplicationId is null
begin
insert master.sysmaintenance.Applications (AppName)
values (app_name())
select #ApplicationId = ##identity
end
/*
*** HostName
*/
select top 1
#HonId = HonId
from master.sysmaintenance.HostName with(index(IX_HonName))
where HonName = host_name()
if #HonId is null
begin
insert master.sysmaintenance.HostName
values (host_name())
select #HonId = ##identity
end
/*
*** UserNames
*/
select top 1
#CurrentUserId = UsnId
from master.sysmaintenance.Usernames with(index(IX_UsnName))
where UsnName = original_login()
if #CurrentUserId is null
begin
insert master.sysmaintenance.Usernames
values (original_login())
select #CurrentUserId = ##identity
end
/*
*** MacAddress
*/
select top 1
#MacId = MacId
from master.sysmaintenance.MacAddress with(index(IX_MacAddress))
where MacAddress = #MacAddress
-- same logic is continued as in the applications
if #MacId is null
begin
insert master.sysmaintenance.MacAddress (MacAddress)
values (#MacAddress)
select #MacId = ##identity
end
/*
*** Get the total logon time
*/
select #TotalLogonTimeMS = datediff(ms,#LoginTime, getdate())
-- insert ids of the data gathered on the logon event into the logon table.
insert master.sysmaintenance.Logon ( LogAppId,
LogHonId,
IsWindowsLogon,
CurrentLogonId,
LogonDatetime,
LogCnaId,
LogMacId,
LogonTimeMS )
values ( #ApplicationId,
#HonId,
#IsWindowsUser,
#CurrentUserId,
#LoginTime,
#CnaId,
#MacId,
#TotalLogonTimeMS
)
end try
begin catch
print cast(error_number() as nvarchar(11))
print cast(error_severity() as nvarchar(11))
print cast(error_state() as nvarchar(11))
print cast(error_procedure() as nvarchar(126))
print cast(error_line() as nvarchar(11))
print cast(error_message() as nvarchar(2048))
end catch
end
Here is the DDL to the Logon Table:
CREATE TABLE [sysmaintenance].[Logon](
[LogonId] [bigint] IDENTITY(1,1) NOT NULL,
[LogAppId] [int] NULL,
[LogHonId] [int] NULL,
[LogMacId] [int] NULL,
[LogCnaId] [int] NULL,
[IsWindowsLogon] [bit] NULL,
[CurrentLogonId] [int] NULL,
[LogonDatetime] [datetime] NULL,
[LogonTimeMS] [int] NULL
) ON [PRIMARY]
CREATE UNIQUE CLUSTERED INDEX [PK_Logon] ON [sysmaintenance].[Logon]
(
[LogonId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
Any help or insight is appreciated.
GETDATE():
Returns the current database system timestamp as a datetime value without the database time zone offset. This value is derived from the operating system of the computer on which the instance of SQL Server is running.
Now, bearing in mind that computer clocks drift, I'd imagine that if the computer's time was corrected (either manually or automatically), the time reported by two successive calls to GETDATE() (that aren't part of a single query) could go backwards. All of the datetime methods ultimately rely on the computer's clock.
It's a shame you're not logging one of these GETDATE() results alongside the timing, so you could see if they all occurred at the same "time".