Alter then update causes an error - sql

I have simple script:
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.Columns
WHERE table_name = 'T1' AND column_name = 'C1')
BEGIN
ALTER Table T1
ADD C1 BIT NOT NULL CONSTRAINT DF_T1_C1 DEFAULT 0
UPDATE Table T1
SET C1 = 1
END
GO
I am getting error
Incorrect syntax near the keyword 'Table'.
I tried this solution but it didn't update column value. I came accross this but I think this is not my case as I don't want to catch exceptions or do any transaction. Do I have easy option to do this?
Putting GO seperator didn't help too.
As Joe Taras pointed out, I have changed my script but now getting error
Invalid column name 'C1'.

You need to ensure that that UPDATE isn't compiled until after you're actually created the column.
Put it in a separate context by using EXEC:
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.Columns
WHERE table_name = 'T1' AND column_name = 'C1')
BEGIN
ALTER Table T1
ADD C1 BIT NOT NULL CONSTRAINT DF_T1_C1 DEFAULT 0
EXEC('UPDATE Table T1
SET C1 = 1')
END
GO

Your row:
UPDATE Table T1 SET C1 = 1
has wrong because you have specified table keyword.
The correct syntax is:
UPDATE T1 SET C1 = 1
EDIT 1
Rewrite your script as follow, so after GO separator you'll update your field, so you are sure the DDL has taken by DBMS:
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.Columns
where table_name = 'T1' AND column_name = 'C1')
BEGIN
ALTER Table T1
ADD C1 BIT NOT NULL CONSTRAINT DF_T1_C1 DEFAULT 0
END
GO
UPDATE T1 SET C1 = 1
EDIT 2
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.Columns
where table_name = 'T1' AND column_name = 'C1')
BEGIN
ALTER Table T1
ADD C1 BIT NOT NULL CONSTRAINT DF_T1_C1 DEFAULT 0
EXEC('UPDATE T1 SET C1 = 1')
END
GO

if there is no entry in the table update cannot be done
try it after giving asingle entry it works

Related

Multiple Rows with in SQL INSTEAD of Trigger

looking a bit of direction or guidance as to why I’m getting multiple rows using my trigger. Basically I have a web app that controls Asset Types (i.e Laptops, Phones etc), what I’m trying to do with this trigger is when the Asset Type Name (at_typedesc) changes that I log to an audit table (in this case sql_log) what the old name was and what the new name is.
This is working, but for some reason I get multiple lines written at the INSERT TO SQL_LOG statement. It does write the old name & new name, but then I’ll get 3 additional rows which has the old name showing the new name...
This is currently on a 2008 SQL Server.
-- create the trigger
go
create trigger trg_InsteadOfUpdate on [dbo].[lkp_asset_types]
instead of update
as
begin
DECLARE #triggerAction varchar(1)
-- determine the TRIGGER action
-- this allows us to tell if its an INSERT or an UPDATE
SELECT #triggerAction = CASE
WHEN EXISTS(SELECT 1 FROM INSERTED)
AND EXISTS(SELECT 1 FROM deleted) THEN 'U'
WHEN EXISTS(SELECT 1 FROM inserted) THEN 'I'
ELSE 'D' END;
-- get the orginally asset name from the DELETED table
-- this contains the rows as they were BEFORE the UPDATE Statement
DECLARE #orgAssetTypeName varchar(255)
SET #orgAssetTypeName = (SELECT top 1 at_typedesc from lkp_asset_types WHERE at_id = (select at_id from deleted))
-- UPDATE to the new asset name based on the NEW value in the INSERTED Table
update lkp_asset_types
set at_typedesc = (select at_typedesc from inserted)
where at_id = (select at_id from inserted)
-- get the new asset name from the INSERTED table
-- this contains the rows as they were AFTER the UPDATE Statement
DECLARE #newAssetTypeName varchar(255)
SET #newAssetTypeName = (SELECT top 1 at_typedesc from lkp_asset_types WHERE at_id = (select at_id from inserted))
insert into sql_log
(sql_log)
values ('SQL PRE Changed from : ' + #orgAssetTypeName + ' to: ' + #newAssetTypeName + '. Action = ' + #triggerAction)
end
go
Logic like this in a trigger in SQL Server is just broken:
where at_id = (select at_id from inserted)
I really wish the SQL Server parser issued a warning when encountering such constructs.
There is no guarantee that inserted has only one value (nor deleted).
That is how SQL Server defines triggers: as set operations. If multiple rows are inserted at the same, then the inserted and deleted "tables" have multiple rows.
That part is simple. You will need to rewrite the trigger to take this into account.
On checking my web code the update button was performing additional updates, this caused the trigger to fire more than once thus causing duplicate rows.
create table dbo.lkp_asset_types_test
(
at_id int identity,
at_typedesc varchar(100)
)
go
create trigger trg_InsteadOfUpdate_test on [dbo].[lkp_asset_types_test]
instead of update
as
begin
select 'trigger fired!!!!'
if not exists(select * from inserted)
and not exists(select * from deleted)
begin
return;
end
--update (maybe only the diffs?)
update t
set at_typedesc = i.at_typedesc
from dbo.lkp_asset_types_test as t
join inserted as i on t.at_id = i.at_id;
--where t.at_typedesc <> i.at_typedesc & nulls??
--insert into sql_log(sql_log)
select
'SQL PRE Changed from : ' + isnull(d.at_typedesc, '*null*') + ' to: ' + isnull(i.at_typedesc, '*null*') + '. Action = U'
from inserted as i
join deleted as d on i.at_id = d.at_id
--where i.at_typedesc <> d.at_typedesc & nulls ??
end
go
insert into dbo.lkp_asset_types_test(at_typedesc) values ('A'), ('B'), ('C'), ('D'), (NULL);
go
update dbo.lkp_asset_types_test
set at_typedesc = case when at_id%2=0 then isnull(at_typedesc, 'X') else isnull(at_typedesc, '') + 'xyz' end
go
select *
from dbo.lkp_asset_types_test;
go
update dbo.lkp_asset_types_test
set at_typedesc = case when at_id%2=0 then at_typedesc else at_typedesc + 'xyz' end
where 1=2
go
--
drop table lkp_asset_types_test;

ERROR: column "EP_TEXT" specified more than once

I try to use script to add column to table, no matter i change to which name, it always return error saying
Column names in each table must be unique. Column name 'xxx' in table
'TEST_TABLE' is specified more than once.
I randomly key in a name, it still saying that name is specified more than once. but actually not, there is no such column existed yet, and column finally is not added successfully also.
anyone had any idea?
thanks
below is my query:
GO
if not exists(SELECT * FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'TEST_TABLE' AND COLUMN_NAME = 'EP_TEXT')
begin
ALTER TABLE TEST_TABLE ADD EP_TEXT [dbo].[UDDT_MAXVARCHAR] NULL
end
GO
The problem is that INFORMATION_SCHEMA.TABLE does not have a COLUMN_NAME column. Try this:
IF NOT EXISTS(
SELECT * from INFORMATION_SCHEMA.TABLES t1
INNER JOIN INFORMATION_SCHEMA.COLUMNS t2 on t1.TABLE_NAME = t2.TABLE_NAME
where t1.TABLE_NAME = 'TEST_TABLE' and t2.COLUMN_NAME = 'EP_TEXT'
)

How to identify the operation type(insert,update,delete) in SQL Server trigger

We are using the following trigger in SQL Server to maintain the history now I need to identify the operations just like insert,update or delete. I found some information HERE but it doesn't works with the SQL Server.
CREATE TRIGGER audit_guest_details ON [PMS].[GSDTLTBL]
FOR INSERT,UPDATE,DELETE
AS
DECLARE #SRLNUB1 INT;
DECLARE #UPDFLG1 DECIMAL(3,0);
SELECT #SRLNUB1 = I.SRLNUB FROM inserted I;
SELECT #UPDFLG1 = I.UPDFLG FROM inserted I;
BEGIN
/* Here I need to identify the operation and insert the operation type in the GUEST_ADT 3rd field */
insert into dbo.GUEST_ADT values(#SRLNUB1,#UPDFLG1,?);
PRINT 'BEFORE INSERT trigger fired.'
END;
GO
But here I need to identify the operation and want to insert operation type accordingly.
Here I don't want to create three trigger for every operations
For Inserted : Rows are in inserted only.
For Updated: Rows are in inserted and deleted.
For Deleted: Rows are in deleted only.
DECLARE #event_type varchar(42)
IF EXISTS(SELECT * FROM inserted)
IF EXISTS(SELECT * FROM deleted)
SELECT #event_type = 'update'
ELSE
SELECT #event_type = 'insert'
ELSE
IF EXISTS(SELECT * FROM deleted)
SELECT #event_type = 'delete'
ELSE
--no rows affected - cannot determine event
SELECT #event_type = 'unknown'
This is a simplified version of Mikhail's answer that uses a searched CASE expression.
DECLARE #Operation varchar(7) =
CASE WHEN EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted)
THEN 'Update'
WHEN EXISTS(SELECT * FROM inserted)
THEN 'Insert'
WHEN EXISTS(SELECT * FROM deleted)
THEN 'Delete'
ELSE
NULL --Unknown
END;
Since you can get multiple rows at once we do it as follows.
INSERT INTO Log_Table
(
LogDate
,LogAction
-- your field list here
,Field0
-- Example : Tracking new and old value for a specific field
-- Make sure that the [Field1_Old] is nullable or has a default value
,Field1,Field1_Old
)
SELECT
LogDate=GETDATE()
,LogAction = CASE WHEN d.[PK_Field] IS NULL THEN 'I' ELSE 'U' END
,i.Field0
,i.Field1, d.Field1
FROM inserted i
LEFT JOIN deleted d on i.[PK_Field]=d.[PK_Field]
WHERE i.[PK_Field] IS NOT NULL
INSERT INTO Log_Table
(
LogDate
,LogAction
-- your field list here
,Field0
-- Example : Tracking new and old value for a specific field
-- Make sure that the [Field1_Old] is nullable or has a default value
,Field1,Field1_Old
)
SELECT
LogDate=GETDATE()
,LogAction = 'D'
,d.Field0
,d.Field1, NULL
FROM deleted d
LEFT JOIN inserted i on i.[PK_Field]=d.[PK_Field]
WHERE i.[PK_Field] IS NULL
create trigger my_trigger on my_table
after update , delete , insert
as
declare #inserting bit
declare #deleting bit
declare #updating bit = 0
select #inserting = coalesce (max(1),0) where exists (select 1 from inserted)
select #deleting = coalesce (max(1),0) where exists (select 1 from deleted )
select #inserting = 0
, #deleting = 0
, #updating = 1
where #inserting = 1 and #deleting = 1
print 'Inserting = ' + ltrim (#inserting)
+ ', Deleting = ' + ltrim (#deleting)
+ ', Updating = ' + ltrim (#updating)
If all three are zero, there are no rows affected and I think there is no way to tell whether it is an update/delete/insert.

How do you reference new columns immediately after they are created in your SQL script

How do I need to write my SQL script to ensure my new column is visible on following lines after it is created.
This is the general form of my SQL:
BEGIN TRANSACTION
if (not exists(select 1 from THIS_TABLE))
BEGIN
ALTER TABLE THIS_TABLE add THIS_COLUMN int
END
COMMIT
BEGIN TRANSACTION
IF (NOT EXISTS (SELECT 1 FROM THIS_TABLE
WHERE THIS_COLUMN = 1))
BEGIN
UPDATE THIS_TABLE SET THIS_COLUMN = 1
END
COMMIT
This is the error I'm getting:
Invalid column name 'THIS_COLUMN'.
on this line:
IF (NOT EXISTS (SELECT 1 FROM THIS_TABLE
WHERE THIS_COLUMN = 1))
The column has to be created before a query that uses it can be parsed. You can accomplish this by putting the update in a different batch, using the "go" keyword:
alter table t1 add c1 int
go
update t1 set c1 = 1
Or by running the second transaction as dynamic SQL:
alter table t1 add c1 int
exec ('update t1 set c1 = 1')
What Andomar said is correct, you need to use the go keyword.
However, the big problem is that your logic looks wrong. Let me go through each use case:
If THIS_TABLE is not empty
If the table is not empty, the if below returns false and you will never add the new column.
if (not exists(select 1 from THIS_TABLE))
BEGIN
ALTER TABLE THIS_TABLE add THIS_COLUMN int
END
Then, the next script obviously fails, because there is no such column THIS_COLUMN:
IF (NOT EXISTS (SELECT 1 FROM THIS_TABLE
WHERE THIS_COLUMN = 1))
If THIS_TABLE is empty
If the table is empty, the column is added:
if (not exists(select 1 from THIS_TABLE))
BEGIN
ALTER TABLE THIS_TABLE add THIS_COLUMN int
END
But then the next if will always be true and the update statement will affect zero rows (because table is empty).
IF (NOT EXISTS (SELECT 1 FROM THIS_TABLE
WHERE THIS_COLUMN = 1))
BEGIN
UPDATE THIS_TABLE SET THIS_COLUMN = 1
END

SQL: How to use a column that was just added

I am trying to add 2 columns and then I would like to set some values to them but I get a compile-error saying the column does not exist. I am using the following script:
IF #LogProcessed = 0
Begin
IF NOT EXISTS (select column_name from INFORMATION_SCHEMA.columns where table_name = 'SYSTM_FRM' and column_name = 'SF_Ip_TXT')
ALTER TABLE SYSTM_FRM add SF_Ip_TXT NVARCHAR(20)
IF NOT EXISTS (select column_name from INFORMATION_SCHEMA.columns where table_name = 'SYSTM_FRM' and column_name = 'SF_Port_NUM')
ALTER TABLE SYSTM_FRM add SF_Port_NUM int
IF (EXISTS (select column_name from INFORMATION_SCHEMA.columns where table_name = 'FRM' and column_name = 'FRM_Ip_TXT') AND
EXISTS (select column_name from INFORMATION_SCHEMA.columns where table_name = 'FRM' and column_name = 'FRM_Ip_TXT'))
begin
Update dbo.SYSTM_FRM
SET dbo.SYSTM_FRM.SF_Ip_TXT = dbo.FRM.FRM_Ip_TXT,
dbo.SYSTM_FRM.SF_Port_NUM = dbo.FRM.FRM_Port_NUM
FROM dbo.FRM INNER JOIN dbo.SYSTM_FRM ON dbo.FRM.FRM_RCRD_NUM = dbo.SYSTM_FRM.SF_FrameRecord_NUM
ALTER TABLE FRM DROP COLUMN FRM_Ip_TXT
ALTER TABLE FRM DROP COLUMN FRM_Port_NUM
end
Update [Update_Log]
Set Update_Log_Processed = 1
Where [Update_Log_Version] = '00001'
end
Is there any way to use a column that I am adding in the same script?
you cannot save it in a proc like that
example
create table TestAdd2 (id int)
go
You cannot create this procedure
create proc prTest as
insert TestAdd2 values (1)
exec ('ALTER TABLE TestAdd2 add SF_Port_NUM int')
update TestAdd2 set id = 1,SF_Port_NUM = 2
select * from TestAdd2
GO
You get this error
Msg 207, Level 16, State 1, Procedure prTest, Line 7
Invalid column name 'SF_Port_NUM'.
This is because at parse time the column does not exist
However if you use dynamic SQL for the update you are good to go
create proc prTest2 as
insert TestAdd2 values (1)
exec ('ALTER TABLE TestAdd2 add SF_Port_NUM int')
exec ('update TestAdd2 set id = 1,SF_Port_NUM = 2')
select * from TestAdd2
GO
in your case your update statement would be
exec('Update dbo.SYSTM_FRM
SET dbo.SYSTM_FRM.SF_Ip_TXT = dbo.FRM.FRM_Ip_TXT,
dbo.SYSTM_FRM.SF_Port_NUM = dbo.FRM.FRM_Port_NUM
FROM dbo.FRM INNER JOIN dbo.SYSTM_FRM
ON dbo.FRM.FRM_RCRD_NUM = dbo.SYSTM_FRM.SF_FrameRecord_NUM')
Put a GO in between
All DDL statements should follow the statement seperator GO in order to make use of it