I have a table that has two columns "name" and "ID". where ID is not null.
I am trying to create a stored prcedure to swap the IDs around for two names when imputed here is what i have so far.
CREATE PROCEDURE dbo.procName
#OldName NVARCHAR(128),
#NewName NVARCHAR(128)
AS
DECLARE #NewId0 INT,
#NewId1 INT,
#OldId0 INT,
#OldId1 INT,
#Number INT
SELECT #NewId0 = ID FROM Table1 WHERE [Name] = #NewName
SELECT #NewId1 = ID FROM Table1 WHERE [Name] = #NewName
SELECT #OldId0 = ID FROM Table1 WHERE [Name] = #OldName
SELECT #OldId1 = ID FROM Table1 WHERE [Name] = #OldName
SELECT #Number = 0
UPDATE Table1 SET ID = #Number WHERE ID = #NewId0
UPDATE Table1 SET ID = #NewId1 WHERE ID = #OldId0
UPDATE Table1 SET ID = #OldID1 WHERE ID = #NewID0
Go
All I get is the first name to have the value 0.
I think my logic is correct but it doesn't seem to be working is there something that I am missing?
Try something like
UPDATE Table1
SET ID = CASE
WHEN ID = #NewId
THEN #OldId
ELSE #NewId
END
WHERE ID IN (#NewId, #OldId)
Here is a full example
DECLARE #Table TABLE(
ID INT,
Name VARCHAR(20)
)
INSERT INTO #Table SELECT 1,'A'
INSERT INTO #Table SELECT 2,'B'
DECLARE #NewName VARCHAR(20),
#OldName VARCHAR(20)
SELECT #NewName = 'A',
#OldName = 'B'
DECLARE #NewId INT,
#OldId INT
SELECT #NewId = ID FROM #Table WHERE [Name] = #NewName
SELECT #OldId = ID FROM #Table WHERE [Name] = #OldName
SELECT *
FROM #Table
UPDATE #Table
SET ID = CASE
WHEN ID = #NewId
THEN #OldId
ELSE #NewId
END
WHERE ID IN (#NewId, #OldId)
SELECT *
FROM #Table
This script works if the ID is not a primary Key
CREATE PROCEDURE dbo.procName
#OldName NVARCHAR(128),
#NewName NVARCHAR(128)
As
DECLARE #NewId INT,
#OldId INT
SELECT #NewId = ID FROM Table1 WHERE [Name] = #NewName
SELECT #OldId = ID FROM Table1 WHERE [Name] = #OldName
UPDATE Table1
SET ID = CASE
WHEN ID = #NewId
THEN #OldId
ELSE #NewId
END
WHERE ID IN (#NewId, #OldId)
SELECT *
FROM Table1
go
Your current code fails because:
You Look for #NewId0, and set it to #Number (which is 0)
You then look for #OldId0, and set it to #NewId1
You then look for #NewId0 again... but two lines back, you set it to 0, so it's not in the table any more
I like #astander's solution (upvoted), saved me writing it out myself.
BUT... your comment, "...ID is a Primary Key", raises all kinds of red flags. You really, really don't want to change primary key values [insert long discussion from past classes about primary keys and relational integrity here]. Figure out why you think you need to do it, and then figure out another way to implement that business requirement, such as:
Don't change the IDs, change everything else (name, description, cost, whatever)
Create completely new entries and drop (or mark as completed, discareded, or junk) the old ones
Implement some clever logic based on the underlying business requirements.
under stand the concerns with changing PKs but it has to be done as it would take too long to change all the other stuff. That said I have come up with an answer. This is only the first part of a long script so again its not perfect:
CREATE PROCEDURE dbo.ID #OldName NVARCHAR(128),
#NewName NVARCHAR(128)
AS
DECLARE #NewId INT,
#OldId INT
CREATE TABLE TmpTable (ID INT,Name NVARCHAR(128))
INSERT INTO TmpTable (Name,ID)
VALUES (#NewName,(SELECT ID FROM Table1 WHERE [Name] = #NewName));
INSERT INTO TmpTable (Name,ID)
VALUES(#OldName,(SELECT ID FROM Table1 WHERE [Name] = #OldName))
UPDATE Table1 SET ID = 11 WHERE [NAME] = #NewName
UPDATE Table1 SET ID = 10 WHERE [NAME] = #OldName
UPDATE Table1 SET ID = (SELECT ID FROM TmpTable where Name = #NewName)WHERE [Name] = #OldName
UPDATE Table1 SET ID = (SELECT ID FROM TmpTable where Name = #OldName) WHERE [Name] = #NewName
DROP TABLE TmpTable
go
"11" and "10" were selected as they are not in the table my next mission will be to query table1 and for a random number that dosent exist then use that number to temprary repplace the ID before updating the new ones.
Thanks
Related
I am attempting to DECLARE a variable then SET it's value using a secondary variable which has already had it's value set.
I have tried:
DECLARE #Type VARCHAR = 'Some unique text';
DECLARE #TypeId UNIQUEIDENTIFIER;
SET #TypeId =
(
SELECT Id
FROM Types
WHERE Name = #Type
)
select #TypeId
To no avail. The result of the final SELECT statement is null. The following works:
DECLARE #TypeId UNIQUEIDENTIFIER;
SET #TypeId =
(
SELECT Id
FROM Types
WHERE Name = 'Some unique text'
)
select #TypeId
but I have several tables linked via dependencies and to delete an entry I need to traverse the tables in the correct order pulling the correct Ids. It is likely I will need to do this frequently so I want to reduce the leg work and just enter the text once and the script do the rest.
Is the syntax wrong or is this not possible?
DECLARE #Type VARCHAR = 'Some unique text';
It seems like you try to configure the variable value to be 'Some unique text' but since the type of the variable is VARCHAR(1) then when you set the value the server implicitly CONVERT it to VARCHAR(1) which lead to trunctaing the string and using only the first character
DECLARE #Type VARCHAR = 'Some unique text';
SELECT #Type
GO -- result is "S" and not "Some unique text"
To clarify, using DECLARE #Type VARCHAR without explicitly set the length is translated into 'DECLARE #Type VARCHAR(1)'
As a result of this issue, your comparing of the value probably result with no rows since you compare "S" and not "Some unique text". Your sub query is the same as SELECT Id FROM Types WHERE Name = 'S'
Here is a simple illustration of the issue
------------ DDL+DML: Creating sample table with some data
DROP TABLE IF EXISTS t1, t2
GO
CREATE TABLE t1 (
ID int, [Name] NVARCHAR(100)
)
GO
INSERT t1 (ID, [Name]) Values
(1, 'Mobile '),
(2, 'TV '),
(3, 'Display')
GO
----------- Using VARCHAR without length returns nothing
DECLARE #Type VARCHAR = 'Mobile';
SELECT #Type
DECLARE #TypeId INT;
SET #TypeId =
(
SELECT Id FROM t1 WHERE Name = #Type
)
select #TypeId
----------- Using VARCHAR(100) works great
DECLARE #Type VARCHAR(100) = 'Mobile';
SELECT #Type
DECLARE #TypeId INT;
SET #TypeId =
(
SELECT Id FROM t1 WHERE Name = #Type
)
select #TypeId
I'm trying to update with a passed variable in only the first row that has value NULL (multiple rows could have NULL in this column, but I need just the one),
Then I need to get the row affected (the primary key) and update the other table with it.
Here's what my two tables look like:
table1
id | some_value | ref_table2_id_fk
table2
id | name | ref_table1_id_fk
In my stored procedure I'm grabbing the passed value as #passed as int, then I try the following:
BEGIN
SET NOCOUNT ON;
DECLARE #id AS INT;
DECLARE #temp TABLE (id int);
BEGIN TRANSACTION;
BEGIN TRY
UPDATE TOP (1) [dbo].table1
SET ref_table2_id_fk = #passed
OUTPUT inserted.id INTO #temp
WHERE ref_table2_id_fk = NULL
UPDATE [dbo].table2
SET ref_table1_id_fk = #temp.id
FROM table2
JOIN #temp i on i.id = table2.id;
SET #id = ##IDENTITY
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
--some error
ROLLBACK TRANSACTION;
RETURN 0;
END
END CATCH;
IF ##TRANCOUNT > 0
BEGIN
--success
COMMIT TRANSACTION;
RETURN #Id;
END
END
As pointed out by Dale ##identity doesn't work in update. My intention is to simply know if the transaction went through or not.
I think the following code does what you are asking. Things fixed:
where ref_table2_id_fk = null should be where ref_table2_id_fk is null
You can't use TOP in an update statement you need a sub-query to get the id.
You're not providing an id in #temp to join onto table2 - you need the table1 id and the table2 id for a joined update.
If I understand your logic the id you want to return is #passed - you already have it.
#temp.id should be i.id since you've (rightly) aliased it
declare #Passed int = 3;
declare #table1 table (id int, some_value varchar(12), ref_table2_id_fk int);
declare #table2 table (id int, some_value varchar(12), ref_table1_id_fk int);
insert into #table1 (id)
select 1 union all select 2;
insert into #table2 (id)
select 3 union all select 4;
select * from #table1;
select * from #table2;
DECLARE #id AS INT, #Result bit = 0;
DECLARE #temp TABLE (id int, fk int);
BEGIN TRANSACTION;
BEGIN TRY
UPDATE #table1
SET ref_table2_id_fk = #passed
OUTPUT #passed, inserted.id INTO #temp
WHERE id = (
select top 1 id
from #table1
where ref_table2_id_fk is NULL
-- Optionally order by if you have a priority here
);
UPDATE T2
SET ref_table1_id_fk = i.fk
FROM #table2 T2
JOIN #temp i on i.id = T2.id
where T2.id = #passed;
-- If we get here then everything worked
-- Return #Result at the end of the proc
SET #Result = 1;
END TRY
begin catch
no_op:;
end catch
select * from #table1;
select * from #table2;
I am new in stored procedures.
ALTER PROCEDURE [dbo].[SP_MY_STORE_PROCEDURED]
(
#ID INT,
#NAME VARCHAR(50)
)
AS
BEGIN
UPDATE MY_TABLE
SET
WHEN
(( ID ) > 1)
THEN
ID=#ID
,
NAME = #NAME
END
I try to use when then for update my ID and Name
If Id is greater than 1 i want to update otherwise no update.
How can i do it ms sql?
Any help will be appreciated.
Thanks.
I think this is what you are after:
ALTER PROCEDURE [dbo].[SP_MY_STORE_PROCEDURED]
(
#ID INT,
#NAME VARCHAR(50)
)
AS
BEGIN
UPDATE MY_TABLE
SET NAME = #NAME
WHERE ID = #ID
END
You do not need to check ID>1 since you are checking the equality with #ID. If you want to be sure that this doesn't happen if #ID <=1 then you may try the following:
ALTER PROCEDURE [dbo].[SP_MY_STORE_PROCEDURED]
(
#ID INT,
#NAME VARCHAR(50)
)
AS
BEGIN
IF #ID > 1
UPDATE MY_TABLE
SET NAME = #NAME
WHERE ID = #ID
END
I'm not sure exactly what you're trying to update. Are you trying to change the name on a user record with id = #ID?
ALTER PROCEDURE [dbo].[SP_MY_STORE_PROCEDURED]
(
#ID INT,
#NAME VARCHAR(50)
)
AS
BEGIN
UPDATE MY_TABLE
SET Name = #Name
WHERE Id = #ID and #ID > 1
END
This should do it.
ALTER PROCEDURE [dbo].[SP_MY_STORED_PROCEDURE] ( #ID INT, #NAME VARCHAR(50) )
AS
BEGIN
IF #ID > 1
UPDATE MY_TABLE
SET Name = #Name
WHERE Id = #ID
END
I'm a stranger to SQL Server Triggers.
I ended up having a problem like this. Please have a look.
I have two tables 'users' & 'test'
CREATE TABLE users(
email VARCHAR(250),
rank FLOAT
);
CREATE TABLE test(
score INT,
total INT
);
I need to create a trigger to;
2.1 Update users rank by the value of avg ( avg = test.score / test.total)
2.2 Here's What I tried so far:
CREATE TRIGGER auto_rank ON dbo.test FOR INSERT
BEGIN
DECLARE #sc INT
DECLARE #tot INT
DECLARE #avg FLOAT
#tot = SELECT inserted.total FROM dbo.test
#sc = SELECT inserted.score FROM dbo.test
SET #avg=#sc/#tot
UPDATE dbo.users SET rank=#avg WHERE email=inserted.email
END
You missing the email in test from your table design, but it should have such column per your code:
UPDATE dbo.users SET rank=#avg WHERE email=inserted.email
Then you need a view instead of trigger in this case:
Create view user as (select email, score/total as rank from test group by email);
Hope this help.
Try this :
CREATE TRIGGER auto_rank ON dbo.test FOR INSERT
BEGIN
UPDATE a SET a.rank=b.rn
from
users a
inner join
(select email,inserted.score/inserted.total rn from inserted)b
on a.email=b.email
END
I have not tested this, but this should work fine.
You need to modify your tables so that the test table contains the email column:
CREATE TABLE test(score INT,
total INT,
email varchar(250)
);
Then you can create the trgiger like this:
CREATE TRIGGER [dbo].[auto_rank] ON [dbo].[test]
FOR INSERT
AS
BEGIN
DECLARE MyCursor CURSOR FOR
SELECT score, total, email FROM Inserted
DECLARE #sc INT
DECLARE #tot INT
DECLARE #email VARCHAR(30)
DECLARE #avg FLOAT
DECLARE #MSG VARCHAR(50)
OPEN MyCursor;
FETCH NEXT FROM MyCursor INTO #sc, #tot, #email
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #avg=#sc/#tot
UPDATE users SET rank=#avg WHERE users.email=#email
SELECT #MSG = 'email Updated ' + #email + '. New Rank is ' + Str(#avg, 25, 5);
PRINT #MSG
FETCH NEXT FROM MyCursor
END;
CLOSE MyCursor;
DEALLOCATE MyCursor;
END
Sorry for being so late to continue this thread but, I'm happy to say that I found the answer. it's because of you all.
So, here's what i did;
first;
CREATE TABLE users(
email VARCHAR(250),
rank FLOAT,
constraint pk_users PRIMARY KEY(email)
);
CREATE TABLE test(
email VARCHAR(250),
score INT,
total INT,
constraint pk_test PRIMARY KEY(email),
constraint fk_from_users FOREIGN KEY(email) references users(email)
);
create trigger trig_ex02 on dbo.test
after insert
as
begin
declare #score FLOAT
declare #total FLOAT
declare #average FLOAT
declare #msg varchar(100)
declare #email varchar(250)
set #email = (select email from inserted)
set #score = (select score from inserted)
set #total = (select total from inserted)
set #average =(#score/#total)
select #msg = 'SCORE IS'+ str(#score)+'TOTAL IS'+str(#total)+' AVERAGE IS ' +str(#average,25,5)+' END '
print #msg
UPDATE users SET rank=#average WHERE users.email=#email
end;
Say I have a self relation table as following :
ID - Name - ParentID
Now everytime that users insert sth in this table I would like to check if the Name inserted is already in the
rows where ParentID equals to the inserted one , if true then rollback the transaction.
But the problem is when I check the rows with the parentID from the inserted table the inserted row is already in the main table too. So, the trigger always rolls back the transaction.
Here is my trigger :
ALTER TRIGGER TG_Check_Existance_In_myTbl
ON myTbl FOR INSERT,UPDATE AS
DEClARE #result BIT
DECLARE #numberOfRows INT
DECLARE #counter INT
DECLARE #names nVARCHAR (30)
DECLARE #name NVARCHAR (30)
SET #result = 0
SET #numberOfRows = (SELECT COUNT (Name)
FROM myTbl
WHERE ParentID IN
(
SELECT ParentID
FROM inserted
)
)
SET #counter = 1;
SELECT #name = Name
FROM inserted
WHILE (#counter <= #numberOfRows)
BEGIN
WITH Q
AS
(
SELECT ROW_NUMBER()
OVER (ORDER BY Name) 'Row', Name
FROM myTbl WHERE ParentID IN
(
SELECT ParentID
FROM inserted
)
)
SELECT #names = Name
FROM Q
WHERE Row = #counter
IF #name = #names
SET #result=1;
SET #counter = #counter + 1
END
IF #result = 1
ROLLBACK TRAN
Unless I am missing something you are making this way too hard.
Why don't you use a unique constraint on the two columns?
table_constraint (Transact-SQL)