Auto Increment Column value whenever update happen on that row in sql server - sql

I want to maintain a column which will store that how many times a row has been modified.
So whenever the row has been updated I want to increase the column value.
I think I have to use trigger for that.But I am looking for an alternative solution.

IMHO trigger is the way to go, but if you sure that you control all your updates, then you can do as simple as this:
UPDATE mytable
SET somefield='newvalue',
update_count = update_count+1
WHERE id=n

CREATE TRIGGER CountRows
ON TestCount
after Update
AS
Update TestCount set Cnt = Cnt +1 where ID in (select ID from inserted)
GO
whenever some value in a row changes, the grigger adds +1 to the same row's Cnt column value.

Related

How to update “row number” column values 1..N after rows are DELETED?

I have this table:
When I make a delete of a box in this table, I need to make an increment of all the following rows.
For example: if I delete BoxNo '4' then the rows which are after the last row of BoxNo '4' (5,6,7,..etc) have to make an increment by 1 and be like (4,5,6,...etc.). I hope I succeed in explaining the problem.
Could you please kindly help me to perform this with an SQL Server query?
Thanks in advance.
Executing actions automatically after rows are deleted is what TRIGGERS are for.
In your case, it looks something like:
CREATE TRIGGER MyTable_AfterDelete
ON MyTable
FOR DELETE
AS
update MyTable set RowCounter = RowCounter + 1
from deleted
inner join MyTable on MyTable.BoxNo > deleted.BoxNo
GO
As you can see SQL Server doesn't raise a trigger after each row deleted but after each DELETE statement execution, which can involve several rows deleted, so we need to use the "deleted" pseudo-table to apply our action on every one of those rows deleted.
PS: This is how what you asked can be done, although I agree with the comments that say that you could structure better this problem instead of needing to update so many rows after every delete.
UPDATE
If you want to execute it manually on every delete instruction, instead of automatically on a trigger, you can pass a parameter to your DELETE statement on C# in order to update the posterior RowCounters. Something like:
delete from MyTable where BoxNo = #BoxNo
update MyTable set RowCounter = RowCounter + 1 where BoxNo > #BoxNo

Oracle SQL: how to enforce only one value may be 'checked'?

So I have a table in a database which contains the column "SELECTED". The values in this column can only be "CHECKED" or "UNCHECKED". I would like to enforce "CHECKED" can only be used once (like a radiobutton) through a PL/SQL trigger, though I cannot think of how to do this.
First, the idea (in case it didn't become clear):
Initial table "dummy":
ID | SELECTED
--------------
1 | 'UNCHECKED'
2 | 'CHECKED'
3 | 'UNCHECKED'
Then, I execute this query:
UPDATE dummy
SET SELECTED = 'CHECKED'
WHERE ID = 3;
Through a PL/SQL trigger, I'd like to have my table "dummy" to look like this after the execution:
ID | SELECTED
--------------
1 | 'UNCHECKED'
2 | 'UNCHECKED'
3 | 'CHECKED'
I hope you get the idea. I myself have tried to solve this, without success. I came up with the following code:
CREATE OR REPLACE TRIGGER DUMMY_ONE_CHECKED
AFTER INSERT OR UPDATE ON DUMMY
FOR EACH ROW
DECLARE
v_checked_is_present DUMMY.SELECTED%TYPE;
BEGIN
SELECT SELECTED
INTO v_checked_is_present
FROM DUMMY
WHERE SELECTED = 'CHECKED';
IF v_checked_is_present IS NOT NULL THEN
UPDATE DUMMY
SET SELECTED = 'UNCHECKED'
WHERE SELECTED = 'CHECKED';
UPDATE DUMMY
SET SELECTED = 'CHECKED'
WHERE ID = :NEW.ID;
END IF;
END;
However, I get the errors ORA-04091, ORA-06512 and ORA-04088 with the following message:
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
Clearly, this is not the right solution. I wonder how I could accomplish what I would like to do (if possible at all)?
Thank you in advance!
I would not design it that way. The database should enforce the rules, not automatically attempt to fix violations of them.
So, I'd enforce that only one row can be CHECKED at a time, like this:
CREATE UNIQUE INDEX dummy_enforce_only_one ON dummy ( NULLIF(selected,'UNCHECKED') );
Then, I'd make it the responsibility of calling code to deselect other rows before selecting a new one (rather than trying to have a trigger do it).
I know that doesn't answer the text of your question, but it does answer the title of your question: "how to enforce only one value..."
I'm not sure a trigger is the best approach to this problem. The trigger needs to update all the records for every update -- even worse, the rows are in the same table leading to the dreaded mutating table error.
How about a different table structure altogether? The idea is just to keep track of the last time something was "checked" and then use the maximum timestamp:
create table t_dummy (
id int,
checkedtime timestamp(6)
);
create view dummy as
select t_dummy.id,
(case when checkedtime = maxct then 'CHECKED' else 'UNCHECKED') as selected
from t_dummy cross join
(select max(checktime) as maxct from t_dummy) x;
This should be simpler to implement than a trigger.
One way to implement this is to use a COMPOUND TRIGGER. A compound trigger is one which has code which fires at each of the possible triggering points (BEFORE STATEMENT, BEFORE ROW, AFTER ROW, and AFTER STATEMENT). Let's look at how to handle your requirement:
CREATE OR REPLACE TRIGGER DUMMY_CHECKED_TRG
FOR INSERT OR UPDATE ON DUMMY
COMPOUND TRIGGER
TYPE NUMBER_TABLE IS TABLE OF NUMBER;
tblDUMMY_IDS NUMBER_TABLE;
BEFORE STATEMENT IS
BEGIN
tblDUMMY_IDS := NUMBER_TABLE();
END BEFORE STATEMENT;
AFTER STATEMENT IS
BEGIN
IF tblDUMMY_IDS.COUNT > 0 THEN
UPDATE DUMMY d
SET d.SELECTED = 'UNCHECKED'
WHERE d.ID <> tblDUMMY_IDS(tblDUMMY_IDS.LAST) AND
d.SELECTED = 'CHECKED';
END IF;
END AFTER STATEMENT;
AFTER EACH ROW IS
BEGIN
-- If the new value of `SELECTED` on this row is 'CHECKED'
-- save the ID of the row in tblDUMMY_IDS
IF :NEW.SELECTED = 'CHECKED' THEN
tblDUMMY_IDS.EXTEND;
tblDUMMY_IDS(tblDUMMY_IDS.LAST) := :NEW.ID;
END IF;
END AFTER EACH ROW;
END TABLE1_NUM_TRG;
In the BEFORE STATEMENT portion of the trigger we just allocate a table (variable length collection object) to hold ID values. This portion of the trigger is executed once, before any rows have been processed by the trigger.
In the AFTER EACH ROW section of the trigger we look at the SELECTED field of the row, and if it's 'CHECKED' we save its ID value in the table we allocated earlier.
The AFTER STATEMENT section of the trigger is where the real work gets done - and it's only a single SQL statement. The reason we defer the real work of the trigger until the AFTER STATEMENT section is because code which executes here will not raise the dreaded "MUTATING TABLE" exception. What we do is we take the last ID value which we found was associated with a row which had SELECTED = 'CHECKED'. This is the row which we want to remain CHECKED - every other row in the table should be UNCHECKED. So we execute an UPDATE statement, saying in effect "set SELECTED to 'UNCHECKED' on every row in the table whose ID is not the one we've got, and whose current value of SELECTED is CHECKED". Normally this will only update one row - but it will handle the case where in a single SQL statement sets a bunch of rows to CHECKED.
I believe compound triggers became available in 10g, so if you're on that version of Oracle or later you should be good.
Hope this helps.
Best of luck.
As I understand, you want to have only one row in the whole table, which could contain CHECKED value. But your way will not work.
I've just invented a new way how to do this. Maybe, it is a bit complicated way. Change your selected column type to number, and fill it with consequent numbers (for example, with sequence). Then consider column with maximal value as "selected". This gives you a lot of advantages: to change selected row you just need to take next value from a sequence and put it in desired row (you don't need to update all rows), you need only one query for that, and you never meet mutation problem. Disadvantages - quite hard to get selected row and impossible (hard) to "deselect all".
Alternative approach using table with one row only (enforced with PK). The `BUTTON_ID contains the ID of the selected button (1-3 or NULL if all buttons are un-checkedd). The button per row result is presented in a view.
create table button
(
id number primary key check (ID in (1)),
button_id number check (button_id in (1,2,3))
);
create view v_button as
with r3 as (select rownum button_id from dual connect by level <= 3)
select
case when button.button_id = r3.button_id then 'SELECTED' else 'UNSELECTED' end as button_code
from r3 cross join button
;
Initialize with
insert into button values(1,1);
gives
select * from v_button;
BUTTON_CODE
-----------
SELECTED
UNSELECTED
UNSELECTED
Switch simple with an update statement:
update button set button_id = 3;
gives
BUTTON_CODE
-----------
UNSELECTED
UNSELECTED
SELECTED
To de-select all simple set to NULL
update button set button_id = NULL;
BUTTON_CODE
-----------
UNSELECTED
UNSELECTED
UNSELECTED
Would be easier if you knew what needed to be unchecked. But if you can't, then unckeck everything.
UPDATE dummy
SET SELECTED = 'UNCHECKED';
Then check the one that you know should be checked.
UPDATE dummy
SET SELECTED = 'CHECKED'
WHERE ID = 3;
Why not use a boolean for this?
--EDIT-- (boolean example)
UPDATE dummy
SET SELECTED = 0;
Then check the one that you know should be checked.
UPDATE dummy
SET SELECTED = 1
WHERE ID = 3;

SQL Trigger not working upon update

I have the following code (in sql server - via 2012): i can't seem to get it right. any suggestions.
Table:
select top 1000 [supplier],
[item],
[reorder_level],
[current_inventory],
[reorder],
from [clinic].[dbo].[emr_suppliers]
I'm working on a trigger and a bit stuck.
CREATE TRIGGER reorder_supplies
ON emr_suppliers
After insert, update
As BEGIN
update emr_suppliers
set reorder = 'yes'
where (emr_suppliers.reorder = emr_suppliers.current_inventory or emr_suppliers.reorder > emr_suppliers.current_inventory)
update emr_suppliers
set reorder = 'no'
where emr_suppliers.reorder < emr_suppliers.current_inventory
END
What the trigger has to do is compare the Current Inventory with the Reorder Level column, and if the value of the Current Inventory is equal to or less than the Reorder Level, it will put a value of Yes in the Reorder column, and if it is not, then it will put a No value instead.
The trigger itself looks syntactically correct.
However, I don't think it's a solution with a decent performance since each and every row of the emr_suppliers table is touched twice, even though there was no data change at all for most of the rows (e.g. after insert of a new row or update of a single value).
I'd use a solution based on the internal inserted table together with a CASE expression:
UPDATE emr_suppliers
SET reorder =
CASE WHEN emr_suppliers.reorder < emr_suppliers.current_inventory THEN 'no'
WHEN emr_suppliers.reorder >= emr_suppliers.current_inventory THEN 'yes'
ELSE reorder -- don't change the value
END
FROM emr_suppliers INNER JOIN inserted ON emr_suppliers.primary_key = inserted.primary_key

Update a table and return both the old and new values

Im writing a VB app that is scrubbing some data inside a DB2 database. In a few tables i want to update entire columns. For example an account number column. I am changing all account numbers to start at 1, and increment as I go down the list. Id like to be able to return both the old account number, and the new one so I can generate some kind of report I can reference so I dont lose the original values. Im updating columns as so:
DECLARE #accntnum INT
SET #accntnum = 0
UPDATE accounts
SET #accntnum = accntnum = #accntnum + 1
GO
Is there a way for me to return both the original accntnum and the new one in one table?
DB2 has a really nifty feature where you can select data from a "data change statement". This was tested on DB2 for Linux/Unix/Windows, but I think that it should also work on at least DB2 for z/OS.
For your numbering, you might considering creating a sequence, as well. Then your update would be something like:
CREATE SEQUENCE acct_seq
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO CYCLE
CACHE 24
;
SELECT accntnum AS new_acct, old_acct
FROM FINAL TABLE (
UPDATE accounts INCLUDE(old_acct INT)
SET accntnum = NEXT VALUE FOR acct_seq, old_acct = accntnum
)
ORDER BY old_acct;
The INCLUDE part creates a new column in the resulting table with the name and the data type specified, and then you can set the value in the update statement as you would any other field.
A possible solution is to add an additional column (let's call it oldaccntnum) and assign old values to that column as you do your update.
Then drop it when you no longer need it.
Here's what I'd do:
-- create a new table to track the changes.
- with columns identifying a unique key, old-vale, new-value, timestamp
-- create a trigger on the accounts table
to write the old and new values to the new table.
But, not knowing all the conditions, it may not be worth the trouble.

Delete one column on the criteria of another SQL Server 2008

One attribute in a table became corrupted after a certain point in a table of mine. I want to delete every pat_coun attribute if it has an ID that begins with 11 (number, not text). So I don't want to get rid of any of the records in the database, just clear out the attribute pat_coun if it's ID begins with 11
DELETE pat_coun from myTable
WHERE id %11
Just want to make sure this is right before I go deleting stuff. Thanks.
To clear out an attribute, do NOT use the DELETE function! That deletes a row from your table!
You need to use UPDATE instead:
UPDATE myTable
SET pat_coun = NULL
WHERE id LIKE '11%'
If you want to delete a record (a row) you can use
DELETE FROM myTable
WHERE condition
If you just want to "clear" a particular column you should use
UPDATE myTable
SET pat_coun = 0 // or NULL or whatever you please
WHERE condition
For condition IMHO you should convert your number to string and check like this
WHERE CONVERT(VARCHAR(20), pat_coun) LIKE '11%'
try this
update myTable
set pat_coun = null
where id like '11%'