I have a table like this in SQL Server:
varID(PK) dataID(PK) is_used
A 1 0
B 1 0
Then I'm loading data to update is_used to 1 if the varID/dataID combo exists and add it in otherwise.
So I have to insert/update these varID/dataID combos.
varID(PK) dataID(PK)
B 1
C 1
So the updated table would look like this:
varID(PK) dataID(PK) is_used
A 1 0
B 1 1
C 1 1
What's the easiest way to do this? I will do it in a stored procedure.
Procedure tries to update is_used given a key. If unsuccessful, inserts new row. Note I put 0 as default value for is_used - I think is_used = 1 for (C, 1) is an inadvertence.
create proc AddVarDataCombo (#varID varchar(100), #dataID int)
as
set nocount on
update ATable
set is_used = 1
where varID = #varID
and dataID = #dataID
if ##rowcount = 0
begin
insert into ATable
values (#varID, #dataID, 0)
end
Related
I have two boolean (bit) fields that I want to remain mutually exclusive. They can both be false, but if one gets set to true the other must be set to false.
I tried to create 2 triggers but wound up creating an infinite loop (that stopped after 32 iterations). Apparently they trigger each other. Here is the code I tried:
CREATE OR ALTER TRIGGER dbo.TriggerNameA
ON dbo.TableName
AFTER UPDATE
AS
IF UPDATE(FieldA)
BEGIN
UPDATE dbo.TableName
SET FieldB =
CASE WHEN dbo.TableName.FieldA = 1
THEN 0
ELSE 1
END
FROM dbo.TableName INNER JOIN inserted ON dbo.TableName.ID = inserted.ID
END;
;
GO
CREATE OR ALTER TRIGGER dbo.TriggerNameB
ON dbo.TableName
AFTER UPDATE
AS
IF UPDATE(FieldB)
BEGIN
UPDATE dbo.TableName
SET FieldA =
CASE WHEN dbo.TableName.FieldB = 1
THEN 0
ELSE 1
END
FROM dbo.TableName INNER JOIN inserted ON dbo.TableName.ID = inserted.ID
END;
;
GO
Any idea how to avoid the infinite loop?
Using a Check constraint removes the need for a trigger:
SQL Fiddle
MS SQL Server 2017 Schema Setup:
create table MyTable
(
ID int Identity,
FieldA bit,
FieldB bit,
CONSTRAINT Chk_Fields CHECK (FieldA <> 1 OR FieldB <> 1)
)
INSERT INTO MyTable(FieldA, FieldB)
VALUES (0,0),(0,1),(1,0)
Query 1:
select *
from MyTable
Results:
| ID | FieldA | FieldB |
|----|--------|--------|
| 1 | false | false |
| 2 | false | true |
| 3 | true | false |
Query 2:
INSERT INTO MyTable(FieldA, FieldB)
VALUES (1,1),(0,1),(1,0)
Results:
The INSERT statement conflicted with the CHECK constraint "Chk_Fields". The conflict occurred in database "db_18_941bc9", table "dbo.MyTable".
Query 3:
UPDATE MyTable
SET FieldB = 1
WHERE ID = 3
Results:
The UPDATE statement conflicted with the CHECK constraint "Chk_Fields". The conflict occurred in database "db_18_941bc9", table "dbo.MyTable".
EDIT As #CharlieFace mentions in his comment, the equivalent check
constraint of (FieldA = 0 OR FieldB = 0) is more readable
Okay, I apologize for my previous answer, but I'm sure I got it now. Thanks to #DB<>Fiddle for pointing out that it still was experiencing recursion (or nesting, not sure).
So I used the trigger_nestlevel function. Tested and now (finally) it works:
create table MyTable
(
ID int Identity,
FieldA bit,
FieldB bit
)
go
CREATE OR ALTER TRIGGER dbo.Trigger1
ON dbo.MyTable
AFTER UPDATE
AS
IF TRIGGER_NESTLEVEL() = 1
BEGIN
IF UPDATE(FieldA)
BEGIN
UPDATE dbo.MyTable SET dbo.MyTable.FieldB = 0
FROM dbo.MyTable INNER JOIN inserted ON dbo.MyTable.ID = inserted.ID;
END;
END;
;
GO
CREATE OR ALTER TRIGGER dbo.Trigger2
ON dbo.MyTable
AFTER UPDATE
AS
IF TRIGGER_NESTLEVEL() = 1
BEGIN
IF UPDATE(FieldB)
BEGIN
UPDATE dbo.MyTable SET dbo.MyTable.FieldA = 0
FROM dbo.MyTable INNER JOIN inserted ON dbo.MyTable.ID = inserted.ID;
END;
END;
;
GO
INSERT INTO MyTable(FieldA, FieldB)
VALUES (0,0)
SELECT * FROM MyTable;
-- 0 0
UPDATE MyTable
SET FieldA = 1
WHERE ID = 1;
SELECT * FROM MyTable;
-- 1 0
UPDATE MyTable
SET FieldB = 1
WHERE ID = 1;
SELECT * FROM MyTable;
-- 0 1
Then I can go back and forth and they continue to switch the way I need. Whew!
Here's an alternative using an INSTEAD OF TRIGGER to ensure that on an UPDATE only one of the fields is set to 1. I have made a decision in the trigger that if FieldA in the virtual INSERTED table is set to 1 then I set FieldB to zero:
SetUp
create table MyTable
(
ID int Identity,
FieldA bit,
FieldB bit
)
CREATE TRIGGER TriggerNameA ON MyTable
INSTEAD OF UPDATE
AS
IF UPDATE(FieldA)
BEGIN
MERGE [MyTable] AS t1
USING (SELECT * FROM INSERTED) AS t2
ON t1.ID = t2.ID
WHEN MATCHED THEN
UPDATE SET t1.[FieldA] = t2.FieldA,
t1.[FieldB] = CASE WHEN t2.FieldA = 1 THEN 0 ELSE t2.FieldB END;
END
ELSE IF UPDATE(FieldB)
BEGIN
MERGE [MyTable] AS t1
USING (SELECT * FROM INSERTED) AS t2
ON t1.ID = t2.ID
WHEN MATCHED THEN
UPDATE SET t1.[FieldB] = t2.FieldB,
t1.[FieldA] = CASE WHEN t2.FieldB = 1 THEN 0 ELSE t2.FieldA END;
END;
-- Add Else for cases where FieldA & FieldB weren't updated if required
Result 1
INSERT INTO MyTable(FieldA, FieldB)
VALUES (0,0),(0,1),(1,0);
SELECT * FROM MyTable
ID FieldA FieldB
1 False False
2 False True
3 True False
Result 2
UPDATE MyTable
SET FieldB = 1
WHERE ID = 1
SELECT * FROM MyTable
ID FieldA FieldB
1 False True
2 False True
3 True False
Result 3
UPDATE MyTable
SET FieldB = 1
WHERE ID = 3
SELECT * FROM MyTable
ID FieldA FieldB
1 False True
2 False True
3 False True
Result 4
UPDATE MyTable
SET FieldA = 1
WHERE ID = 2
SELECT * FROM MyTable
ID FieldA FieldB
1 False True
2 True False
3 True False
DB<>Fiddle
Edited to Update fieldA or FieldB depending on what column is updated
in the UPDATE statement
I have a SQL Server table in the below format and I am looking for a solution to keep the QuestionOrder in the correct sequence for that QuestionnaireID when I delete a record from this table. So let's say I delete the row with ID = 3, the QuestionOrder column for all rows with QuestionnaireID = 1 will be updated to be in the right sequence starting from 1 to N.
ID
QuestionnaireID
Question
QuestionOrder
...
1
1
Question-1
1
...
2
1
Question-2
2
...
3
1
Question-3
3
...
4
1
Question-4
4
...
5
1
Question-5
5
...
6
2
Question-1
1
...
7
2
Question-2
2
...
Try this, assuming your table's name is QuestTable.
First create the stored procedure:
CREATE PROCEDURE dbo.usp_QuestionOrder_DeleteAndReOrder
#QuestionnaireID int,
#QuestionOrder int
AS
BEGIN
SET NOCOUNT ON;
DELETE
FROM QuestTable
WHERE QuestionnaireID = #QuestionnaireID
AND QuestionOrder = #QuestionOrder;
UPDATE QuestTable
SET QuestionOrder = QuestionOrder - 1
WHERE QuestionOrder > #QuestionOrder;
END
Then make the call:
DECLARE #QuestionnaireID int = 1,
#QuestionOrder int = 3;
EXEC dbo.usp_QuestionOrder_DeleteAndReOrder
#QuestionnaireID, #QuestionOrder;
SELECT *
FROM QuestTable
Consider the following scenario
Suppose there are three fields in a database table
------------------------------------------
PrmiaryKey | Column A | Column B
-----------------------------------------
I need to enforce that for values in Column B should have unique values for Column A
Example
Col B Col A
12 13 (OK)
14 15 (OK)
15 16 (OK)
12 13 (OK)
15 16 (OK)
14 17 (not OK)
Since value 14 previously have value 15 under Column B. So it should not have a different value than 15. I need to enforce this behavior from database side. Is it there a particular constraint that i need to have to sort this out
Thanks in Advance.
A constraint operates on the fields within a row. The only "constraint" that takes into account values in other rows is a unique constraint...and that really just creates an unique index. There is no way to enforce your requirement using just constraints. You have to use a trigger.
create trigger TableTrigger_AIU
on Table
after insert, update
as begin
declare
#Dups int;
set NoCount on;
select #Dups = count(*)
from Inserted i
join Table t
on t.ColA = i.ColA
and t.ColB <> i.ColB;
if #Dups > 0 begin
raise holy_blue_well_you_know;
end;
end;
You could try putting a CHECK CONSTRAINT:
CREATE FUNCTION dbo.CheckDuplicateEntry(#id INT, #colA INT, #colB INT)
RETURNS INT
AS
BEGIN
DECLARE #ret INT
IF EXISTS(SELECT 1 FROM Test WHERE ID <> #id AND ColB = #colB) BEGIN
IF EXISTS(SELECT 1 FROM Test WHERE ID <> #id AND ColB = #colB AND ColA = #colA) BEGIN
SET #ret = 1
END
ELSE BEGIN
SET #ret = 0
END
END
ELSE BEGIN
SET #ret = 1
END
RETURN #ret
END
ALTER TABLE [TableName]
ADD CONSTRAINT ConstCheckDuplicateEntry
CHECK (dbo.CheckDuplicateEntry(ID, ColA, ColB) = 1);
I have two tables with FOREIGN KEY([Table_ID])
Columns
ID Table_ID ActiveFlag
1 1 0
2 2 1
3 1 1
4 3 0
Sys_Tables
Table_ID Name
1 Request
2 Plan
3 Contecst
I'm writing a stored procedure that returns any column for each table.
Example Output for values above
--first output table
ID Table_ID ActiveFlag
1 1 0
3 1 1
--second output table
ID Table_ID ActiveFlag
2 2 1
--third output table
ID Table_ID ActiveFlag
4 3 0
My idea is this
Select c.*
from Ccolumns c
inner join Sys_tables t
on t.Table_ID = c.Table_ID and t.Table_ID = #Parameter
My problem, i do't know how to make a loop for each row. I need the best way. Example i can use following loop:
DECLARE #i int = 0
DECLARE #count int;
select #count = count(t.Table_ID)
from Sys_tables t
WHILE #i < #count BEGIN
SET #i = #i + 1
--DO ABOVE SELECT
END
But this is not entirely correct. Example my Sys_tables such data may be
Table_ID Name
1 Request
102 Plan
1001 Contecst
Do You have any idea?
There are couple ways you can achieve that: loops and cursors, but first of all you need to know that it's a bad idea: either are very slow, anyway, here's some kind of loop sample:
declare #row_ids table (
id INT IDENTITY (1, 1),
rid INT
);
insert into #row_ids (rid) select someIdField from SomeTable
declare #cnt INT = ##ROWCOUNT
declare #currentRow INT = 1
WHILE (#currentRow <= #cnt)
BEGIN
SELECT rid FROM #row_ids WHERE id = #currentRow
SET #currentRow = #currentRow + 1
END
I guess you're using SQL Server, right?
Then, you can use a CURSOR as here: How to write a cursor inside a stored procedure in SQL Server 2008
Hai guys,
I have a table with a column named Is_Deleted and my query is
select Is_Deleted from Stock where Stock.Mat_Id=1
alt text http://www.freeimagehosting.net/uploads/aaaff13d8a.jpg
Now i have to write a condition to check whether all values are 1 else i have to terminate my loop.. How it can be done? any suggestions...
This should do what you're after.
IF EXISTS(SELECT 1 FROM (select DISTINCT Is_Deleted from Stock where Stock.Mat_Id=1) a WHERE Is_Deleted <> 1)
BEGIN
-- Terminate the loop
END
ELSE
BEGIN
-- Perform action
END
select exists(select 1 from Stock where Mat_Id = 1 and
(is_deleted is null or is_deleted <> 1))
A simple exists is all you need
IF EXISTS (SELECT * FROM Stock.Is_Deleted = 0 AND Stock.Mat_Id = 1)
...
If you are looping through different Mat_Ids...
...
--Get first #Mat_Id
WHILE #Mat_Id IS NOT NULL
BEGIN
IF EXISTS (SELECT * FROM Stock.Is_Deleted = 0 AND Stock.Mat_Id = #Mat_Id)
BEGIN
...
END
SET #Mat_Id = NULL
--Get next #Mat_Id
END
...