I want to create a kind of check, thats check if a products date is overdue. In my product table I have a column named veilingGesloten (auctionClosed) that can contain the value 'yes' or 'no'.
So I made this:
create TRIGGER [dbo].[trg_validategesloten]
ON [dbo].[voorwerp]
after update, insert
AS
begin
set nocount on;
update [dbo].[voorwerp]
set veilinGesloten =
case when ( inserted.looptijdeindeDag <= GETDATE() ) then 'wel'
else 'niet'
end
from [dbo].[voorwerp]
inner join inserted on [dbo].[voorwerp].voorwerpnummer = inserted.voorwerpnummer;
end
Now im wondering how to actually make this an auto process without any insert or update events
You don't trigger it - you use a computed column:
alter table voorwerp add veilinGesloten
as (case when looptijdeindeDag <= getdate() then 'wel' else 'niet' end);
Personally I would use a bit field and convert to meaningful words at the front-end.
alter table voorwerp add veilinGesloten
as (convert(bit, case when looptijdeindeDag <= getdate() then 1 else 0 end));
Related
I have a table in my oracle DB which has a column with random values. Screenshot is attached below :
I had manually updated the first row to "V0001". Is there any way I can update the rest of the rows to "V0002", "V0003" and so on without manual intervention.
You could use a sequence for this. Create a sequence, convert the sequence's .NEXTVAL to a string, use CONCAT() and UPDATE eg:
Table
create table demo
as
select dbms_random.string( 'x', 11 ) as vehicleid
from dual
connect by level <= 100 ;
select * from demo fetch first 10 rows only ;
-- output
VEHICLEID
LS23XFRNH5N
47DUDNOIRO9
POS5GQSQLMO
BBEEZJMQZI4
2Q8QE30HM2E
S7M5V40YNTD
N2X1YN0OIE3
...
Sequence
create sequence vehicleid_seq start with 1 increment by 1 ;
Update
update demo
set vehicleid = concat( 'V', to_char( vehicleid_seq.nextval, 'FM00000' ) ) ;
Result
select * from demo order by vehicleid fetch first 10 rows only ;
VEHICLEID
V00001
V00002
V00003
V00004
V00005
V00006
V00007
V00008
V00009
V00010
dbfiddle
The identifier code of a table is recommended to be a numeric data, what you could do is an extra field that works as a second code, perhaps called secondary_code. You can do it with Stored Procedure, I give you a small example:
DELIMITER$$
DROP PROCEDURE IF EXISTS sp_genwrar_code$$
CREATE PROCEDURE sp_genwrar_code(
OUT p_secondary_code VARCHAR(4)
)
BEGIN
DECLARE accountant INT;
BEGIN
SET accountant = (SELECT COUNT(*)+1 FROM product);
IF(accountant <10)THEN
SET p_secondary_code= CONCAT('V00',accountant );
ELSE IF(accountant<100) THEN
SET p_secondary_code= CONCAT('V0',accountant);
ELSE IF(accountant<1000)THEN
SET p_secondary_code= CONCAT('V',accountant );
END IF;
END IF;
END IF;
END;
END$$
With that you can generate codes as you need with the structure 'V001'
I am moving a small database from MS Access into SQL Server. Each year, the users would create a new Access database and have clean data, but this change will put data across the years into one pot. The users have relied on the autonumber value in Access as a reference for records. That is very inaccurate if, say, 238 records are removed.
So I am trying to accommodate them with an id column they can control (somewhat). They will not see the real primary key in the SQL table, but I want to give them an ID they can edit, but still be unique.
I've been working with this trigger, but it has taken much longer than I expected.
Everything SEEMS TO work fine, except I don't understand why I have the same data in my INSERTED table as the table the trigger is on. (See note in code.)
ALTER TRIGGER [dbo].[trg_tblAppData]
ON [dbo].[tblAppData]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #NewUserEnteredId int = 0;
DECLARE #RowIdForUpdate int = 0;
DECLARE #CurrentUserEnteredId int = 0;
DECLARE #LoopCount int = 0;
--*** Loop through all records to be updated because the values will be incremented.
WHILE (1 = 1)
BEGIN
SET #LoopCount = #LoopCount + 1;
IF (#LoopCount > (SELECT Count(*) FROM INSERTED))
BREAK;
SELECT TOP 1 #RowIdForUpdate = ID, #CurrentUserEnteredId = UserEnteredId FROM INSERTED WHERE ID > #RowIdForUpdate ORDER BY ID DESC;
IF (#RowIdForUpdate IS NULL)
BREAK;
-- WHY IS THERE A MATCH HERE? HAS THE RECORD ALREADY BEEN INSERTED?
IF EXISTS (SELECT UserEnteredId FROM tblAppData WHERE UserEnteredId = #CurrentUserEnteredId)
BEGIN
SET #NewUserEnteredId = (SELECT Max(t1.UserEnteredId) + 1 FROM tblAppData t1);
END
ELSE
SET #NewUserEnteredId = #CurrentUserEnteredId;
UPDATE tblAppData
SET UserEnteredId = #NewUserEnteredId
FROM tblAppData a
WHERE a.ID = #RowIdForUpdate
END
END
Here is what I want to accomplish:
When new record(s) are added, it should increment values from the Max existing
When a user overrides a value, it should check to see the existence of that value. If found restore the existing value, otherwise allow the change.
This trigger allows for multiple rows being added at a time.
It is great for this to be efficient for future use, but in reality, they will only add 1,000 records a year.
I wouldn't use a trigger to accomplish this.
Here is a script you can use to create a sequence (op didn't tag version), create the primary key, use the sequence as your special id, and put a constraint on the column.
create table dbo.test (
testid int identity(1,1) not null primary key clustered
, myid int null constraint UQ_ unique
, somevalue nvarchar(255) null
);
create sequence dbo.myid
as int
start with 1
increment by 1;
alter table dbo.test
add default next value for dbo.myid for myid;
insert into dbo.test (somevalue)
select 'this' union all
select 'that' union all
select 'and' union all
select 'this';
insert into dbo.test (myid, somevalue)
select 33, 'oops';
select *
from dbo.test
insert into dbo.test (somevalue)
select 'oh the fun';
select *
from dbo.test
--| This should error
insert into dbo.test (myid, somevalue)
select 3, 'This is NO fun';
Here is the result set:
testid myid somevalue
1 1 this
2 2 that
3 3 and
4 4 this
5 33 oops
6 5 oh the fun
And at the very end a test, which will error.
I have some trouble with entityFramework 4. Here is the thing :
We have a SQL server database. Every table have 3 instead of triggers for insert, update and delete.
We know EntityFramework has some issues to deal with theses triggers, that's why we added the following code at the end of triggers to force the rowCount :
for insert :
DECLARE #Identifier BIGINT;
SET #Identifier = scope_identity()
SELECT #Identifier AS Identifier
for update/delete :
CREATE TABLE #TempTable (temp INT PRIMARY KEY);
INSERT INTO #TempTable VALUES (1);
DROP TABLE #TempTable
It worked fine until now :
From an instead of insert trigger (let's say table A), I try to update a field of an other table (table B)
I know my update code perfectly work since a manual insert does the work. The issue shows up only when I'm using Entity framework.
I have the solution now, let's make a school case of this with a full example. :)
In this example, our application is an addressBook. We want to update the business Activity (IsActive column in Business)
everytime we add, update or delete a contact on this business. The business is considered as active if at least one of the contact
of the business is active. We record every state changements on the business in a table to have the full history.
So, we have 3 tables :
table Business (Identifier (PK Identity), Name, IsActive),
table Contact (Identifier (PK Identity), Name, IsActive, IdentifierBusiness)
table BusinessHistory (Identifier (PK Identity), IsActive, Date, IdentifierBusiness)
Here's are the triggers one we are interested in :
table Contact (trigger IoInsert):
-- inserting the new rows
INSERT INTO Contact
(
Name
,IsActive
,IdentifierBusiness
)
SELECT
t0.Name
,t0.IsActive
,t0.IdentifierBusiness
FROM
inserted AS t0
-- Updating the business
UPDATE
Business
SET
IsActive = CASE WHEN
(
(t0.IsActive = 1 AND Business.IsActive = 1)
OR
(t0.IsActive = 1 AND Business.IsActive = 0)
) THEN 1 ELSE 0
FROM
inserted AS t0
WHERE
Business.Identifier = t0.IdentifierBusiness
AND
t0.IsActive = 1
AND
Business.IsActive = 0
-- Forcing rowCount for EntityFramework
DECLARE #Identifier BIGINT;
SET #Identifier = scope_identity()
SELECT #Identifier AS Identifier
Table Business (trigger IoUpdate)
UPDATE
Business
SET
IsActive = 1
FROM
Contact AS t0
WHERE
Business.Identifier = t0.IdentifierBusiness
AND
t0.IsActive = 1
AND
Business.IsActive = 0
---- Updating BusinessHistory
INSERT INTO BusinessHistory
(
Date
,IsActive
,IdentifierBusiness
)
SELECT
DATE()
,t0.IsActive
,t0.Identifier
FROM
inserted AS t0
INNER JOIN
deleted AS t1 ON t0.Identifier = t1.Identifier
WHERE
(t0.Identifier <> t1.Identifier)
-- Forcing rowCount for EntityFramework
CREATE TABLE #TempTable (temp INT PRIMARY KEY);
INSERT INTO #TempTable VALUES (1);
DROP TABLE #TempTable
Table BusinessHistory :
-- Updating the business
UPDATE
Business
SET
IsActive = CASE WHEN
(
(t0.IsActive = 1 AND Business.IsActive = 1)
OR
(t0.IsActive = 1 AND Business.IsActive = 0)
) THEN 1 ELSE 0
FROM
inserted AS t0
WHERE
Business.Identifier = t0.IdentifierBusiness
AND
t0.IsActive = 1
AND
Business.IsActive = 0
-- inserting the new rows
INSERT INTO BusinessHistory
(
Date
,IsActive
,IdentifierBusiness
)
SELECT
DATE()
,t0.IsActive
,t0.Identifier
FROM
inserted AS t0
-- Forcing rowCount for EntityFramework
DECLARE #Identifier BIGINT;
SET #Identifier = scope_identity()
SELECT #Identifier AS Identifier
So, in a nutshell, what happened ?
We have 2 tables, Business and Contact. Contact is updating table Business on insert and update.
When Business is updated, it does an insert into BusinessHistory, which is storing the history of updates of table Business
,when the field IsActive is updated.
the thing is, even if I don't insert a new row in BusinessHistory, I launch an insert instruction and so, I go inside the instead of insert trigger of the table BusinessHistory. Of course, in the end of this one, there is a scope_identity(). You can use scope_identity only once, and it gives back the last identity inserted.
So, since I did not inserted any BusinessHistory, it was consuming the scope_identity of my newly inserted contact : the scope_identity of the instead of
insert of the contact table was empty !
How to isolate the issue ?
Using the profiler, you figure out that there are insert instruction in BusinessHistory when it should not be any of them.
Using the debugging, you will eventually end in the an insert trigger your are not supposed to be in.
How to fix it ?
Several alternatives here. What I did was to surround in table Business the insert of BusinessHistory by an If condition :
I want the insert to be inserted only if the statut "IsActive" has changed :
IF EXISTS
(
SELECT
1
FROM
inserted AS t0
INNER JOIN
deleted AS t1 ON t0.Identifier = t1.Identifier
WHERE
(t0.IsActive <> t1.IsActive)
)
BEGIN
INSERT INTO BusinessHistory
(
Date
,IsActive
,IdentifierBusiness
)
SELECT
DATE()
,t0.IsActive
,t0.Identifier
FROM
inserted AS t0
INNER JOIN
deleted AS t1 ON t0.Identifier = t1.Identifier
WHERE
(t0.IsActive <> t1.IsActive)
END
An other possibility is, in the trigger instead of insert of the table BusinessHistory, to surround the whole trigger by an IF EXISTS condition
IF EXISTS (SELECT 1 FROM inserted)
BEGIN
----Trigger's code here !
END
How to avoid it ?
Well, use one of these fixes !
Avoiding scope_identity(), ##IDENTITY is more than enough in most of the cases ! In my company, we only use scope_identity because of EF 4 !
I know my english is not perfect, I can edit if it's not good enough, or if someone want to add something on this subject !
I have a table where a person can log a number of hours on a day:
|__person__|__day__|__hours__|
| 1 | 1 | 4 |
|___2______|__1____|___2_____|
...
I want to create a trigger that doesn't allow the sum of hours to be greater than a specific value, for example 24, for a single person on a specific day.
Multiple rows can be inserted on multiple days simultaneously, and the trigger should then check that each day still has a valid number of hours for each person.
I have tried reading the documentation and similar questions here, but haven't been able to solve this, and have very little experience with SQL Server.
Any help would be appreciated!
You would need a user defined function for this and create a contratins using that user defined functions something like this...
User-Defined Function
CREATE FUNCTION dbo.get_TotalHoursRemaining (
#PersonID INT
, #Day INT
)
RETURNS INT
AS
BEGIN
Declare #Hours INT
SELECT #Hours = ISNULL(SUM([HOURS]), 0)
FROM Test_Table
WHERE Person = #PersonID
AND [DAY] = #Day
GROUP BY Person , [DAY]
SET #Hours = 24 - #Hours;
RETURN #Hours;
END
Constraint
ALTER TABLE Test_Table
ADD CONSTRAINT chk_hours_remaining
CHECK (((dbo.get_TotalHoursRemaining(Person , [Day])) >= 0))
You are looking for constraint,trigger is a overkill in this case.
This is based on assuming ,hours will be logged only once per day against each id
create table dbo.test
(
id int,
hrs int
);
ALTER TABLE dbo.test
ADD CONSTRAINT CHK_hrs CHECK (hrs <= 24);
You also can add constraint to restict least hours:
ALTER TABLE dbo.test
ADD CONSTRAINT CHK_hrs CHECK (hrs > 0 AND hrs <= 24);
insert into dbo.test
select 1,25
You also can have a trigger if each per logs hours mutiple times a day and you want the sum to be < 24
create table dbo.test
(
id int,
hrs int,
dayy datetime
);
create trigger trg_test
on dbo.test
after insert
as
begin
if exists(select id, sum(hrs)
from inserted i
join test t on i.id = t.id
group by id, dayy
having sum(hrs) > 24)
---sum of hours per day exceeded...some thig like that--you can even insert to log table '
insert into logtable
select id, sum(hrs)
from inserted i
join test t on i.id = t.id
group by id, dayy
having sum(hrs) > 24
end
rollback
end
If you create a check constraint on that column, you are able to prevent any value greater than 24 to be inserted. Basically what that does is: when you run an insert statement and the hour sum turns out bigger than 24, you violate the constraint and it kills the transaction immediately.
Check this link to get you started: http://www.w3schools.com/sql/sql_check.asp
Hope this helps :)
How do I state - If a field in my table is NULL only then do the update.
For example:
IF customer_day_phone (from my #invoice table) where id_key = #id_key -matches my parameter - is null. Then run my update.
But i'll have multiple rows coming back and I only want to update those who are NULL.
IF select customer_day_phone from #invoice where id_key = #id_key and customer_day_phone is null
BEGIN
...
END
I need the IF statement I can't simply use where day_phone is NULL because its a two part update. The first part updates the value if the field is null the second update formats the data (but I don't want it formatted if it wasn't updated only if it was).
I dont see any reason why you couldn't simply do TWO PART update in a single update statement.
Simply do the following. Update to the "Formatted" value in your first update and avoid running another update statement, just to update it first and then format it.
UPDATE #invoice
SET columnName = 'value'
WHERE customer_day_phone IS NULL --<-- this will only bring nulls
AND id_key = #id_key
Edit
From your update statement I think it should be as simple as .....
update a
set a.customer_day_phone = ISNULL(b.phone,'') + ' ' + ISNULL(customer_day_phone,'')
from #invoice a
join T_PHONE b on a.customer_no = b.customer_no
where b.[type] = 5
and a.customer_day_phone IS NULL
-- and id_key = #id_key --<-- you had this in your first query too
Let me guess maybe it something like this ?
Update #invoice set <fields which you want to update> WHERE id_key = #id_key and customer_day_phone is null
And at this point you dont need IF Statement
Assuming you want to exclude the record update entirely...
UPDATE invoice SET customer_Day_Phone = #InputValue
WHERE customer_day_Phone is null
and id_key = #Id_Key
or if you need to update other values on the record but not phone..
UPDATE invoice SET customer_Day_Phone = case
when customer_Day_Phone is null then #InputValue
else customer_Day_phone end,
field2=#field2value
WHERE id_key = #Id_Key