Adding computed column in SQL - sql

How to add computed column that calculates the sum of selected column.
this is what i've done but has an error..
ALTER TABLE dbo.tblEM_Reimbursement
ADD [Amount] AS (SELECT SUM(Total) FROM dbo.tblEM_ReimbursementEntry WHERE ReimbursementID = ReimbursementID)
it says
Msg 1046, Level 15, State 1, Line 2
Subqueries are not allowed in this context. Only scalar expressions are allowed.
Thanks in advance

You could use a user-defined function. Non tested code below.
CREATE FUNCTION dbo.Get_ReimbursementEntry (#ReimbursementID int)
RETURNS DECIMAL(10,2)
AS BEGIN
RETURN (SELECT SUM(Total) FROM dbo.tblEM_ReimbursementEntry WHERE ReimbursementID = ReimbursementID)
END
GO
ALTER TABLE dbo.tblEM_Reimbursement
ADD [Amount] AS (dbo.Get_ReimbursementEntry (ReimbursementID))
GO
but beware of the performance implications

You can use a Trigger. Trigger is a automated function written in PL/SQL whcih is called
after or before a particular event.
Read this tutorial for better understanding.
For SQL-Server read this and this.
Example:
CREATE TABLE Employee_Test
(
Emp_ID INT Identity,
Emp_name Varchar(100),
Emp_Sal Decimal (10,2)
)
INSERT INTO Employee_Test VALUES ('Anees',1000);
INSERT INTO Employee_Test VALUES ('Rick',1200);
INSERT INTO Employee_Test VALUES ('John',1100);
INSERT INTO Employee_Test VALUES ('Stephen',1300);
INSERT INTO Employee_Test VALUES ('Maria',1400);
Suppose you have another table:
CREATE TABLE Employee_Test_Audit
(
Emp_ID int,
Emp_name varchar(100),
Emp_Sal decimal (10,2),
Audit_Action varchar(100),
Audit_Timestamp datetime
)
This trigger is fired after an INSERT on the table. Let’s create the trigger as:
CREATE TRIGGER trgAfterInsert ON [dbo].[Employee_Test]
FOR INSERT
AS
declare #empid int;
declare #empname varchar(100);
declare #empsal decimal(10,2);
declare #audit_action varchar(100);
select #empid=i.Emp_ID from inserted i;
select #empname=i.Emp_Name from inserted i;
select #empsal=i.Emp_Sal from inserted i;
set #audit_action='Inserted Record -- After Insert Trigger.';
insert into Employee_Test_Audit
(Emp_ID,Emp_Name,Emp_Sal,Audit_Action,Audit_Timestamp)
values(#empid,#empname,#empsal,#audit_action,getdate());
PRINT 'AFTER INSERT trigger fired.'
GO

Related

Inserting into a Table the result between a variable and a table parameter

Having the following procedure:
CREATE PROCEDURE [dbo].[Gest_Doc_SampleProc]
#Nome nvarchar(255),
#Descritivo nvarchar(255),
#SampleTable AS dbo.IDList READONLY
AS
DECLARE #foo int;
SELECT #foo=a.bar FROM TableA a WHERE a.Nome=#Nome
IF NOT EXISTS (SELECT a.bar FROM TableA a WHERE a.Nome=#Nome)
BEGIN
INSERT INTO TableA VALUES (#Nome,#Descritivo)
INSERT INTO TableB VALUES (scope_identity(),#SampleTable)
END
I am trying, as shown, inserting into TableB all the values of SampleTable, together with the scope_identity.
SampleTable is as:
CREATE TYPE dbo.SampleTable
AS TABLE
(
ID INT
);
GO
How can I correctly achieve this?
The right way to do this type of work is the OUTPUT clause. Although technically not needed for a single row insert, you might as well learn how to do it correctly. And even what looks like a single row insert can have an insert trigger that does unexpected things.
PROCEDURE [dbo].[Gest_Doc_SampleProc] (
#Nome nvarchar(255),
#Descritivo nvarchar(255),
#SampleTable AS dbo.IDList
) READONLY AS
BEGIN
DECLARE #ids TABLE (id int);
DECLARE #foo int;
SELECT #foo = a.bar
FROM TableA a
WHERE a.Nome = #Nome;
IF NOT EXISTS (SELECT 1 FROM TableA a WHERE a.Nome = #Nome)
BEGIN
INSERT INTO TableA (Nome, Descritive)
OUTPUT Inserted.id -- or whatever the id is called
INTO #ids;
VALUES (#Nome,#Descritivo)
INSERT INTO TableB (id, sampletable)
SELECT id, #SampleTable
FROM #ids;
END;
END; -- Gest_Doc_SampleProc
In addition to using OUTPUT, this code also adds column lists to the INSERTs. That is another best practice.

Handling bulk insert on a table with multiple input sources in SQL

I am performing bulk insert on a table in sql server 2012, at the same time i am picking the last inserted row with max() function and inserting it into another table , how to perform this when my table is getting data from multiple sources because while performing insertion into secondary table there is time delay while insertions are still happening in primary table so next time max() will pick up last updated row and i will loose some rows which are not max() but still inserted into primary table meanwhile.
create table dbo.emp
(
id int primary key identity(1,1),
emp_id int,
name varchar(255),
address varchar(255)
)
create table dbo.empx
(
id int primary key,
emp_id int foreign key references dbo.emp(id),
)
declare #temp int ;
set #temp=1;
while #temp<1000
begin
insert into dbo.emp(emp_id,name,address)values (100+#temp,'Ename'+LTRIM(STR(#temp)),'123 Sample Address'+LTRIM(STR(#temp)));
set #temp=#temp+1;
insert into dbo.empx select max(dbo.emp.id),max(dbo.emp.emp_id) from dbo.emp
end
Use OUTPUT Clause...
CREATE TABLE #empx
(Id INT ,emp_id VARCHAR(50))
DECLARE #temp INT ;
SET #temp=1;
WHILE #temp<1000
BEGIN
INSERT INTO dbo.emp(emp_id,name,address)
OUTPUT INSERTED.Id,INSERTED.emp_id INTO #empx(Id,emp_id)
VALUES (100+#temp,'Ename'+LTRIM(STR(#temp)),'123 Sample Address'+LTRIM(STR(#temp)));
SET #temp=#temp+1;
END
INSERT INTO dbo.empx(Id,emp_id)
SELECT Id,emp_id FROM #empx
Or Use a trigger
CREATE TRIGGER EmpLog
ON dbo.emp
AFTER Insert
AS
BEGIN
SET NOCOUNT ON;
Insert into dbo.empx (id,emp_id) Select id,emp_id from inserted;
END
GO

Insert another parameter along with table type

The table tblEffort has 3 columns EmployeeName, Effort, Date. I get the first 2 column values from a table type parameter and have to insert a common date for all these values.
do not have the option to include timesheetdate in dtCSV
There should be some other way than running an update again on the inserted columns right?
The below code gives the error - Must declare the table variable "#TimesheetDate"
CREATE TYPE dbo.SaveEffort_TableType AS TABLE
(
EmployeeName varchar(200),
Effort decimal(18,2)
);
GO
CREATE PROCEDURE SaveEmployeeEffort
(
#TimesheetDate datetime,
#dtCSV AS dbo.SaveEffort_TableType readonly
)
AS
BEGIN
INSERT INTO tblEffort(EmployeeName,Effort,[Date])
SELECT * FROM #dtCSV,#TimesheetDate
END
You need to put the variable into the SELECT clause, not the FROM clause
CREATE PROCEDURE SaveEmployeeEffort
(
#TimesheetDate datetime,
#dtCSV AS dbo.SaveEffort_TableType readonly
)
AS
BEGIN
INSERT INTO tblEffort(EmployeeName,Effort,[Date])
SELECT EmployeeName,Effort, #TimesheetDate FROM #dtCSV
END

Values() constructor in SQL Server 2008 and triggers

I have two tables Employee and Emp_Audit.
On table Employee, I have an AFTER INSERT trigger, which fires when I insert rows into Employee. The function of trigger is to insert the rows into the Emp_Audit table that have been inserted into Employee.
The trigger works fine when I explicitly use 'insert values' for each record to be inserted in Employee as
INSERT INTO Employee_Test VALUES ('Anees',1000);
INSERT INTO Employee_Test VALUES ('Rick',1200);
INSERT INTO Employee_Test VALUES ('John',1100);
INSERT INTO Employee_Test VALUES ('Stephen',1300);
INSERT INTO Employee_Test VALUES ('Maria',1400);
Trigger inserts all these rows inti Emp_Audit -------------GOOD
But when I use values constructor as
insert into dbo.Employee_Test
values ('Kritika', 25000),
('Ritu', 15000),
('Maduri', 7000),
('Dinkar', 7000);
Only the first row ('Kritika', 25000) gets inserted into Emp_Audit
The whole query is as follows:
CREATE TABLE Employee_Test
(
Emp_ID INT Identity,
Emp_name Varchar(100),
Emp_Sal Decimal (10,2)
)
CREATE TABLE Employee_Test_Audit
(
Emp_ID int,
Emp_name varchar(100),
Emp_Sal decimal (10,2),
Audit_Action varchar(100),
Audit_Timestamp datetime
)
-----------------------Trigger------------------------------------
CREATE TRIGGER trgInsertAfter ON [dbo].[Employee_Test]
FOR INSERT
AS
declare #empid int;
declare #empname varchar(100);
declare #empsal decimal(10,2);
declare #audit_action nvarchar(200);
select #empid = inserted.Emp_ID
FROM inserted;
select #empname = inserted.Emp_name
from inserted;
select #empsal = inserted.Emp_Sal
from inserted;
set #audit_action = 'Record Inserted after Insert Trigger Fired';
INSERT INTO Employee_Test_Audit
VALUES(#empid, #empname, #empsal, #audit_action, GETDATE());
GO
print('Insert trigger FIRED')
insert into dbo.Employee_Test
values ('Kritika', 25000),
('Ritu', 15000),
('Maduri', 7000),
('Dinkar', 7000);
Yep, what you want is:
CREATE TRIGGER trgInsertAfter ON [dbo].[Employee_Test]
FOR INSERT
AS
INSERT INTO Employee_Test_Audit (Emp_ID, Emp_name, Emp_Sal, Audit_Action, Audit_Timestamp)
SELECT Emp_ID,Emp_name,Emp_Sal,
'Record Inserted after Insert Trigger Fired',GETDATE()
from inserted;
Because inserted can contain multiple rows (or no rows), you have to treat it as a table. I've never seen any different behaviour, but there's no guarantee (in your version) that all of the variables would have been assigned values from the same row even.
Also, you really should get into the habit of supplying a column list to the INSERT.
Try this one -
CREATE TRIGGER dbo.trgInsertAfter
ON [dbo].[Employee_Test]
FOR INSERT
AS BEGIN
INSERT INTO dbo.Employee_Test_Audit(Emp_ID, Emp_name, Emp_Sal, ..., ...)
SELECT
i.Emp_ID
, i.Emp_name
, i.Emp_Sal
, 'Record Inserted after Insert Trigger Fired'
, GETDATE()
FROM INSERTED i
END
The reason of why only one record inserted is when you assign value to the variable using select then it don't provide any error and set the value to any arbitrary value in the column.
and also using that method only insert one record. So correct trigger code should be
CREATE TRIGGER trgInsertAfter ON [dbo].[Employee_Test]
FOR INSERT
AS
INSERT INTO Employee_Test_Audit
SELECT Emp_ID,Emp_name,Emp_Sal,
'Record Inserted after Insert Trigger Fired',GETDATE()
from inserted;

i am trying to execute the before insert trigger , but i m getting the sql errors

what i want to achieve is i have a table called orders.
i want to perform the before insert trigger on my orders table.i want to capture the
username of person performing INSERT into table.
one table called info which contain the user.
this is my code
create table orders
(
order_id int,
quantity int,
cost int,
total_cost int,
created_date datetime,
created_by varchar(20)
)
create trigger beforeInsertdata
before insert
on orders
for each row
declare
v_username varchar2(10);
begin
-- Find username of person performing INSERT into table
SELECT user INTO v_username
FROM info;
-- Update create_date field to current system date
:new.create_date := sysdate;
-- Update created_by field to the username of the person performing the INSERT
:new.created_by := v_username;
END;
--user information--
create table info
(
userid int ,
user_name varchar(10)
)
insert into info values(1,'vivek')
select * from info
Basically, triggers are classified into two main types:-
1)After Triggers (For Triggers)
2)Instead Of Triggers
and the syntax for trigger is
CREATE TRIGGER trigger_name ON table_name
[FOR|AFTER|INSTEAD OF] [INSERT|UPDATE|DELETE]
AS
//your code goes here
GO
NOTE : FOR keyword used for INSERT |UPDATE Command where as AFTER USED FOR DELETE Command.
It's hard to tell what you're really trying to do. I've modified your code sample so that it will work on SQL2K5 and made some assumptions about how you're wanting to use the connected user account.
CREATE TABLE orders (
order_id int,
quantity int,
cost int,
total_cost int,
created_date datetime,
created_by varchar(20)
);
CREATE TABLE info (
userid int,
user_name varchar(10)
);
INSERT INTO info
VALUES (1, 'vivek');
SELECT *
FROM info;
CREATE TRIGGER orders_InsteadOfInsert ON orders
INSTEAD OF INSERT AS BEGIN
SET NOCOUNT ON;
-- varchar(10) is to match your table, but probably should be larger
DECLARE #CurrentUser VarChar(10);
SELECT #CurrentUser = SYSTEM_USER;
IF (#CurrentUser NOT IN (SELECT user_name FROM info)) BEGIN
-- consider using an identity column for the key instead of this
INSERT INTO info (userid, user_name)
SELECT
ISNULL((SELECT MAX(userid) FROM info), 0) + 1,
#CurrentUser;
END;
INSERT INTO orders (order_id, quantity, cost, total_cost, created_date, created_by)
SELECT
INS.order_id,
INS.quantity,
INS.cost,
INS.total_cost,
GETDATE(),
#CurrentUser
FROM INSERTED INS;
END;