When I execute a Procedure I would like to UPDATE table1 and SET the new values for the CODE, NUMBER, and ADDRESS columns only if the same CODE and DeptID do not exist. If I change the CODE to an existing name in table1 it is OK as long as the DeptID is different.
Example:
Say I want to change Beta to Delta. That is fine because they have different DeptIDs. So I want to UPDATE everything, aka the third row in my example would now have the values (1, Delta, 'whateverNUMBER', 'whateverADDRESS')
But if I wanted to take that same third row Beta and change the CODE to Alpha I don't want to allow that and I don't want to SET the NUMBER or ADDRESS either because there is already a row with CODE Alpha and DeptID 1.
How would I be able to accomplish this?
Here is one of my attempts which does not work:
UPDATE dbo.table1
SET
CODE = #CODE
,NUMBER = #NUMBER
,ADDRESS = #ADDRESS
WHERE ID = #ID
AND NOT EXISTS
(
SELECT NAME FROM dbo.table1
WHERE NAME = #NAME
AND ID = #ID
)
This should work if you have the commas in the right place for syntax and specify the old code:
UPDATE dbo.table1
SET CODE = #CODE,
NUMBER = #NUMBER,
ADDRESS = #ADDRESS
WHERE ID = #ID AND
CODE = #OLDCODE AND
NOT EXISTS (SELECT 1
FROM dbo.table1
WHERE NAME = #NAME AND ID = #ID
);
The easiest would be to add a unique constraint for columns DeptId and Code.
This will prevent any duplicate insert as well as an update to already existing values.
The constraint will even prevent any changes performed directly against the table and not just within your update statement.
Related
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
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
First the solution is working perfect, and after as per our Project Manager requirement I have added two column in table. After that one insert,update store procedure is not working it show "Invalid column name" (it mention newly inserted two column name). I think some details is stored in temporary but I don't know how to find and solve it.
I tried something like this:
Removed all constrain and tried to run the store procedure, but no use
Just removed the newly added two column, it is working perfect.
Tried to add the column through an Alter query
My stored procedure is
ALTER PROCEDURE [Page].[SP_INSERT_EXPERIENCEDETAILS]
(#EXPERIENCEDETAILS [PAGE].[EXPERIENCEDETAILS] READONLY)
AS --drop PROCEDURE [Page].[SP_INSERT_EXPERIENCEDETAILS]
BEGIN
DECLARE #TEMPTABLE AS TABLE
(
ID INT,
[ACTION] VARCHAR(50)
)
MERGE INTO [PAGE].[EXPERIENCEDETAILS] AS TARGET
USING (SELECT
ID, Description, ISCurrent, COMPANYID,
Designationid, locationid, FROMDAY, FromMonth, FromYear,
TODAY, TOMONTH, Toyear
FROM
#EXPERIENCEDETAILS) AS SOURCE ON TARGET.ID = SOURCE.ID
WHEN MATCHED THEN
UPDATE
SET TARGET.[DESCRIPTION] = SOURCE.[DESCRIPTION],
TARGET.ISCURRENT = SOURCE.ISCURRENT,
TARGET.COMPANYID = SOURCE.COMPANYID,
TARGET.DESIGNATIONID = SOURCE.DESIGNATIONID,
TARGET.LOCATIONID = SOURCE.LOCATIONID,
TARGET.FROMDAY = SOURCE.FROMDAY,
TARGET.FROMMONTH = SOURCE.FROMMONTH,
TARGET.FROMYEAR = SOURCE.FROMYEAR,
TARGET.TODAY = SOURCE.TODAY,
TARGET.TOMONTH = SOURCE.TOMONTH,
TARGET.TOYEAR = SOURCE.TOYEAR
WHEN NOT MATCHED THEN
INSERT
VALUES (SOURCE.MEMBERID, SOURCE.PAGEID, SOURCE.COMPANYID,
SOURCE.DESIGNATIONID, SOURCE.LOCATIONID,
SOURCE.FROMDAY, SOURCE.FROMMONTH, SOURCE.FROMYEAR,
SOURCE.TODAY, SOURCE.TOMONTH, SOURCE.TOYEAR,
SOURCE.[DESCRIPTION], SOURCE.[ISCURRENT],
SOURCE.ENTRYDATE)
OUTPUT INSERTED.ID, $ACTION INTO #TEMPTABLE;
SELECT ID FROM #TEMPTABLE
END
Error shown in the following lines
TARGET.FROMDAY= SOURCE.FROMDAY
TARGET.TODAY=SOURCE.TODAY
SOURCE.FROMDAY
SOURCE.TODAY
You should also add those columns in table type [PAGE].[EXPERIENCEDETAILS] that used in your SP as TVP type.
This is my trigger:
Create trigger Points
on Posts
after insert, update
As
declare #Id int;
declare #value int;
select #value= Count(i.Message) from Posts i;
select #Id = [PostedBy] from inserted;
update AspNetUsers set User_points = #value * 3
where #Id = #Id
Here, at the last line, where condition always fails.Its not picking correct Id and updating same value in User_Points column in all rows not in particular rows.
I have written an insert statement to check what value i get back like this:
insert into Employee_Demo(PostedBy, TotalCount)
values (#postedby,#value );
here, i am getting correct #postedby value in table.
Previosly, i was trying this:
create trigger Points
on Posts
after insert, update
As
declare #value int
declare #postedby int
select #value= Count(Message) from Posts
select #postedby = PostedBy from inserted
update AspNetUsers set User_points = #value * 3
where Id = #postedby
please please someone help me. how to update only single row based on Id.
One more thing, PostedBy is not the primary key in post table.It is foreign key to aspnetuser table and it contains id value of user who has posted the message as u can see in below image.
tried this too:
update AspNetUsers set User_points = #value * 3
FROM INSERTED INNER JOIN Posts ON INSERTED.PostedBy = Posts.PostedBy
INNER JOIN AspNetUsers ON AspNetUsers.Id = inserted.PostedBy
Currently, you are comparing same Variable in where clause, where you are supposed to compare DB field with Variable.
Finally got it working with this query dont know how it happens
update AspNetUsers set User_points = #value * 3
where Id = (Select PostedBy from inserted)
and removing the declaration of #Id or #PostedBy. when i declared those variable and then assign value from inserted to those variable and then use that variable value in where then it was not working.
Instead, directly getting value from inserted table in where condition works.
Is it possible (in SQL SERVER 2012) to read data before update in one transaction.
Example:
I have one table, name: tab with two columns col1 and col2. I have one record: col1 = 1 and col2 = 'a'
begin transaction
update tab set col2 = 'A' where col1 = 1
-- here i want to read data before update (in this example 'a')
-- here i want to read data after update (in this example 'A')
Committ transaction
Before committ transaction when using select always i get data after update (in this example 'A'). I try to do
select * from tab with(nolock)
but it doesn't work.
Question: In section: after update and before committ - can i read data which was before update ?
Thanks.
Locking hints determine how nicely you read data that is being updated by another transaction. But code that runs in a transaction always sees changes made earlier in the same transaction.
If you need the old state, why not store the old version in a variable? Like:
begin transaction
declare #old_col2 int
select #old_col2 = col2 from tab where id = 1
update tab set col2 = 'A' where id = 1
... now you can access both the old and the new data ...
You can achieve the same using below sample :
USE AdventureWorks2012;
GO
DECLARE #MyTableVar table(
EmpID int NOT NULL,
OldVacationHours int,
NewVacationHours int,
ModifiedDate datetime);
UPDATE TOP (10) HumanResources.Employee
SET VacationHours = VacationHours * 1.25,
ModifiedDate = GETDATE()
OUTPUT inserted.BusinessEntityID,
deleted.VacationHours,
inserted.VacationHours,
inserted.ModifiedDate
INTO #MyTableVar;
--Display the result set of the table variable.
SELECT EmpID, OldVacationHours, NewVacationHours, ModifiedDate
FROM #MyTableVar;
GO
--Display the result set of the table.
SELECT TOP (10) BusinessEntityID, VacationHours, ModifiedDate
FROM HumanResources.Employee;
GO
This is elegant solution instead of writing seperate SELECTs in the code.
Reference http://msdn.microsoft.com/en-IN/library/ms177564.aspx