I have an MS SQL database where sensor data is being streamed from a low level device, into say column 1.
In columns 2 and 3 I have some alarm levels.
What I would like to do is for "some script" to test the data as it arrives in column 1 against the levels in columns 2 and 3 and place a boolean decision in column 4.
My question is can I have some SQL script to do this? Where would it reside or does my low level device need knowledge of the column 2 and 3 trip points and when it posts the data to column 1 it also posts the decision to column 4?
UPDATE - First Test
I have implemented this and tested in sqlfiddle (but I am unable to login for some reason) so here is my schema:
CREATE TABLE SensorData(
data1 float NULL,
data1alarm bool NOT NULL DEFAULT FALSE
);
//
CREATE TABLE SensorConfig(
data1max float NULL,
data1min float NULL
);
//
CREATE TRIGGER Alarm
AFTER INSERT
ON SensorData FOR EACH ROW
BEGIN
IF (new.data1 > 5.0) AND (new.data1 < 60.0) THEN
INSERT INTO SensorData(data1alarm)
VALUES(true);
END IF;
END
//
INSERT INTO SensorConfig(data1max, data1min) VALUES (200.0, 100.0);
INSERT INTO SensorData(data1) VALUES (10.0);
When INSERT INTO SensorData(data1) VALUES (10.0); I get the error:
Can't update table 'sensordata' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
but when INSERT INTO SensorData(data1) VALUES (1.0); then the schema builds.
I cannot figure out the problem.
Also, I want really to implement such a condition that a true is written when data1 is greater data1max and less than data1min.
But at the moment I not sure why the schema does not build?
Furthermore, when I have tried getting the data1min and data1max into the conditional part of the trigger I have encountered errors:
IF (new.data1 > SensorConfig.data1min) AND (new.data1 < SensorConfig.data1max) THEN
Any help would be much appreciated.
I would recommend that you don't insert things into your table, but rather create a view that contains the logic for the alarm codes. So you might do
create view alarming_data as
select
data1.thevalue as theValue
true as theAlarm
from
data1 where data1.theValue > 5 and data1.theValue < 60
That keeps your raw data "pure" and your processing separate. In fact, you don't really need the second field, I'm just putting it there to keep my answer as close to your example as possible.
You can query this view as you would a table, and if your definition of alarming changes, it's just a matter of updating the definition of the view.
If you only want to display the offending lines, you don't need to persist column4, you can query them dynamically:
SELECT *
FROM mytable
WHERE column1 > column2 OR column1 > column3
You could create a trigger for that table with option AFTER INSERT
CREATE TRIGGER trDoMagic
ON YourTable
AFTER INSERT
your magic code
Related
I have no experience in writing database trigger but I need one in my current project.
My use case is the following. I have two tables - Table 1 and Table 2.
These tables have a 1 : m relation.
My usecase is, if all records in Table1 have "VALUE2" than value in Table2 should updated to VALUE2.
So if record-value with ID 3 of table1 is updated to VALUE2 than Value of table2 also should be updated to value2.
It would be great if someone could help me - Thanks a lo for than!
TABLE1:
ID FK_Table2 VALUE
-----------------------------
1 77 VALUE2
2 77 VALUE2
3 77 VALUE1
4 54 OTHERVALUE
TABLE2:
ID VALUE
---------------
77 VALUE1
So you need to learn and try basic trigger first.
CREATE OR REPLACE TRIGGER trigger_name
AFTER UPDATE ON TABLE1
FOR EACH ROW
BEGIN
/* trigger code goes here...*/
/* for this particular case you need to update value of table2 */
UPDATE TABLE2 SET VALUE = new.VALUE WHERE TABLE2.ID = new.FK_Table2 ;
END
Try and write some code. IF stucked... come back and let us know...
No matter which system, there are some basic rules or best practices you should know. One is that it is bad form (and outright prohibited in many systems) for a trigger to reach back out and query the very table the trigger is written for. Your use case requires the trigger on Table1 to go back out and read from Table1 during the Update operation. Not good.
One available option is to use a stored procedure to handle all the updates to this table. They are more awkward to work with (for example: if a parameter is NULL, does that mean put a NULL in the corresponding field or leave it unmodified?). For that reason, and with the understanding that this is based on the limited amount of information in the question, I would recommend one of two alternatives.
One is to have a stored procedure that is used only to change the VALUE field. That field is not changed in a vacuum, but as part of a larger process. The step in the process that actually ends up changing the field could then call the SP.
Another is to front the table with a view with an "instead of" trigger and perform all DML through the view. This is the method I prefer, at least on those systems that allow triggers on views. The view trigger may query the underlying table as needed.
As for the logic (SP or trigger) here is some pseudo code:
-- Make the update
update table1 set value = #somevalue
where id = #someid;
-- Get the group that id is in
select FK_Table2 into #somegroupid
from Table1
where id = #someid;
-- Are all the values in that group the same?
select count(*) into #OtherValues
from Table1
where FK_Table2 = #somegroupid
and value <> #somevalue;
-- If so, notify the other table.
if #OtherValues = 0 then
update table2 set value = #somevalue
where id = #somegroupid;
I hope this answers your immediate question. However, based on what you have shown us here, the major cause of the problem would seem to be poor design. Let us know the higher level requirement you are trying to fill here and I'll bet we could come up with some modeling changes that would make this a whole lot easier without having to get really clever with SPs or triggers.
I want to create a table which should contain max of 5 rows, when ever there is a 6th insert operation the last row must be deleted.
I want max of 5 rows and I want to do this in SQLIte database in android.
please suggest me a query which is simple.
You could do a row count in your application. If it returns more than four, or eaqual to five, rows you delete the last row before inserting a new one.
UPDATE:
I did a bit of testing on a table which i named test_table, and added this trigger:
DELIMITER $$
CREATE TRIGGER `schema_name`.`trigger_name` BEFORE INSERT ON `database_name`.`test_table`
FOR EACH ROW
if (select count(*) from test_table)> 4 /* count records to see if it exeeds your limit */
then
delete from test_table where id=(SELECT MAX(id) FROM test_table LIMIT 1); /* delete last row */
END if;
END
When I try to insert a sixth row I'm getting this error:
ERROR 1442: 1442: Can't update table 'test_table' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
I looked it up and found this answer: https://stackoverflow.com/a/21117071/1355562 - which lead me to http://dev.mysql.com/doc/refman/5.6/en/stored-program-restrictions.html.
The section "Restrictions for Stored Functions", point 5, says "... cannot modify a table that is already being used..."
Looks like you have to do this in you application bro ;)
ops.. just noticed you're working an MySQLlite.. Maybe it's different. This was for MySQL... Sorry about that...
I have written a Trigger which is transferring a record from a table members_new to members_old. The Function of trigger is to insert a record into members_old on after insert in members_new. So suppose a record is getting inserted into a members_new like
nMmbID nMmbName nMmbAdd
1 Abhi Bangalore
This record will get inserted into members_old with the same data structure of the table
My trigger is like :
create trigger add_new_record
after
insert on members_new
for each row
INSERT INTO `test`.`members_old`
(
`nMmbID`,
`nMmbName`,
`nMmbAdd`
)
(
SELECT
`members_new`.`nMmbID`,
`members_new`.`nMmbName`,
`members_new`.`nMmbAdd`
FROM `test`.`members_new`
where nMmbID = (select max(nMmbID) from `test`.`members_new` // written to read the last record from the members_new and stop duplication on the members_old , also this will reduce the chances of any error . )
)
This trigger is working for now , but my confusion is that what will happen if a multiple insertion is happening at one instance of time.
Will it reduce the performance?
Will I face deadlock condition ever in any case as my members_old have FKs?
If any better solution for this situation is there, please give limelight on that
From the manual:
You can refer to columns in the subject table (the table associated with the trigger) by using the aliases OLD and NEW. OLD.col_name refers to a column of an existing row before it is updated or deleted. NEW.col_name refers to the column of a new row to be inserted or an existing row after it is updated.
create trigger add_new_record
after
insert on members_new
for each row
INSERT INTO `test`.`members_old`
SET
`nMmbID` = NEW.nMmbID,
`nMmbName` = NEW.nMmbName,
`nMmbAdd` = NEW.nMmbAdd;
And you will have no problem with deadlocks or whatever. Also it should be much faster, because you don't have to read the max value before (which is also unsecure and might lead to compromised data). Read about isolation levels and transactions if you're interested why...
I have two tables
moduleprogress which contains fields:
studentid
modulecode
moduleyear
modules which contains fields:
modulecode
credits
I need a trigger to run when the user is attempting to insert or update data in the moduleprogress table.
The trigger needs to:
look at the studentid that the user has input and look at all modules that they have taken in moduleyear "1".
take the modulecode the user input and look at the modules table and find the sum of the credits field for all these modules (each module is worth 10 or 20 credits).
if the value is above 120 (yearly credit limit) then it needs to error; if not, input is ok.
Does this make sense? Is this possible?
#a_horse_with_no_name
This looks like it will work but I will only be using the database to input data manually so it needs to error on input. I'm trying to get a trigger similar to this to solve the problem(trigger doesn't work) and forget that "UOS_" is before everything. Just helps me with my database and other functions.
CREATE OR REPLACE TRIGGER "UOS_TESTINGS"
BEFORE UPDATE OR INSERT ON UOS_MODULE_PROGRESS
REFERENCING NEW AS NEW OLD AS OLD
DECLARE
MODULECREDITS INTEGER;
BEGIN
SELECT
m.UOS_CREDITS,
mp.UOS_MODULE_YEAR,
SUM(m.UOS_CREDITS)
INTO MODULECREDITS
FROM UOS_MODULE_PROGRESS mp JOIN UOS_MODULES m
ON m.UOS_MODULE_CODE = mp.UOS_MODULE_CODE
WHERE mp.UOS_MODULE_YEAR = 1;
IF MODULECREDITS >= 120 THEN
RAISE_APPLICATION_ERROR(-20000, 'Students are only allowed to take upto 120 credits per year');
END IF;
END;
I get the error message :
8 23 PL/SQL: ORA-00947: not enough values
4 1 PL/SQL: SQL Statement ignored
I'm not sure I understand your description, but the way I understand it, this can be solved using a materialized view, which might give better transactional behaviour than the trigger:
CREATE MATERIALIZED VIEW LOG
ON moduleprogress WITH ROWID (modulecode, studentid, moduleyear)
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LOG
ON modules with rowid (modulecode, credits)
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW mv_module_credits
REFRESH FAST ON COMMIT WITH ROWID
AS
SELECT pr.studentid,
SUM(m.credits) AS total_credits
FROM moduleprogress pr
JOIN modules m ON pr.modulecode = m.modulecode
WHERE pr.moduleyear = 1
GROUP BY pr.studentid;
ALTER TABLE mv_module_credits
ADD CONSTRAINT check_total_credits CHECK (total_credits <= 120)
But: depending on the size of the table this might however be slower than a pure trigger based solution.
The only drawback of this solution is, that the error will be thrown at commit time, not when the insert happens (because the MV is only refreshed on commit, and the check constraint is evaluated then)
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.