How to use batch separator inside Begin Try Catch SQL Server - sql

I'm trying to create the SQL Script which needs to revert all the schema changes made if the error comes under any single SQL query. However, I'm trying to join both table and procedure under the same .sql file to run and try catch the error if present. It is working when i only uses the table, but when i try to add the procedure, it has some issue so added GO to separate table and procedure. But I can't able to Combine Begin try with GO statement.
BEGIN TRANSACTION
BEGIN TRY
Create TABLE [dbo].[Persons_1](
[PersonID] [int] NULL,
[LastName] [varchar](255) NULL,
[FirstName] [varchar](255) NULL,
[Address] [varchar](255) NULL,
[City] [varchar](255) NULL
) ON [PRIMARY] ;
Create TABLE [dbo].[Persons_2](
[PersonID] [int] NULL,
[LastName] [varchar](255) NULL,
[FirstName] [varchar](255) NULL,
[Address] [varchar](255) NULL,
[City] [varchar](255) NULL
) ON [PRIMARY] ;
CREATE PROCEDURE [dbo].[AAAtest]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Select statements for procedure here
Select * from Persons_1 ;
END;
COMMIT
END TRY
BEGIN CATCH
ROLLBACK;
THROW; -- Only if you want reraise an exception (to determine the reason of the exception)
END CATCH

You don't need go statements in a batch. What you can do is use an exec statement. I added a drop table and drop proc so the program can be rerun as much as you want:
BEGIN TRANSACTION
BEGIN TRY
DROP TABLE IF EXISTS [dbo].[Persons_1] ;
Create TABLE [dbo].[Persons_1](
[PersonID] [int] NULL,
[LastName] [varchar](255) NULL,
[FirstName] [varchar](255) NULL,
[Address] [varchar](255) NULL,
[City] [varchar](255) NULL
) ON [PRIMARY] ;
DROP TABLE IF EXISTS DBO.PERSONS_2;
Create TABLE [dbo].[Persons_2](
[PersonID] [int] NULL,
[LastName] [varchar](255) NULL,
[FirstName] [varchar](255) NULL,
[Address] [varchar](255) NULL,
[City] [varchar](255) NULL
) ON [PRIMARY] ;
DROP PROC IF EXISTS DBO.AAATEST;
DECLARE #TSQL VARCHAR(4000);
SET #TSQL = '
CREATE PROCEDURE [dbo].[AAAtest]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Select statements for procedure here
Select * from Persons_1 ;
END;'
EXEC (#TSQL);
COMMIT
END TRY
BEGIN CATCH
ROLLBACK;
THROW; -- Only if you want reraise an exception (to determine the reason of the exception)
END CATCH

Related

Cannot update a timestamp column

I was recommended the following stored procedure to audit a login table.
CREATE PROCEDURE ApplicationLogin
#Username NVARCHAR(255),
#IpAddress NVARCHAR(255)
AS
BEGIN
DECLARE #UserID INT;
BEGIN TRANSACTION;
SET #UserID = (SELECT UserID FROM User WHERE Username = #Username);
IF #UserID > 0
BEGIN
UPDATE User
SET LastLogin = GETDATE()
WHERE UserID = #UserID;
END
INSERT INTO UserLogger (Username, UserID, TimeStamp)
VALUES (#Username, #UserID, #Timestamp);
COMMIT TRANSACTION;
SELECT #UserID
END
However I can't make it work for some syntax errors that I can't figure out.
The User table looks like this:
CREATE TABLE [dbo].[User]
(
[UserID] [INT] IDENTITY(1,1) NOT NULL,
[UserName] [VARCHAR](50) NOT NULL,
[Enabled] [BIT] NOT NULL,
[LastLogin] [TIMESTAMP] NOT NULL,
PRIMARY KEY CLUSTERED ([UserID] ASC)
)
The Audit table looks like this:
CREATE TABLE [dbo].[UserLogger]
(
[UserID] [INT] IDENTITY(1,1) NOT NULL,
[UserName] [VARCHAR](50) NOT NULL,
[Name] [VARCHAR](100) NULL,
[TS] [TIMESTAMP] NULL,
[IpAddress] [NCHAR](10) NULL
) ON [PRIMARY]
I get an error
Cannot update a timestamp column
which I don't see why.
Any idea?
timestamp is not what you think it is. It is some sort of internal representation of the row address. Here is an explanation.
Use datetime or datetime2 to fix your problem.
This is made all the more confusing, because CURRENT_TIMESTAMP doesn't return a timestamp.

How to update my table where updated columns names stored in another table in SQL Server

I have table User with n columns that stores user information in it.
I have another table User_Edit_Changes that I use to temporarily store changes to table User in it so that after admin confirmation I update the actual table User with new values.
In table User_Edit_Changes, I stored which user column requested for update and what is new value for that. How to write a dynamic query to get just changed value columns and new value from User_Edit_Changes and update the User table?
here is my sample create table command ,
teacher stores infos,
Tbl_ProfessorRequest stores edit change request,
Tbl_ProfessorEditInfoFields stores which fileds teacher request to edit
CREATE TABLE [dbo].[Teacher](
[code_ostad] [numeric](18, 0) NOT NULL,
[name] [varchar](30) NULL,
[family] [varchar](40) NOT NULL,
[namep] [varchar](30) NULL,
[idmadrak] [numeric](18, 0) NULL,
[namemadrak] [varchar](50) NULL,
[idresh] [numeric](18, 0) NULL,
[nameresh] [varchar](50) NULL,
[martabeh] [numeric](18, 0) NULL,
[namemartabeh] [varchar](30) NULL,
[nahveh_hamk] [numeric](18, 0) NULL,
CREATE TABLE [Request].[Tbl_ProfessorRequest](
[ProfessorRequestID] [int] IDENTITY(1,1) NOT NULL,
[Code_Ostad] [int] NULL,
[RequestTypeID] [bigint] NULL,
[RequestLogID] [bigint] NULL,
[CreateDate] [nvarchar](10) NULL,
[Note] [nvarchar](1000) NULL,
[term] [nvarchar](8) NULL,
[ProfessorMessage] [nvarchar](1000) NULL,
[Erae_Be] [nvarchar](100) NULL,
[ChangeSet] [int] NULL,
[isdeleted] [bit] NOT NULL,
[ScanImageUrl] [nvarchar](300) NULL,
CREATE TABLE [Request].[Tbl_ProfessorEditInfoFields](
[Id] [int] IDENTITY(1,1) NOT NULL,
[code_ostad] [int] NOT NULL,
[teacher_Column_Name] [nvarchar](200) NULL,
[OldValue] [nvarchar](200) NULL,
[NewValue] [nvarchar](200) NULL,
[State] [int] NOT NULL,
[ProfessorRequestID] [int] NOT NULL,
I'd say you have 3 options:
Handle the logic of updates outside the database, in what ever your application is built with. That's most likely the easiest way, since this kind of dynamic handling is not what databases are good at.
Build a dynamic SQL clause based on the contents of User_Edit_Changes. Loop through the changes in the table, construct an update statement into a variable and use sp_executesql to execute it. With cursor the code should be something like this:
set #params = N'#NewValue varchar(100)'
fetch next from yourcursor into #FieldName, #NewValue
while ##FETCH_STATUS = 0 begin
set #sql = 'update User set ' + #FieldName + ' = #NewValue'
exec sp_executesql #sql, #params, #NewValue = #NewValue
fetch next from yourcursor into #FieldName, #NewValue
end
Create static SQL statements for updating each of the columns. You can build something like this:
update U
set U.UserName = C.NewValue
from
User U
join User_Edit_Changes C on U.UserId = C.UserId
where
C.FieldName = 'UserName'
For this you of course need to have similar statements for each of your columns. You could build one massive update query with pivot or max+case, but handling the old and new values gets pretty complex.

Schema Changes history using t-sql

Is there any DMV or query to track the ddl changes to objects on sql server 2008r2 that incudes the LOGINNAME and SERVERNAME of the USER who altered the specific object
I'm trying to create a monitoring tool that includes this feature.
Please help!
Thank you very much!
You can create DDL trigger for your database like:
CREATE TABLE [dbo].[ddl_log](
[data] [XML] NULL,
[systemuser] [NVARCHAR](255) NULL,
[hostname] [NVARCHAR](255) NULL,
[date] [DATETIME] NULL,
[EventType] [NVARCHAR](255) NULL,
[SPID] [SMALLINT] NULL,
[SchemaName] [NVARCHAR](255) NULL,
[ObjectName] [NVARCHAR](255) NULL,
[ObjectType] [NVARCHAR](255) NULL,
[CommandText] [NVARCHAR](MAX) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TRIGGER [ddl_log_all]
ON DATABASE FOR DDL_DATABASE_LEVEL_EVENTS
AS
SET NOCOUNT ON
INSERT INTO ddl_log (data, systemuser, hostname, date, EventType, SPID, SchemaName, ObjectName, ObjectType, CommandText)
SELECT
EVENTDATA(), SYSTEM_USER, HOST_NAME(), GETDATE(),
EVENTDATA().value('(/EVENT_INSTANCE/EventType)[1]','nvarchar(max)'),
EVENTDATA().value('(/EVENT_INSTANCE/SPID)[1]','smallint'),
EVENTDATA().value('(/EVENT_INSTANCE/SchemaName)[1]','nvarchar(max)'),
EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(max)'),
EVENTDATA().value('(/EVENT_INSTANCE/ObjectType)[1]','nvarchar(max)'),
EVENTDATA().value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')
GO
ENABLE TRIGGER [ddl_log_all] ON DATABASE
GO
Then any change will be logged in table ddl_log.

How to call procedure for each row?

I have a Microsoft SQL Server R2 2008. And i see it first time in my life.
I have sql procedure:
DECLARE #RC int
DECLARE #Id uniqueidentifier
DECLARE #Segment_ID uniqueidentifier
DECLARE #SDate datetime
DECLARE #EDate datetime
DECLARE #withBig bit
DECLARE #withKm bit
DECLARE #withGeo bit
DECLARE #withDescr bit
-- TODO: задайте здесь значения параметров.
EXECUTE #RC = [Request_BusStation]
#Id
,#Segment_ID
,#SDate
,#EDate
,#withBig
,#withKm
,#withGeo
,#withDescr
GO
How i understand its just calling of procedure not thetself. But procedure too bit to copy it here.
AND have a table:
CREATE TABLE [BusStation](
[Id] [uniqueidentifier] NOT NULL,
[Segment_ID] [uniqueidentifier] NOT NULL,
[Dist] [decimal](18, 4) NOT NULL,
[Kod_Spr012] [smallint] NOT NULL,
[Square] [decimal](18, 6) NULL,
[OperationStartDate] [date] NULL,
[BallanceCost] [decimal](18, 6) NULL,
[DepreciatedCost] [decimal](18, 6) NULL,
[ChargesNorm] [decimal](18, 6) NULL,
[DocumentName] [varchar](100) NULL,
[DocumentNum] [varchar](100) NULL,
[DocumentDate] [date] NULL,
[Authority] [varchar](100) NULL,
[Kod_Spr091] [smallint] NOT NULL,
[HasWaysideStop] [bit] NOT NULL,
[HasLanding] [bit] NOT NULL,
[HasSpeedTransitArea] [bit] NOT NULL,
[LenSpeedTransitArea] [decimal](18, 6) NULL,
[YearBuilt] [smallint] NULL,
[YearMajorOverhaul] [smallint] NULL,
[Kod_Spr019] [smallint] NOT NULL,
[TechCond] [varbinary](max) NULL,
[LandCont] [varbinary](max) NULL,
[LandContDate] [date] NULL,
[LandContStartDate] [date] NULL,
[LandContEndDate] [date] NULL,
[Kod_Spr120] [smallint] NULL,
[E_Date_Begin] [datetime] NOT NULL,
[E_Date_End] [datetime] NULL,
[E_Date_Stop] [datetime] NULL,
Now i want to call this procedure for each row of table.
Its possible?
Yes, you can use a cursor that selects all the rows in the table and iteratively calls the stored procedure.
I would suggest that you may have a design issue before going down that route though. If the stored procedure needs to be called for every row in the table, you may be able to write a stored procedure that simply does what your current sp does to all the rows instead of a single row operation.
You have not provided what the sp is doing so I can only speculate here.
As mentioned in my comment, the only way I would know how to do that is using a CURSOR. Here is some sample code (untested of course):
DECLARE #ID INT
DECLARE #Segment_ID uniqueidentifier
DECLARE #getAccountID CURSOR
SET #BusStationCursor = CURSOR FOR
SELECT Id, Segment_ID --(etc: all the fields you need)
FROM BusStation
OPEN #BusStationCursor
FETCH NEXT FROM #BusStationCursor INTO #ID, #Segment_ID
WHILE ##FETCH_STATUS = 0
BEGIN
--CALL YOUR SP HERE
PRINT #ID
PRINT #Segment_ID
FETCH NEXT FROM #BusStationCursor INTO #ID, #Segment_ID
END
CLOSE #BusStationCursor
DEALLOCATE #BusStationCursor
This should help as well:
http://msdn.microsoft.com/en-us/library/ms180169.aspx
Good luck.

How to load image into varbinary column

I have table like this
CREATE TABLE [dbo].[Product](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
[Description] [nvarchar](max) NOT NULL,
[Price] [decimal](16, 3) NOT NULL,
[SmallPhoto] [varbinary](max) NULL);
I want load image into [SmallPhoto] field
Main script looks like this
declare #filePath varchar(max)
declare #productId int
set #filePath = 'D:\myfile.png'
set #productId = 1
update Product
set SmallPhoto = -- some actions
where Id = #productId
Expected result
Command(s) completed successfully. :)
UPD:
1) My database located in other machine
2) I do not have opportunity to copy file to remote machine