How to change a field data after time t in a table? - sql

Is it possible to change the data in table automatically after time i.
Say time i = 10 sec.
Table t, has column x1,x2,x3.
x3 is boolean as datatype. x3'll be false by
Now suppose I changed the value in t.x3 (any row) to True then after 10 sec it should change itself to False.
Is it possible.? By some trigger or cursor or procedure or something?
Alteast need a keyword to google it.
Thank You!

You can do this without a trigger. Just use a computed column.
For instance:
create table t (dte datetime,
flag as (case when dte < dateadd(second, -10, getdate()) then 0 else 1
end)
);
This is a computed column. It will recalculate when it is queried, doing the calculation you are asking for.

This scenario you are asking for looks like an anti-pattern. Exists a command to wait for random amount of time ( WAITFOR DELAY '00:00:02'; ) but, usually, is not a good idea to use it:
Caution+
Including WAITFOR will slow the completion of the SQL Server process and can result in a timeout message in the application. If necessary, adjust the timeout setting for the connection at the application level.
In my opinion, the easy way is to create a view over your table and calculate the field on the fly:
CREATE TABLE t ( PrimaryKey ....,
x3 bit,
t datetime default gettime(),
);
CREATE VIEW v as
SELECT PrimaryKey,
(CASE WHEN x3 = 1 and datediff(second, t, getdate()) > 10
THEN 1
ELSE 0
END ) as x3 --<-- lets supose this is your field.
FROM t;
Then, you can select from view to check for your calculate field:
SELECT x3 FROM v; --<-- select from v instead from t.
You can use a trigger to keep t field up to date on change x3:
CREATE TRIGGER updateModified
ON dbo.t
AFTER UPDATE
AS
UPDATE dbo.t
SET t = getdate()
FROM Inserted i
WHERE dbo.t.PrimaryKey = i.PrimaryKey

You can use a trigger to automatically update the values after something has changed on the table. But this change will be affected within milliseconds as the trigger executes straight after the update/insert/delete.
The second method you can do is to create a scheduled Database job which will run every 10 seconds (In this case) or any specific interval, which will make the updates in the required columns.
Refer this Article for more information on scheduling the DB Jobs

Related

Create a trigger after insert that update another table

I'm trying to create a trigger that update another table after an insert, when the state of the swab test changes from positive to negative.
I have created this trigger, but the problem is that every time there is a user with a negative swab, the user id is copied to the table, even if this user has never been positive. Maybe, have I to compare date?
Create or replace trigger trigger_healed
After insert on swab_test
For each row
Begin
if :new.result = 'Negative' then
UPDATE illness_update
SET illness_update.state = 'healed'
WHERE illness_update.id_user = :new.id_user;
end if;
end;
This is the result that I'm trying to get.
SWAB_TEST
id_user id_swab swab_result date
1 test1 'positive' May-01-2020
1 test1 'negative' May-08-2020
2 test2 'negative' May-02-2020
ILLNESS_UPDATE
id_user state date
1 'healed' May-08-2020
What you ask for would require the trigger to look at the existing rows in the table that is being inserted on - which by default cannot be done, since a trigger cannot action the table it fires upon.
Instead of trying to work around that, I would suggest simply creating a view to generate the result that you want. This gives you an always up-to-date perspective at your data without any maintenance cost:
create view illness_update_view(id_user, state, date) as
select id_user, 'healed', date
from (
select
s.*,
lag(swab_result) over(partition by id_user order by date) lag_swab_result
from swab_test s
) s
where lag_swab_result = 'positive' and swab_result = 'negative'
The view uses window function lag() to recover the "previous" result of each row (per user). Rows that represents transitions from a positive to a negative result are retained.
As #GMB indicates you cannot do what you are asking with a standard before/after row trigger as it that cannot reference swab_test as that is the table causing the trigger to fire (that would result in an ORA-04091 mutating table error). But you can do this in a Compound Trigger (or an After statement). But before getting to that I think your data model has a fatal flaw.
You have established the capability for multiple swab tests. A logical extension for this being that each id_swab tests for a different condition, or a different test for the same condition. However, the test (id_swab) is not in your illness update table. This means if any test goes to negative result after having a prior positive result the user is healed from ALL tests. To correct this you need to a include id_swab id making the healed determination. Since GMB offers the best solution I'll expand upon that. First drop the table Illness_update. Then create Illness_update as a view. (NOTE: in answer to your question you DO NOT need a trigger for the view, everything necessary is in the swab_test; see lag windowed function.
create view illness_update(id_user, state, swab_date) as
select id_user, id_swab, 'healed' state,swab_date
from (
select
s.*
, lag(swab_result) over(partition by id_user, id_swab
order by id_user, id_swab, swab_date) as lag_swab_result
from swab_test s
) s
where lag_swab_result = 'positive'
and swab_result = 'negative';
Now, as mentioned above, if your assignment requires the use of a trigger then see fiddle. Note: I do not use date (or any data type) as a column name. Here I use swab_date in all instances.

Is it possible to auto increment data type "time(7)" in SQL server?

Currently I have a table which contains a row for "time", which have the data type "time(7)". I've made the default value for 05:00:00 since, that's where I wish to start the time. For every entered data in the table, the time have to go "up" by 2 minutes. I was wondering if there is anyway that I can auto increment it, so it automatically increases the time by 2 minutes?
I've tried to look into some of the features in Microsoft SQL Server Management Studio (which I use), but haven't really found a way to auto incremement it, if it's a possibility.
Create a trigger that on insert updates inserted value
Get previous entry and add 2 minutes to new row
Example:
Create TRIGGER updatetime
ON dbo.TimeTable
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #NewTime time
SELECT Top 1 #NewTime = DATEADD(MINUTE, 2,tbl.tmt_time)
FROM (
SELECT TOP 2 *
FROM [dbo].[TimeTable]
ORDER BY tmt_Id DESC) as tbl
ORDER BY tmt_ID ASC
UPDATE tm
SET tmt_time = #NewTime
FROM [dbo].[TimeTable] tm
INNER JOIN inserted i ON i.tmt_ID = tm.tmt_ID
END
GO
You could use lag as well instead of the top 1 top 2 select.
This query also adds 2 minutest to your 1st entry. So you may want to seed the first row with your default value.
References:
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
https://learn.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql
https://learn.microsoft.com/en-us/sql/t-sql/queries/update-transact-sql

How can I make a SQL script run after a certain time without waiting for it?

I'm trying to create a SQL script with a delay.
I could use:
blah blah
WAITFOR DELAY '00:30:00'
blah blah
but using a script like this requires that I sit through the 30 minutes.
Leaving the website will cancel the script.
The problem is that I want to change something in a table, and then change it back automatically 30 minutes later.
The person making the change will leave the webpage, so any kind of client-side script is out of the question.
Nor can I wait for the person to return and make the change if 30 minutes have passed, the change must happen after 30 minutes regardless.
Is there anyway to do this without making a service or any other program on the server?
Using only ASP/SQL programming.
If this is impossible, how do I make a service or program on the server to make this change?
Must be able to start this from the website using ASP.
I personally would not approach the situation this way. I don't know exactly what your data structure is, or why you need to change something for 30 minutes, but I would use a 'Change' table.
So you might have something like
MainTable (ID, Column1, Column2, Column3, Column4);
ChangeTable (ID, Column1, Column2, Column3, Column4, CreatedDateTime);
Whenever you make your change instead of updating your main table you can simply insert the values you would be updating to into the ChangeTable (I'm assuming SQL-Server based on WAITFOR).
I would then make a view like so:
CREATE VIEW dbo.MainView
AS
SELECT m.ID,
Column1 = ISNULL(c.Column1, m.Column1),
Column2 = ISNULL(c.Column2, m.Column2),
Column3 = ISNULL(c.Column3, m.Column3)
FROM dbo.MainTable m
OUTER APPLY
( SELECT TOP 1 c.Column1, c.Column2, c.Column3
FROM dbo.ChangeTable c
WHERE c.ID = m.ID
AND c.CreatedDate >= DATEADD(MINUTE, -30, GETDATE())
ORDER BY c.CreatedDate DESC
) c;
Then refer to this throughout the website.
If space is an issue you could set up a nightly Job to delete any old entries, e.g. set the following to run at 00:30
DELETE ChangeTable
WHERE CreatedDate < CAST(GETDATE() AS DATE);
Personally, although others on this board would never think it is professional... I use website uptime monitoring services to run scripts. For example this one will hit an asp page of your choosing every 30 minutes http://www.serviceuptime.com/free_monitoring.php.
For ease of development I use different monitoring services to load scripts. There are more professional ways of doing so such as making a VBS script and running it through the Task Manager which I do for long running scripts but for simple things like checking a mail queue every so often I just use a monitoring service to load the page often enough to do what I want.
Figured it out by using SQL Server Agent and SQL procedures.
This is basically how my code is built up now:
Make the temporary change in the table
UPDATE table SET column = 'temp_value' WHERE column = 'normal_value'
Check if the procedure is there, if so, delete it. Create a procedure to revert the changes in the table.
IF EXISTS ( SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'myRevertProcedure')
AND type IN ( N'P', N'PC' ) )
DROP PROCEDURE myRevertProcedure
CREATE PROCEDURE myRevertProcedure
AS
BEGIN
WAITFOR DELAY '00:30:00'
UPDATE table SET column = 'normal_value' WHERE column = 'temp_value'
END
I've created a job in the SQL Server Agent that runs the following:
IF EXISTS ( SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'myRevertProcedure')
AND type IN ( N'P', N'PC' ) )
BEGIN
EXEC MyProc
DROP PROCEDURE myRevertProcedure
END
The reason the job does not simply revert the change itself is because the user shall set the delay.
If the delay were allways to be 30 mins, I could've made the job run the following:
IF EXISTS (SELECT * FROM table WHERE column = 'temp_value')
BEGIN
WAITFOR DELAY '00:30:00'
UPDATE table SET column = 'normal_value' WHERE column = 'temp_value'
END
By doing this, I would not need any procedure.
BTW: The job runs every few seconds.

How to set a default value for one column in SQL based on another column

I'm working with an old SQL 2000 database and I don't have a whole lot of SQL experience under my belt. When a new row is added to one of my tables I need to assign a default time value based off of a column for work category.
For example, work category A would assign a time value of 1 hour, category B would be 2 hours, etc...
It should only set the value if the user does not manually enter the time it took them to do the work. I thought about doing this with a default constraint but I don't think that will work if the default value has a dependency.
What would be the best way to do this?
I would use a trigger on Insert.
Just check to see if a value has been assigned, and if not, go grab the correct one and use it.
Use a trigger as suggested by Stephen Wrighton:
CREATE TRIGGER [myTable_TriggerName] ON dbo.myTable FOR INSERT
AS
SET NOCOUNT ON
UPDATE myTable
SET
timeValue = '2 hours' -- assuming string values
where ID in (
select ID
from INSERTED
where
timeValue = ''
AND workCategory = 'A'
)
Be sure to write the trigger so it will handle multi-row inserts. Do not process one row at a time in a trigger or assume only one row will be in the inserted table.
If what you are looking for is to define a column definition based on another column you can do something like this:
create table testable
(
c1 int,
c2 datetime default getdate(),
c3 as year(c2)
);
insert into testable (c1) select 1
select * from testable;
Your result set should look like this :
c1 | c2 | c3
1 | 2013-04-03 17:18:43.897 | 2013
As you can see AS (in the column definition) does the trick ;) Hope it helped.
Yeah, trigger.
Naturally, instead of hard-coding the defaults, you'll look them up from a table.
Expanding on this, your new table then becomes the work_category table (id, name, default_hours), and you original table maintains a foreign key to it, transforming fom
(id, work_category, hours) to (id, work_category_id, hours).
So, for example, in a TAG table (where tags are applied to posts) if you want to count one tag as another...but default to counting new tags as themselves, you would have a trigger like this:
CREATE TRIGGER [dbo].[TR_Tag_Insert]
ON [dbo].[Tag]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Tag
SET [CountAs] = I.[ID]
FROM INSERTED AS I
WHERE I.[CountAs] IS NULL
AND dbo.Tag.ID = I.ID
END
I can think of two ways:
triggers
default value or binding (this should work with a dependency)
Triggers seem well explained here, so I won't elaborate. But generally I try and stay away from triggers for this sort of stuff, as they are more appropriate for other tasks
"default value or binding" can be achieved by creating a function e.g.
CREATE FUNCTION [dbo].[ComponentContractor_SortOrder] ()
RETURNS float
AS
BEGIN
RETURN (SELECT MAX(SortOrder) + 5 FROM [dbo].[tblTender_ComponentContractor])
END
And then setting the "default value or binding" for that column to ([dbo].ComponentContractor_SortOrder)
Generally I steer away from triggers. Almost all dbms have some sort of support for constraints.
I find them easier to understand , debug and maintain.

SQL - how to check table for new data?

I need to create a stored procedure that upon exceution checks if any new rows have been added to a table within the past 12 hours. If not, an warning email must be sent to a recipient.
I have the procedures for sending the email, but the problem is the query itself. I imagine I'd have to make an sql command that uses current date and compares that to the dates in the rows. But I'm a complete beginner in SQL so I can't even use the right words to find anything on google.
Short version:
Using MS SQL Server 2005, how can I check against the dates, then return a result based on whether new rows were created within the last 12 hours, and use that result to decide whether or not to send email?
Something like this should do what you wish.
Select ID
from TableName
where CreatedDate >= dateadd(hour,-12,getDate())
Hope this is clear but please feel free to pose further questions.
Cheers, John
Say your date field in the table is 'CreateDate' and it's of type DateTime.
Your time to compare with is: GETDATE()
(which returns date + time)
To get the datetime value of 12 hours before that, is done using DATEADD:
DATEADD(hour, -12, GETDATE())
so if we want the # of rows added in the last 12 hours, we'll do:
SELECT COUNT(*)
FROM Table
WHERE CreateDate >= DATEADD(hour, -12, GETDATE())
in your proc, you've to store the result of this query into a variable and check if it's > 0, so:
DECLARE #amount int
SELECT #amount=COUNT(*)
FROM Table
WHERE CreateDate >= DATEADD(hour, -12, GETDATE())
and then you'll check the #amount variable if it's > 0.
You could use a trigger, this link has several examples: http://msdn.microsoft.com/en-us/library/aa258254(SQL.80).aspx
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'reminder' AND type = 'TR')
DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE, DELETE
AS
EXEC master..xp_sendmail 'MaryM',
'Don''t forget to print a report for the distributors.'
GO
If you do not want something for each insert/update, you could copy data to a another table then examine that table every 12 hours, report on the rows in it, then delete them...
assuming you have on this table :
- either a unique id autoincrementing
- either a created_timestamp field containing the timestamp of creation of the row
-> have a new table
reported_rows
- report_timestamp
- last_id_seen
(OR)
- last_timestamp_seen
fill the reported row each time you send your email with the actual value
and before sending the email, check with the previous values, so you know what rows have been added
If the table has an identity field, you could also save the max value (as a bookmark) and next time check if there are any rows with an ID greater than your saved bookmark. May be faster if the key is the clustered key.