Populate extra database column depending on other column values - sql

I have a database which collects data from an application. And now, I have to create another column that will be populated with predefined data depending on the values in other columns. So, no math, just to look up the values in two other columns and insert the data into the newly added column.
Example
id column1 column2 newColumn
1 15 3 100
So when column1 has 15, and column2 has 3, the newColumn should be auto-populated with 100. Again, the number 100 is predifned, not calcualted.
I know I can use triggers for new entries, but the database already has a large amount of data entered, so is there a way to auto populate the newColumn for data that is already tere?
EDIT --------------------------------
So I can use update to populate the column for the records that are already entered ?!
Can i make a trigger which will wait for both values and until both are entered it will return NULL?

You can create scalar function:
ALTER FUNCTION [dbo].[Test] ( #column1 INT, #column2 INT)
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
DECLARE #r INT
IF #column1 = 15 AND #column2 = 3
SET #r = 100
ELSE
SET #r = NULL
RETURN #r
END
And then add new computed column:
ALTER TABLE TableName ADD ColumnName AS dbo.Test(column1, column2) PERSISTED
Persisted means, that column is not calculated on the fly, but data is saved.
That's why you used WITH SCHEMABINDING. Without binding you can not make the column persisted.
You can also update your current data with simple update statement like in #Rhys Jones answer and add trigger on table like:
ALTER TRIGGER trTest ON TableName
AFTER INSERT, UPDATE
AS
BEGIN
IF UPDATE(column1) AND UPDATE(column2)
BEGIN
UPDATE TableName
SET NewColumn = CASE
WHEN column1 = 15 and column2 = 3 then 100
ELSE NULL
END
FROM Inserted i
JOIN TableName t ON t.id = i.id
END
END

You could just use a single UPDATE to update the missing values, then use the TRIGGER for new rows.
update MyTable set
newColumn = case
when column1 = 15 and column2 = 3 then 100
when ...
end
where
newColumn is null
However, note what #jarlh says above, there are usually better ways of doing this such as views or computed columns.

Related

Enumerate the multiple rows in a multi-update Trigger

I have something like the table below:
CREATE TABLE updates (
id INT PRIMARY KEY IDENTITY (1, 1),
name VARCHAR (50) NOT NULL,
updated DATETIME
);
And I'm updating it like so:
INSERT INTO updates (name, updated)
VALUES
('fred', '2020-11-11),
('fred', '2020-11-11'),
...
('bert', '2020-11-11');
I need to write an after update Trigger and enumerate all the name(s) that were added and add each one to another table but can't work out how enumerate each one.
EDIT: - thanks to those who pointed me in the right direction, I know very little SQL.
What I need to do is something like this
foreach name in inserted
look it up in another table and
retrieve a count of the updates a 'name' has done
add 1 to the count
and update it back into the other table
I can't get to my laptop at the moment, but presumably I can do something like:
BEGIN
SET #count = (SELECT UCount from OTHERTAB WHERE name = ins.name)
SET #count = #count + 1
UPDATE OTHERTAB SET UCount = #count WHERE name = ins.name
SELECT ins.name
FROM inserted ins;
END
and that would work for each name in the update?
Obviously I'll have to read up on set based SQL processing.
Thanks all for the help and pointers.
Based on your edits you would do something like the following... set based is a mindset, so you don't need to compute the count in advance (in fact you can't). It's not clear whether you are counting in the same table or another table - but I'm sure you can work it out.
Points:
Use the Inserted table to determine what rows to update
Use a sub-query to calculate the new value if its a second table, taking into account the possibility of null
If you are really using the same table, then this should work
BEGIN
UPDATE OTHERTAB SET
UCount = COALESCE(UCount,0) + 1
WHERE [name] in (
SELECT I.[name]
FROM Inserted I
);
END;
If however you are using a second table then this should work:
BEGIN
UPDATE OTHERTAB SET
UCount = COALESCE((SELECT UCount+1 from OTHERTAB T2 WHERE T2.[name] = OTHERTAB.[name]),0)
WHERE [name] in (
SELECT I.[name]
FROM Inserted I
);
END;
Using inserted and set-based approach(no need for loop):
CREATE TRIGGER trg
ON updates
AFTER INSERT
AS
BEGIN
INSERT INTO tab2(name)
SELECT name
FROM inserted;
END

Manually Checking of Value Changes in Tables for SQL

An example to the problem:
There are 3 columns present in my SQL database.
+-------------+------------------+-------------------+
| id(integer) | age(varchar(20)) | name(varchar(20)) |
+-------------+------------------+-------------------+
There are a 100 rows of different ids, ages and names. However, since many people update the database, age and name constantly change.
However, there are some boundaries to age and name:
Age has to be an integer and has to be greater than 0.
Name has to be alphabets and not numbers.
The problem is a script to check if the change of values is within the boundaries. For example, if age = -1 or Name = 1 , these values are out of the boundaries.
Right now, there is a script that does insert * into newtable where age < 0 and isnumeric(age) = 0 or isnumeric(name) = 0;
The compiled new table has rows of data that have values that are out of the boundary.
I was wondering if there is a more efficient method to do such checking in SQL. Also, i'm using microsoft sql server, so i was wondering if it is more efficient to use other languages such as C# or python to solve this issue.
You can apply check constraint. Replace 'myTable' with your table name. 'AgeCheck' and 'NameCheck' are names of the constraints. And AGE is the name of your AGE column.
ALTER TABLE myTable
ADD CONSTRAINT AgeCheck CHECK(AGE > 0 )
ALTER TABLE myTable
ADD CONSTRAINT NameCheck CHECK ([Name] NOT LIKE '%[^A-Z]%')
See more on Create Check Constraints
If you want to automatically insert the invalid data into a new table, you can create AFTER INSERT Trigger. I have given snippet for your reference. You can expand the same with additional logic for name check.
Generally, triggers are discouraged, as they make the transaction lengthier. If you want to avoid the trigger, you can have a sql agent job to do auditing on regular basis.
CREATE TRIGGER AfterINSERTTrigger on [Employee]
FOR INSERT
AS
BEGIN
DECLARE #Age TINYINT, #Id INT, Name VARCHAR(20);
SELECT #Id = ins.Id FROM INSERTED ins;
SELECT #Age = ins.Age FROM INSERTED ins;
SELECT #Name = ins.Name FROM INSERTED ins;
IF (#Age = 0)
BEGIN
INSERT INTO [EmployeeAudit](
[ID]
,[Name]
,[Age])
VALUES (#ID,
#Name,
#Age);
END
END
GO

Update a field in table when I'm updating a field in another table

I have 2 tables - A and B. I have a field in Table A, which can be a null or some value; and a field in table B, which can be null or some value. Both are of nvarchar type.
Now, I would like to have a sort of event listener that whenever that field in Table A changes value, I would like the same value to be appended to the start of Table B field.
If I would want to run UPDATE statement, I would write:
UPDATE B
SET B.myValueFromB = A.myValueFromA + CONVERT(nvarchar(max),B.myValueFromB)
FROM
A JOIN B on A.Ref_num = B.Task_num
WHERE A.Ref_Num = --here is the problem
I have a form in program that updates the record having specific A.Ref_num with values. End result would be whenever I update that record, a mtaching record in Table B also gets updated. How can I do something like that?
You can use a trigger for this purpose like :
CREATE TRIGGER up_update_your_table
ON Table_A FOR UPDATE
AS
--Your id field to find the corresponding record in the other table (Table B)
DECLARE #ID VARCHAR(50)
DECLARE #Value VARCHAR(50)
SELECT #ID = YourIDFieldInTableA, #Value = YourValueFieldInTableA FROM Inserted
--Inserted returns the updated row
UPDATE Table_B SET YourValueFieldInTableB = #Value WHERE YourIDFieldInTableB = ID

Add a new column to an existing table, with a value equal to the ID

In my database in a sql-server 2005 instance, I need to add a new column to a table with existing data. The table currently looks like this:
CREATE TABLE [dbo].[Status](
[Status_ID] [int] IDENTITY(1,1) NOT NULL,
[Description] [nvarchar](80) COLLATE Latin1_General_CI_AS NOT NULL
)
The column I want to add is also of type INT and nulls are not allowed. Furthermore, I want the initial values for this column to be equal to the ID (it cannot be a computed column).
This is the script I have written for this:
ALTER TABLE [dbo].[Status]
add Status_Code int NOT NULL DEFAULT 1
GO
//Get the number of rows
DECLARE #RowCount INT
SET #RowCount = (SELECT COUNT(Status_ID) FROM Status)
//Create an iterator
DECLARE #I INT
SET #I = 1
//Loop through the rows of a table #myTable
WHILE (#I <= #RowCount)
BEGIN
UPDATE Status
SET Status_Code = #I
WHERE Status_ID = #I
//Increment the iterator
SET #I = #I + 1
END
This script seems to work perfectly. However, it seems like a lot of code for a rather small task. Does anyone know of a more efficient way to code this?
Why loop through the table to do the update? Just do this:
ALTER TABLE [dbo].[Status]
add Status_Code int NOT NULL DEFAULT 1
UPDATE Status
SET Status_Code = Status_ID
Create the new column, and make it nullable.
Then write a simple update statement to insert the existing id's into the new columns.
Then make your column not-nullable.
I would go with Gerrie's answer as you want the column to be NOT NULL. If you specify a default value, SQL server will still allow you to leave out that column in any subsequent insert statement (the column will be assigned the default value)

IF UPDATE() in SQL server trigger

If there's:
IF UPDATE (col1)
...in the SQL server trigger on a table, does it return true only if col1 has been changed or been updated?
I have a regular update query like
UPDATE table-name
SET col1 = 'x',
col2 = 'y'
WHERE id = 999
Now what my concern is if the "col1" was 'x' previously then again we updated it to 'x'
would IF UPDATE ("col1") trigger return True or not?
I am facing this problem as my save query is generic for all columns, but when I add this condition it returns True even if it's not changed...So I am concerned what to do in this case if I want to add condition like that?
It returns true if a column was updated. An update means that the query has SET the value of the column. Whether the previous value was the same as the new value is largely irelevant.
UPDATE table SET col = col
it's an update.
UPDATE table SET col = 99
when the col already had value 99 also it's an update.
Within the trigger, you have access to two internal tables that may help. The 'inserted' table includes the new version of each affected row, The 'deleted' table includes the original version of each row. You can compare the values in these tables to see if your field value was actually changed.
Here's a quick way to scan the rows to see if ANY column changed before deciding to run the contents of a trigger. This can be useful for example when you want to write a history record, but you don't want to do it if nothing really changed.
We use this all the time in ETL importing processes where we may re-import data but if nothing really changed in the source file we don't want to create a new history record.
CREATE TRIGGER [dbo].[TR_my_table_create_history]
ON [dbo].[my_table] FOR UPDATE AS
BEGIN
--
-- Insert the old data row if any column data changed
--
INSERT INTO [my_table_history]
SELECT d.*
FROM deleted d
INNER JOIN inserted i ON i.[id] = d.[id]
--
-- Use INTERSECT to see if anything REALLY changed
--
WHERE NOT EXISTS( SELECT i.* INTERSECT SELECT d.* )
END
Note that this particular trigger assumes that your source table (the one triggering the trigger) and the history table have identical column layouts.
What you do is check for different values in the inserted and deleted tables rather than use updated() (Don't forget to account for nulls). Or you could stop doing unneeded updates.
Trigger:
CREATE TRIGGER boo ON status2 FOR UPDATE AS
IF UPDATE (id)
BEGIN
SELECT 'DETECT';
END;
Usage:
UPDATE status2 SET name = 'K' WHERE name= 'T' --no action
UPDATE status2 SET name = 'T' ,id= 8 WHERE name= 'K' --detect
To shortcut the "No actual update" case, you need also check at the beginning whether your query affected any rows at all:
set nocount on; -- this must be the first statement!
if not exists (select 1 from inserted) and not exists (select 1 from deleted)
return;
SET NOCOUNT ON;
declare #countTemp int
select #countTemp = Count (*) from (
select City,PostCode,Street,CountryId,Address1 from Deleted
union
select City,PostCode,Street,CountryId,Address1 from Inserted
) tempTable
IF ( #countTemp > 1 )
Begin
-- Your Code goes Here
End
-- if any of these "City,PostCode,Street,CountryId,Address1" got updated then trigger
-- will work in " IF ( #countTemp > 1 ) " Code)
This worked for me
DECLARE #LongDescDirty bit = 0
Declare #old varchar(4000) = (SELECT LongDescription from deleted)
Declare #new varchar(4000) = (SELECT LongDescription from inserted)
if (#old <> #new)
BEGIN
SET #LongDescDirty = 1
END
Update table
Set LongDescUpdated = #LongDescUpdated
.....