Update Statement Incorrectly Applying - sql-server
I am trying to update 45000 rows with missing values on SQL Server 2000 sp4 Database.
I will try to simulate the table setup and conditions of values below:
I have two tables. One is holding valid transactions and the other has missing values in certain rows.
--Table #Trans holds valid transactions
Create Table #trans (
[DocumentNumber] [char](21) NOT NULL,
[CustomerName] [char](21) NOT NULL,
[CustomerID] [char](31) NOT NULL,
[ACTINDX] [int] NOT NULL,
[CRDTAMNT] [numeric](19, 5) NOT NULL,
[DEBITAMT] [numeric](19, 5) NOT NULL,
[TRXSORCE] [char](30) NOT NULL,
[TRXDATE] [datetime] NOT NULL
)
INSERT INTO #trans ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[TRXSORCE],[TRXDATE])
Values('INV20123','Andrew Sesinyi','A0001',2501,25620.00,0.000,'SALESTRN15012015','15-Jan-2015')
INSERT INTO #trans ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[TRXSORCE],[TRXDATE])
Values('INV20123','Andrew Sesinyi','A0001',2201,0.000,25620.00,'SALESTRN15012015','15-Jan-2015')
INSERT INTO #trans ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[TRXSORCE],[TRXDATE])
Values('PMTRN00155','Bame Moonwa','B0001',1700,1550.00,0.0000,'PYMNTTRN17012015','17-Jan-2015')
INSERT INTO #trans ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[TRXSORCE],[TRXDATE])
Values('PMTRN00155','Bame Moonwa','B0001',1900,0.0000,1550.00,'PYMNTTRN17012015','17-Jan-2015')
INSERT INTO #trans ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[TRXSORCE],[TRXDATE])
Values('PMTRN00156','OLERATO PHAMA','OL0001',1900,0.0000,1020.00,'PYMNTTRN17012015','17-Jan-2015')
INSERT INTO #trans ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[TRXSORCE],[TRXDATE])
Values('PMTRN00156','OLERATO PHAMA','OL0001',1700,1020.00,0.0000,'PYMNTTRN17012015','17-Jan-2015')
INSERT INTO #trans ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[TRXSORCE],[TRXDATE])
Values('INV20124','Bame Moonwa','B0001',2501,18000.00,0.000,'SALESTRN15012015','15-Jan-2015')
INSERT INTO #trans ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[TRXSORCE],[TRXDATE])
Values('INV20124','Bame Moonwa','B0001',2201,0.000,18000.00,'SALESTRN15012015','15-Jan-2015')
--Tables #GL holds some of the transactions with missing values i.e --DocumentNumber,CustomerID A and CustomerName
Create Table #GL(
[DocumentNumber] [char](21) ,
[CustomerName] [char](21),
[CustomerID] [char](31),
[ACTINDX] [int] NOT NULL,
[CRDTAMNT] [numeric](19, 5) NOT NULL,
[DEBITAMT] [numeric](19, 5) NOT NULL,
[ORTRXSORCE] [char](30) NOT NULL,
[TRXDATE] [datetime] NOT NULL
)
INSERT INTO #GL ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[ORTRXSORCE],[TRXDATE])
Values('','Andrew Sesinyi','A0001',2501,25620.00,0.000,'SALESTRN15012015','15-Jan-2015')
INSERT INTO #GL ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[ORTRXSORCE],[TRXDATE])
Values('','Andrew Sesinyi','A0001',2201,0.000,25620.00,'SALESTRN15012015','15-Jan-2015')
INSERT INTO #GL ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[ORTRXSORCE],[TRXDATE])
Values('','Bame Moonwa','B0001',1700,1550.00,0.0000,'PYMNTTRN17012015','17-Jan-2015')
INSERT INTO #GL ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[ORTRXSORCE],[TRXDATE])
Values('','Bame Moonwa','B0001',1900,0.0000,1550.00,'PYMNTTRN17012015','17-Jan-2015')
INSERT INTO #GL ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[ORTRXSORCE],[TRXDATE])
Values('','','',1900,0.0000,1020.00,'PYMNTTRN17012015','17-Jan-2015')
INSERT INTO #GL ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[ORTRXSORCE],[TRXDATE])
Values('','','',1700,1020.00,0.0000,'PYMNTTRN17012015','17-Jan-2015')
INSERT INTO #GL ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[ORTRXSORCE],[TRXDATE])
Values('INV20124','','',2501,18000.00,0.000,'SALESTRN15012015','15-Jan-2015')
INSERT INTO #GL ([DocumentNumber],[CustomerName],[CustomerID],[ACTINDX],[CRDTAMNT],[DEBITAMT],[ORTRXSORCE],[TRXDATE])
Values('INV20124','','',2201,0.000,18000.00,'SALESTRN15012015','15-Jan-2015')
When I run the following update statement incorrect updates are rendered to the #GL Records. Is there an alternative method or better way to apply an update records in this scenario.
UPDATE #GL
SET DocumentNumber = TR.DocumentNumber
, CustomerName = TR.CustomerName
,CustomerID =TR.CustomerID
FROM #GL GL
INNER JOIN #trans TR ON GL.ORTRXSORCE = TR.TRXSORCE
WHERE GL.ACTINDX = TR.ACTINDX
AND GL.DEBITAMT = TR.DEBITAMT
OR GL.CRDTAMNT = TR.CRDTAMNT
AND GL.TRXDATE = TR.TRXDATE
N.B I am trying to use this method to update 45000 records that have missing values.
N.B Multiple transactions can be posted as a batch to the #GL hence the shared TRXSORCE
Many Thanks for your insight in advance.
I bet you mean
WHERE GL.ACTINDX = TR.ACTINDX
AND (GL.DEBITAMT = TR.DEBITAMT OR GL.CRDTAMNT = TR.CRDTAMNT)
AND GL.TRXDATE = TR.TRXDATE
Related
After insert trigger doesn't work when using Inserted
I'm trying to write a trigger on my Employees table that should not allow the insertion of a new employee that has a hire date that is older than the hire date of his boss CREATE TABLE [dbo].[Employees] ( [EID] [int] IDENTITY(1,1) NOT NULL, [Ename] [nvarchar](20) NOT NULL, [Gender] [nvarchar](1) NOT NULL, [IsMarried] [nvarchar](1) NOT NULL, [Birthdate] [date] NOT NULL, [HireDate] [date] NOT NULL, [Salary] [float] NOT NULL, [Notes] [nvarchar](200) NULL, [NationalityID] [int] NULL, [BossID] [int] NULL, CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED () ) And here's the trigger code: CREATE TRIGGER [dbo].[Trig_04] ON [dbo].[Employees] AFTER INSERT AS BEGIN IF ((SELECT INSERTED.HireDate FROM INSERTED WHERE BossID <> EID) < (SELECT Employees.HireDate FROM Employees WHERE EID IN (SELECT Employees.BossID FROM Employees WHERE BossID <> EID))) ROLLBACK END It executes normally (no errors) but it just doesn't work, but when I was using the employees table in the subquery instead of the inserted table, it was working normally. Does anyone have an answer for this?
You have to write triggers in SQL Server to handle the fact that INSERTED could contain multiple records. You cannot assume it will only be a single record. I think the following is what you are looking for: if exists ( select 1 from Inserted I where I.BossID <> I.EID and I.HireDate < (select E.HireDate from Employees E where E.EID = I.BossID) ) begin ROLLBACK; end
After Trigger for Update and Insert failing
I am writing a trigger for keeping audit record for one table for Insert and Update records. CREATE TABLE [dbo].[AppLog]( [TableName] [varchar](32) NOT NULL, [ColumnName] [varchar](32) NOT NULL, [RecordId] [varchar](20) NOT NULL, [OldValue] [varchar](2000) NULL, [NewValue] [varchar](2000) NULL, [UpdatedBy] [varchar](200) NULL, [UpdatedOn] [datetime] NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[Persons]( [Personid] [int] IDENTITY(1,1) NOT NULL, [LastName] [varchar](255) NOT NULL, [FirstName] [varchar](255) NULL, [Age] [int] NULL ) ON [PRIMARY] GO CREATE TRIGGER AuditRecord ON dbo.Persons AFTER UPDATE, INSERT AS INSERT INTO AppLog (TableName ,ColumnName ,RecordId ,OldValue ,NewValue ,UpdatedBy ,UpdatedOn ) SELECT 'Persons', 'LastName', COALESCE(i.Personid,NULL), d.LastName, i.LastName, CURRENT_USER, GETDATE() FROM Persons pv LEFT JOIN INSERTED i ON pv.Personid = i.Personid LEFT JOIN DELETED d ON pv.Personid = d.Personid; GO INSERT INTO Persons (FirstName,LastName,age) VALUES ('Satish','Parida',40); INSERT INTO Persons (FirstName,LastName,age) VALUES ('SKP','Tada',90); The last insert is failing as it is trying to insert null to recordid column in applog table, could someone explain or fix the issue.
The statement that is failing is the one below: INSERT INTO Persons (FirstName,LastName,age) VALUES ('SKP','Tada',90); This is because in your Trigger you are using Persons are your "base" table and the performing a LEFT JOIN to both inserted and deleted. As result when you try to perform the above INSERT, values from the previous INSERT are used as well in the trigger's dataset. The person 'Parida' doesn't appear in the table inserted for your second INSERT, and so COALESCE(i.Personid,NULL) returns NULL; as I mentioned in my comment, there' no point using COALESCE to return NULL, as if an expression's value evaluates to NULL it will return NULL. As RecordID (which is what COALESCE(i.Personid,NULL) is being inserted into) can't have the value NULL the INSERT fails and the whole transaction is rolled back. I suspect that what you want for your trigger is the below: CREATE TRIGGER AuditRecord ON dbo.Persons AFTER UPDATE, INSERT AS BEGIN INSERT INTO AppLog (TableName, ColumnName, RecordId, OldValue, NewValue, UpdatedBy, UpdatedOn) SELECT 'Persons', 'LastName', i.Personid, d.LastName, i.LastName, CURRENT_USER, GETDATE() FROM inserted AS i LEFT JOIN deleted AS d ON i.Personid = d.Personid; END; inserted will always have at least 1 row for an UPDATE or an INSERT. inserted would not for a DELETE, but your trigger won't fire on that DML event so using inserted as the "base" table seems the correct choice.
Following code should work CREATE TABLE [dbo].[AppLog]( [TableName] [varchar](32) NOT NULL, [ColumnName] [varchar](32) NOT NULL, [RecordId] [varchar](20) NOT NULL, [OldValue] [varchar](2000) NULL, [NewValue] [varchar](2000) NULL, [UpdatedBy] [varchar](200) NULL, [UpdatedOn] [datetime] NULL ) ON [PRIMARY] GO CREATE TABLE [dbo].[Persons]( [Personid] [int] IDENTITY(1,1) NOT NULL, [LastName] [varchar](255) NOT NULL, [FirstName] [varchar](255) NULL, [Age] [int] NULL ) ON [PRIMARY] GO CREATE TRIGGER AuditRecord ON dbo.Persons AFTER UPDATE, INSERT AS if exists(SELECT * from inserted) and exists (SELECT * from deleted) BEGIN INSERT INTO AppLog (TableName ,ColumnName ,RecordId ,OldValue ,NewValue ,UpdatedBy ,UpdatedOn ) SELECT 'Persons', 'LastName', COALESCE(i.Personid,NULL), d.LastName, i.LastName, CURRENT_USER, GETDATE() FROM Persons pv INNER JOIN INSERTED i ON pv.Personid = i.Personid INNER JOIN DELETED d ON pv.Personid = d.Personid; END If exists (Select * from inserted) and not exists(Select * from deleted) begin INSERT INTO AppLog (TableName ,ColumnName ,RecordId ,OldValue ,NewValue ,UpdatedBy ,UpdatedOn ) SELECT 'Persons', 'LastName',i.Personid,NULL,i.LastName,CURRENT_USER,GETDATE() FROM inserted AS i END GO INSERT INTO Persons (FirstName,LastName,age) VALUES ('Satish','Parida',40); INSERT INTO Persons (FirstName,LastName,age) VALUES ('SKP','Tada',90); INSERT INTO Persons (FirstName,LastName,age) VALUES ('abc','def',90); INSERT INTO Persons (FirstName,LastName,age) VALUES ('gg','hh',90); UPDATE dbo.Persons SET LastName='Paridachanged' WHERE Personid=1 SELECT * FROM Persons SELECT * FROM AppLog
USE KnockKnockDev; GO IF OBJECT_ID('dbo.AuditRecord', 'TR') IS NOT NULL DROP TRIGGER dbo.AuditRecord; GO CREATE TRIGGER AuditRecord ON dbo.Persons AFTER UPDATE, INSERT, DELETE AS DECLARE #Action as char(1) DECLARE #Count as int DECLARE #TableName as char(32) DECLARE #ColumnName as char (32) SET #TableName = 'Persons' SET #ColumnName = 'LastName' SET #Action = 'I' -- Set Action to 'I'nsert by default. SELECT #Count = COUNT(*) FROM DELETED IF #Count > 0 BEGIN SELECT #Count = COUNT(*) FROM INSERTED IF #Count > 0 SET #Action = 'U' -- Set Action to 'U'pdated. ELSE SET #Action = 'D' -- Set Action to 'D'eleted. END IF #Action = 'I' BEGIN INSERT INTO AppLog (TableName ,ColumnName ,RecordId ,OldValue ,NewValue ,Action ,UpdatedBy ,UpdatedOn ) SELECT #TableName, #ColumnName, Personid, NULL, LastName, #Action, CURRENT_USER, GETDATE() FROM INSERTED; END ELSE IF #Action = 'D' BEGIN INSERT INTO AppLog (TableName ,ColumnName ,RecordId ,OldValue ,NewValue ,Action ,UpdatedBy ,UpdatedOn ) SELECT #TableName, #ColumnName, Personid, LastName, NULL, #Action, CURRENT_USER, GETDATE() FROM DELETED; END ELSE BEGIN INSERT INTO AppLog (TableName ,ColumnName ,RecordId ,OldValue ,NewValue ,Action ,UpdatedBy ,UpdatedOn ) SELECT #TableName, #ColumnName, i.Personid, d.LastName, i.LastName, #Action, CURRENT_USER, GETDATE() FROM Persons pv INNER JOIN INSERTED i ON pv.Personid = i.Personid INNER JOIN DELETED d ON pv.Personid = d.Personid; END GO
UPDATE doesn't allow to INSERT null in FK field
I have a SQL Server database and in a table there's a lookup column which is a "nullable FK" linked to a master table. There's a data import process in which we fetch data from table01_staging into table01. This is the UPDATE statement which is failing (if CarrierID is null) - DECLARE #code as nvarchar(10); SET #code = 'xyz'; UPDATE table01 SET CarrierID = t2.CarrierID FROM table01 AS t1 INNER JOIN table01_staging AS t2 ON t1.Code = #code; WHERE t1.Code = #code; It goes fine if the CarrierID is not null. In fact I can successfully execute: UPDATE table01 SET CarrierID = null WHERE t1.Code = 'xyz'; So setting null is not the problem but it doesn't work when its updated from the staging table which has a null value. How can I make it right? Error : The UPDATE statement conflicted with the FOREIGN KEY constraint FK_table01_MasterCarrier. The conflict occurred in database table01, table dbo.MasterCarrier, column ID. > EDIT 02: DONE!. Updated the first SQL to show my variable usage - which I believe was the culprit. Correcting the JOIN operation as follows with a COLLATion conversion error fix makes it work - DECLARE #code as nvarchar(10); SET #code = 'xyz'; UPDATE table01 SET CarrierID = t2.CarrierID FROM table01 AS t1 INNER JOIN table01_staging AS t2 ON t1.Code = t2.code COLLATE SQL_Latin1_General_CP1_CI_AS WHERE t1.Code = #code; Thank you all, esp. Zhang. I deserve a -1 for not being able to understand the JOIN clause properly :-)
Does table01_staging have the same Foreign Key Constraint on CarrierID? If not, check whether there are some CarrierID not existing in MasterCarrier table. select * from table01_staging t where not exists (select 1 from MasterCarrier m where m.ID = t.CarrierID) !!UPDATE!! I created the sample table and data. It worked without any issue. CREATE TABLE [dbo].MasterCarrier( [id] [int] IDENTITY NOT NULL, [NAME] [varchar](10) NULL, CONSTRAINT [PK_mastertable] PRIMARY KEY CLUSTERED ( [id] ASC ) ) GO CREATE TABLE [dbo].[table01]( [id] [int] NOT NULL, [CarrierID] [int] NULL, [Code] [varchar](10) NULL ) GO ALTER TABLE [dbo].table01 WITH CHECK ADD CONSTRAINT [FK_table01_MasterCarrier] FOREIGN KEY([CarrierID]) REFERENCES [dbo].[MasterCarrier] ([id]) GO ALTER TABLE [dbo].table01 CHECK CONSTRAINT [FK_table01_MasterCarrier] GO CREATE TABLE [dbo].[table01_staging]( [id] [int] NOT NULL, [CarrierID] [int] NULL, [Code] [varchar](10) NULL ) GO --Insert sample data INSERT INTO MasterCarrier (NAME) VALUES ('carrier'); INSERT INTO table01 (id, CarrierID, Code) VALUES (1, 1, 'abc'), (2, NULL, 'abc'),(3, 1, 'ddd'); INSERT INTO table01_staging (id, CarrierID, Code) VALUES (1, 1, 'abc'), (2, NULL, 'abc'),(3, 1, 'ddd'); UPDATE table01 SET CarrierID=t2.CarrierID FROM table01 AS t1 INNER JOIN table01_staging AS t2 ON t1.ID = t2.ID WHERE t1.Code='abc' It gets (2 rows affected) message.
TRIGGER AFTER INSERT SELECT MIN(COUNT) insert ID
I'm trying to create a trigger after an insert on the eventss table. The trigger should select the Bcoordinator_ID from the bookingCoordinator table where they have the minimum number of occurrences in the eventss table. Here's my table data followed by the trigger. It doesn't like the minCount in the values, I think it's looking for and int. DROP TABLE eventsBooking CREATE TABLE eventsBooking ( EBK INT NOT NULL IDENTITY(100, 1), booking_ID AS 'EBK'+CAST( ebk as varchar(10)) PERSISTED PRIMARY KEY, bookingDate DATE, Bcoordinator_ID VARCHAR (20), eventss_ID VARCHAR (20) NOT NULL ) INSERT INTO eventsBooking VALUES ('2015-01-07 11:23:00', NULL, 'EVT100'); Eventss table: EVT INT NOT NULL IDENTITY(100, 1), eventss_ID AS 'EVT' + CAST(evt as varchar(10)) PERSISTED PRIMARY KEY, eventsName varchar(50), noOfStages SMALLINT, noOfRounds SMALLINT, eventsDate DATE, entryFee DECIMAL (7,2), venue_ID VARCHAR (20) NOT NULL, judges_ID VARCHAR (20) INSERT INTO eventss VALUES ('Swimming Gala 2015', '3', '7', '2015-01-07 09:00:00', '35.00', 'VEN101', 'JUD100'); CREATE TABLE bookingCoordinator ( BCO INT NOT NULL IDENTITY(100, 1), Bcoordinator_ID AS 'BCO'+CAST( bco as varchar(10)) PERSISTED PRIMARY KEY, forename varchar(20) NOT NULL, familyName varchar(50) ) INSERT INTO bookingCoordinator VALUES ('Steve', 'Wills'); Trigger: CREATE TRIGGER TRGinsertJudge ON [dbo].[eventss] AFTER INSERT AS BEGIN SET NOCOUNT ON; INSERT INTO dbo.eventsBooking (Bcoordinator_ID, bookingDate, Eventss_ID) VALUES(minCount, getdate(), 100) SELECT MIN(COUNT(Bcoordinator_ID)) AS minCount FROM eventsBooking END
You can't do an aggregation of an aggregation i.e. MIN(COUNT(1)) If you just want the Bcoordinatior_ID with the least counts in eventsBooking, do this select top 1 bcoordinator_id from eventsBooking group by bcoordinator_id order by count(1) asc And you don't use VALUES() in an INSERT INTO ... SELECT statement Also, in your current code, since eventsBooking.bcoordinator_id is always null, you need to join to the actual table of bookingCoordinators to return booking coordinators without any events booked. So your complete trigger statement should be INSERT INTO dbo.eventsBooking (Bcoordinator_ID, bookingDate, Eventss_ID) select top 1 bookingcoordinator.bcoordinator_id, getdate(), 100 from bookingCoordinator left join eventsBooking on bookingCoordinator.Bcoordinator_ID = eventsBooking.Bcoordinator_ID group by bookingcoordinator.bcoordinator_id order by count(1) asc
Persisted computed column with subquery
I have something like this create function Answers_Index(#id int, #questionID int) returns int as begin return (select count([ID]) from [Answers] where [ID] < #id and [ID_Question] = #questionID) end go create table Answers ( [ID] int not null identity(1, 1), [ID_Question] int not null, [Text] nvarchar(100) not null, [Index] as [dbo].[Answers_Index]([ID], [ID_Question]), ) go insert into Answers ([ID_Question], [Text]) values (1, '1: first'), (2, '2: first'), (1, '1: second'), (2, '2: second'), (2, '2: third') select * from [Answers] Which works great, however it tends to slow down queries quite a bit. How can I make column Index persisted? I have tried following: create table Answers ( [ID] int not null identity(1, 1), [ID_Question] int not null, [Text] nvarchar(100) not null, ) go create function Answers_Index(#id int, #questionID int) returns int with schemabinding as begin return (select count([ID]) from [dbo].[Answers] where [ID] < #id and [ID_Question] = #questionID) end go alter table Answers add [Index] as [dbo].[Answers_Index]([ID], [ID_Question]) persisted go insert into Answers ([ID_Question], [Text]) values (1, '1: first'), (2, '2: first'), (1, '1: second'), (2, '2: second'), (2, '2: third') select * from [Answers] But that throws following error: Computed column 'Index' in table 'Answers' cannot be persisted because the column does user or system data access. Or should I just forget about it and use [Index] int not null default(0) and fill it in on insert trigger? edit: thank you, final solution: create trigger [TRG_Answers_Insert] on [Answers] for insert, update as update [Answers] set [Index] = (select count([ID]) from [Answers] where [ID] < a.[ID] and [ID_Question] = a.[ID_Question]) from [Answers] a inner join [inserted] i on a.ID = i.ID go
You could change the column to be a normal column and then update its value when you INSERT/UPDATE that row using a trigger. create table Answers ( [ID] int not null identity(1, 1), [ID_Question] int not null, [Text] nvarchar(100) not null, [Index] Int null ) CREATE TRIGGER trgAnswersIU ON Answers FOR INSERT,UPDATE AS DECLARE #id int DECLARE #questionID int SELECT #id = inserted.ID, #questionID = inserted.ID_question UPDATE Answer a SET Index = (select count([ID]) from [Answers] where [ID] < #id and [ID_Question] = #questionID) WHERE a.ID = #id AND a.ID_question = #questionID GO NB* This is not fully correct as it wont work correctly on UPDATE as we wont have the "inserted" table to reference to get the ID and questionid. There is a way around this but i cant remember it right now :( Checkout this for more info
Computed columns only store the formula of the calculation to perform. That is why it will be slower when querying the computed column from the table. If you want to persist the values to an actual table column, then you are correct about using a trigger.