I need to have a table like this:
CLIENT_ID ID CONTENT
--------------------
1 1 abc
1 2 abc
1 3 abc
2 1 abc
2 2 abc
2 3 abc
client_id and id are the table's pk. on the application side, only client client_id and content are known, so I thought to have an after insert trigger that updates the id column as needed, but I'm having troubles. any help? Thanks.
My first attempt was:
CREATE TRIGGER test
ON [dbo].[table]
AFTER INSERT
AS
BEGIN
DECLARE #client_id INT;
DECLARE #id INT;
SELECT #client_id = client_id
FROM inserted;
SELECT #id = ISNULL(MAX(clifor_id), 0) + 1
FROM table
WHERE client_id = #client_id
UPDATE [dbo].[table]
SET [id] = #id
WHERE ?????
END
but don't know how to complete it.
ps: sorry for my English!
#Squirrel pointed you in good direction - instead of trigger is a way to go. S.t. like this one:
CREATE TRIGGER test
ON [dbo].[table]
INSTEAD OF INSERT
AS
BEGIN
UPDATE inserted
SET id = (select 1+max(id) from dbo.table where client_id = inserted.client_id)
INSERT INTO dbo.table
SELECT * FROM inserted --note - you should rewrite this block so it will be with explicit list of columns
END
Note that this will fail if you can insert multiple entries for the same client in one query.
Related
I'm looking for a method to update old rows before an insert or update using a Trigger ,
For example , I have this table
ID PersonID Name Status
1 001 Alex False
2 002 Mark True
What I need exactly is that when I insert in this table a new row (3,003,Jane,True) , the column status should be affected to False ( all old rows ) only the new row will have True
So the expected result when applying the trigger will be like this :
ID PersonID Name Status
1 001 Alex False
2 002 Mark False
3 003 Jane True
How can I do this?
What I have tried:
ALTER TRIGGER [dbo].[dbo.TR_SetStatus] ON [dbo].[Person]
after INSERT
NOT FOR REPLICATION
AS
DECLARE #CursorTestID INT = 1;
DECLARE #RowCnt BIGINT = 0;
BEGIN
DECLARE #count INT;
SELECT #RowCnt = COUNT(*) FROM Person;
WHILE #CursorTestID <= #RowCnt
BEGIN
update Person set status=0
SET #CursorTestID = #CursorTestID + 1
END
END
I have two questions:
How can I update the rows that are existed before the insert using Trigger ( SQL Server )?
How can I pass a parameter to a trigger? (as an example PersonID)
DROP TABLE IF EXISTS dbo.Test;
CREATE TABLE dbo.Test
(
id tinyint identity(1,1)not null primary key,
person_id char(3)not null,
name varchar(50)not null,
status varchar(5) not null
)
insert dbo.Test(person_id,name,status)
values('001','alex','false'),('002','mark','true');
go
SELECT *FROM DBO.Test
DROP TRIGGER IF EXISTS dbo.II_Test;
GO
CREATE TRIGGER dbo.II_Test
ON dbo.Test
INSTEAD OF INSERT
AS
BEGIN
UPDATE DBO.Test SET status='FALSE';
INSERT DBO.Test(person_id,name,status)
SELECT I.person_id,I.name,I.status
FROM inserted AS I
END
GO
insert dbo.Test(person_id,name,status)
values('003','JANE','true');
select * from dbo.Test
Could you please check if the above is suitable for you
How can I update the rows that are existed before the insert using Trigger ( SQL Server )
For example, you can use INSTEAD OF-trigger
How can I pass a parameter to a trigger? (as an example PersonID)
This is not supported at all. If you need parameters the better way, I guess, is to use stored procedure
Finally, I solved my problem ( the first question ) :
ALTER TRIGGER [dbo].[dbo.TR_SetActive] ON [dbo].[test]
after INSERT
NOT FOR REPLICATION
AS
BEGIN
update dbo.test set status=0 WHERE Id < (SELECT MAX(Id) FROM dbo.test)
END
For the second question , I have used to get the last record as parameter:
set #PersonId = (select PersonId from inserted)
Can be simplfy with :
CREATE TRIGGER dbo.E_I_Test
ON dbo.Test
FOR INSERT
AS
BEGIN
UPDATE T
SET status = CASE WHEN I.id IS NULL THEN 0 ELSE 1 END
FROM dbo.Test as T
LEFT OUTER JOIN inserted AS I
ON T.id = I.id;
BEWARE.... status is a reserved Transact SQL keyword. Should not be use for any SQL identifier (table, name, column neme...)
Corrections made...
I am trying to create a trigger within SQL Server Management Studio that will increment a column value by 1 when a separate column has been updated within the same table.
The value for the column we want to update when the update script has been ran becomes NULL
My example is that I when I change the address of a customer, I want a column that goes up by 1 every time the address is changed i.e NoOfAddressess = 1, 2, 3 etc...
Here is the SQL code that I am writing
ALTER TRIGGER trg_customeraudit
ON tblCustomer
AFTER UPDATE, DELETE, INSERT
AS
INSERT INTO dbo.CustomerDetailsAudit
VALUES (CURRENT_USER, CURRENT_TIMESTAMP,
(SELECT CustomerID FROM inserted),
(SELECT CustomerAddress FROM deleted),
(SELECT CustomerAddress FROM inserted),
(SELECT CustomerPostcode FROM deleted),
(SELECT CustomerPostcode FROM inserted),
(SELECT NumberOfChangedAddresses FROM dbo.CustomerDetailsAudit)
)
IF ((SELECT CustomerAddress FROM inserted) =
(SELECT CustomerAddress FROM deleted) OR
(SELECT CustomerPostcode FROM deleted) =
(SELECT CustomerPostcode FROM inserted))
BEGIN
RAISERROR ('You must enter both a new postcode and address',16,10)
ROLLBACK TRANSACTION
END
ELSE
BEGIN
PRINT 'Transaction successful'
WHERE CustomerID = (SELECT CustomerID from inserted)
END
IF UPDATE (CustomerName)
BEGIN
RAISERROR ('You cannot change the customer name', 16, 10)
ROLLBACK
END
Depending on the other things happening on this data triggers can be a very inefficient method of handling this, but here is one possible solution.
1. Setup
First create a table to use for testing.
create table test_table (
MyPrimaryKey int primary key clustered not null identity(1, 1)
, SomeColumn varchar(255) not null
, SomeColumnCounter int null
);
go
Now, add a trigger to initialize the counter to 1. This could be handled by a default constraint or set at the application level but it can also be done with a trigger.
-- this trigger will set the counter to 1 when a record is first added
-- doesn't need to be a trigger, but since the question was on triggers
create trigger trg_test_table_insert
on test_table
after insert
as
update tt
set tt.SomeColumnCounter = 1
from
test_table as tt
inner join
Inserted as i
on
tt.MyPrimaryKey = i.MyPrimaryKey;
go
Now, add a trigger that will check for changes on the designated column and increment the counter if needed.
-- this trigger will increment the counter by 1 if 'SomeColumn' changed
-- doesn't handle nulls so will need to be modified depending on schema
create trigger trg_test_table_update
on test_table
after update
as
update tt
set tt.SomeColumnCounter = tt.SomeColumnCounter + 1
from
Inserted as i -- new version of the record
inner join
Deleted as d -- old version of the record
on
i.MyPrimaryKey = d.MyPrimaryKey
and i.SomeColumn <> d.SomeColumn
inner join
test_table as tt
on
tt.MyPrimaryKey = i.MyPrimaryKey
go
2. Testing
Add some test data.
insert into test_table (SomeColumn)
values ('abc'), ('def');
go
Now we have:
MyPrimaryKey SomeColumn SomeColumnCounter
1 abc 1
2 def 1
Update without changing anything:
update tt
set tt.SomeColumn = 'abc'
from
test_table as tt
where
tt.MyPrimaryKey = 1
We still have:
MyPrimaryKey SomeColumn SomeColumnCounter
1 abc 1
2 def 1
Update that actually changes something:
update tt
set tt.SomeColumn = 'abbc'
from
test_table as tt
where
tt.MyPrimaryKey = 1
Now we have:
MyPrimaryKey SomeColumn SomeColumnCounter
1 abbc 2
2 def 1
Update that changes everything:
update tt
set tt.SomeColumn = tt.SomeColumn + 'z'
from
test_table as tt
Now we have:
MyPrimaryKey SomeColumn SomeColumnCounter
1 abbcz 3
2 defz 2
I have a query results like below
name id
a 2
b 3
c 7
h 9
i 1
i need to show the results like below
Expected Result
name id
c 7
a 2
b 3
h 9
i 1
The id field is Primarykey so based on that i want to show this
so far i tried with order by but it only give the option to order like decenting or acenting.
Please help me to do this
I had done something like below
create procedure proc1
#id int;
begin
select name,id from tablname order by id
end
I had also tried like but it also fails . Please help me to solve this
Try this:
CREATE PROCEDURE proc1 (#id int)
AS
SELECT name,id
FROM tablname
ORDER BY CASE WHEN id=#id THEN 0 ELSE id END
Note: This will work assuming the values of your id columns are always greater then 0.
I tried this and it gives exactly what is your required output.
declare #name varchar(10)
set #name = 'c'
create table #temp (id int ,name varchar(10))
insert into #temp(id,name) values(1,'i')
insert into #temp(id,name) values(2,'a')
insert into #temp(id,name) values(3,'b')
insert into #temp(id,name) values(7,'c')
insert into #temp(id,name) values(9,'h')
Select name,id from #temp ORDER BY CASE WHEN name=#name THEN '' ELSE name END
Drop table #temp
I have got the below stored procedure to return the list of Id, parentId and absoluteUrls which works fine:
ALTER PROCEDURE [dbo].[SearchDataManager.HierarchyById]
#currentId AS int
AS
BEGIN
DECLARE #id INT
DECLARE #parentId INT
DECLARE #absoluteUrl NVARCHAR(1000)
DECLARE #Hierarchy TABLE (Id int, ParentId int, AbsoluteUrl nvarchar(1000))
WHILE #currentId != 0
BEGIN
SELECT #id = Id, #parentId = ParentId, #absoluteUrl = AbsoluteUrl
FROM dbo.[SearchDataManager.NiceUrls]
WHERE id = #currentId
INSERT INTO #Hierarchy VALUES (#id, #parentId, #absoluteUrl)
SET #currentId = #parentId
END
SELECT * FROM #Hierarchy
END
The "NiceUrls" table has Id and ParentId. parentId refers to a record in the same table.
it returns like:
----------------------------------
Id | ParentId | AbsoluteUrl
----------------------------------
294 | 5 | url1
5 | 2 | url2
2 | 0 | url3
The above code works fine using a WHILE loop and defining a Table variable but I'm just wondering is there any better way to retrieve hierarchy data from a table?
The problem with the above code is maintainability. If I'd need to return 1 more column of the NiceUrls table then I'd have to define a new variable, add the column to the inline table, etc.
Is there any better way to rewrite the sp?
Thanks,
What's the
with Hierarchy (Id, ParentId, AbsoluteUrl, Level)
AS
(
-- anchor member
SELECT Id,
ParentId,
AbsoluteUrl,
0 AS Level
FROM dbo.[NiceUrls]
WHERE id = #currentId
UNION ALL
-- recursive members
SELECT su.Id,
su.ParentId,
su.AbsoluteUrl,
Level + 1 AS Level
FROM dbo.[NiceUrls] AS su
INNER JOIN Hierarchy ON Hierarchy.ParentId = su.Id
)
SELECT * FROM Hierarchy
Looks like you want all the records from the source table that are related to the original id.
1) Create a CTE that gives you all the ids (see the link Triple noted)
2) Join this CTE to the original table
Say I have a table variable:
DECLARE #MyTableVar TABLE (ID INT IDENTITY(1,1), SomeData NVARCHAR(300))
After I have inserted 250 rows, I need to "Start Over" with the table. I do this:
DELETE FROM #MyTableVar
Is there anything I can do to the table variable so that this:
insert into #MyTableVar Values("TestData")
select * from #MyTableVar
will return this:
_______________________________
| ID | SomeData |
|___________|_________________|
| | |
| 1 | TestData |
|___________|_________________|
instead of this:
_______________________________
| ID | SomeData |
|___________|_________________|
| | |
| 251 | TestData |
|___________|_________________|
Instead relying on an Identity, why not use the new ranking functions such as Row_Number
Insert #MyTableVar( Id, Value )
Select Row_Number() Over ( Order By Value )
, Value
From SomeOtherTable
Instead of re-seeding the IDENTITY, why not just delete from the #table variable, then use ROW_NUMBER() against the input? e.g. instead of the lazy
SELECT * FROM #MyTableVar;
...use...
SELECT ID = ROW_NUMBER() OVER (ORDER BY ID), SomeData FROM #MyTableVar;
Now you don't need to care what the seed is, whether it starts at 1, whether there are any gaps, etc.
unfortunately there is no function to reseed identity column in table variable, I know this question is very old, but in case other people encountered the same problem, I would like to share my method to solve this problem.
/* declare another table variable with same structure and perform select insert*/
DECLARE #MyTableVar1 TABLE (ID INT IDENTITY(1,1), SomeData NVARCHAR(300))
insert into #MyTableVar1
select someData from #MyTableVar
However, If you want to perform dynamic reseeding inside a loop, I would suggest using a table object
You can't reseed the identity value on a Table Variable but you can do the same thing with a Temp Table:
CREATE TABLE #TAB(ID INT IDENTITY,VALUE VARCHAR(10))
DECLARE #RESEED INT = 32
DBCC CHECKIDENT(#TAB,RESEED,#RESEED)
INSERT INTO #TAB
SELECT 'TEST'
SELECT * FROM #TAB
Since you are re-using your table, if I got it right, how about you do not initialize your counters to 1 and instead use this as an example?
DECLARE #ctr INT
IF #ctr IS NULL or #ctr <= 0 --this part is to control #ctr value on loops
SET #ctr = 1
ELSE
SELECT #ctr = MIN(id) FROM #tbl
This way, you are not restarting your loop to 1 nor is there a need for you to truncate the table.
Is it possible to have another int column on your table variable and update that column with modulo after the insert is finished?
declare #Mytablevar table
(
id int identity(1,1)
,id1 int
somedata nvarchar(300)
)
-- insert your data as you would. After insert is finished, do the following:
update #mytablevar set id1 = case when id > 250 then id % 250 else id end
I tried it on net but i am not able to get any solution on reset identity for table variable.
If you are able to use temp table #MyTableVar instead of table #MyTableVar variable then it is possible to reset identity value
DBCC CHECKIDENT('TableName', RESEED, NewValue)
DBCC CHECKIDENT(#MyTableVar, RESEED, 0)
Newvalue must be one less than the newIdentiyValue
NewValue= NewIdentity-1;
If you still want to learn more you can refer my blog
http://tryconcepts.blogspot.in/2012/08/reset-identity-column-to-new-id.html
I just had this idea and it works!!! :
declare #TableVariable table (
IdentityColumn int identity(1,1),
SomeOtherValue int,
DesiredResult int
)
declare #FirstIdentityValueEachTimeYouLoadDataToTable int
declare #Count int
set #Count = 1
while #Count <= 5
begin
delete #TableVariable
insert into #TableVariable (SomeOtherValue) select 45
insert into #TableVariable (SomeOtherValue) select 90
insert into #TableVariable (SomeOtherValue) select 2
select #FirstIdentityValueEachTimeYouLoadDataToTable = min(IdentityColumn) from #TableVariable
Update #TableVariable set DesiredResult = IdentityColumn - #FirstIdentityValueEachTimeYouLoadDataToTable + 1
select * from #TableVariable
set #Count = #Count + 1
end
Can you use temporary table?
This is a sample how to do this with a temp table.
CREATE TABLE #MyTableVar (ID INT IDENTITY(1,1), SomeData NVARCHAR(300))
insert #MyTableVar(SomeData) values ('test1'), ('test2')
---doesn't work
DELETE FROM #MyTableVar
insert #MyTableVar(SomeData) values ('test3'), ('test4')
select * from #MyTableVar
--resets the identity
truncate table #MyTableVar
insert #MyTableVar(SomeData) values ('test3'), ('test4')
select * from #MyTableVar
Regards
Piotr
you should truncate your table instead of deleting all rows from it.
but note that truncate will not work for some tables, (listed from MSDN):
You cannot use TRUNCATE TABLE on tables that:
Are referenced by a FOREIGN KEY constraint. (You can truncate a table that has a foreign key that references itself.)
Participate in an indexed view.
Are published by using transactional replication or merge replication.
and added myself:
You cannot truncate a table variable.
syntac of truncate is:
TRUNCATE TABLE
[ { database_name .[ schema_name ] . | schema_name . } ]
table_name
[ ; ]
EDIT: I didn't notice that you are questioning about table variables.
as far as I know there is no way to reset an identity column in a table variable. you can use a temp table instead.
DELETE FROM does not reset identity.
TRUNCATE does.
If you are using SQL Server then use this DBCC CHECKIDENT('Customer', RESEED, 0) Where Customer is a table name. When you insert records into table after this command your primery key column value will be start from 1 again.
Read this
http://codedotnets.blogspot.in/2012/09/how-to-reset-identity-in-sql-server.html