Im running stored procedure which in charges to insert, update and delete table's entries.
While both insert and delete runs smoothly, the update operation updates all columns except DATETIME2 one.
The scenario - I test my Repository pattern (using C# code) in the following way:
delete the entire [BackgroundTaskAttachtment] table
Create 4 new entries
delete single entry created on step 2
Wait for 5 seconds
modify one of the entries
the result is having 3 entries in [BackgroundTaskAttachtment] table, with all properties set as expected, except the [UpdatedOnUtc] which not updated (it is equal to [CreatedOnUtc]
I marked the updated row (as you can see [FilePath] was successfully updated):
Would appreciate community insights,
Thank you
This is the stored procedure code:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP_ArrangeBackgroundTaskAttachtments]
(
#backgroundTaskId BIGINT,
#taskAttchs [dbo].[BackgroundTaskAttachtmentType] READONLY
)
AS
BEGIN
SET NOCOUNT ON;
--delete all removed attachtments
DELETE FROM [BackgroundTaskAttachtment]
WHERE [BackgroundTaskId] = #backgroundTaskId AND [Id] NOT IN (SELECT [Id] FROM #taskAttchs)
----Update exist key-value pairs
UPDATE [dbo].[BackgroundTaskAttachtment]
SET
[IsPrimary] = attachs.[IsPrimary],
[FilePath] = attachs.[FilePath],
[Bytes] = attachs.[Bytes],
[UpdatedOnUtc] = GETUTCDATE()
FROM #taskAttchs AS attachs
WHERE attachs.[Id] = [BackgroundTaskAttachtment].[Id]
--insert new records
SELECT #backgroundTaskId AS [BackgroundTaskId], [FilePath], [IsPrimary], [Bytes], GETUTCDATE() AS [CreatedOnUtc], GETUTCDATE() AS [UpdatedOnUtc]
INTO #Temp FROM #taskAttchs as atcs
WHERE atcs.[Id] NOT IN (SELECT [Id] FROM [BackgroundTaskAttachtment] AS bta WHERE bta.[BackgroundTaskId] = #backgroundTaskId )
INSERT INTO [BackgroundTaskAttachtment]([BackgroundTaskId], [IsPrimary], [Bytes], [FilePath], [CreatedOnUtc], [UpdatedOnUtc] )
SELECT [BackgroundTaskId], [IsPrimary], [Bytes], [FilePath], [CreatedOnUtc], [UpdatedOnUtc]
FROM #Temp
END
This is the table type (sent from CLR to SQL)
CREATE TYPE [dbo].[BackgroundTaskAttachtmentType] AS TABLE(
[Id] [BIGINT] NOT NULL,
[FilePath] [NVARCHAR](MAX) NULL,
[IsPrimary] [BIT] NOT NULL,
[BackgroundTaskId] [BIGINT] NULL,
[Bytes] [VARBINARY](MAX) NULL
)
GO
this is the table definition
CREATE TABLE [dbo].[BackgroundTaskAttachtment]
(
[Id] BIGINT IDENTITY(1,1) NOT NULL,
[BackgroundTaskId] BIGINT NOT NULL,
[IsPrimary] BIT NOT NULL DEFAULT 0,
[FilePath] NVARCHAR(MAX) NULL,
[Bytes] VARBINARY(MAX) NULL,
[CreatedOnUtc] DATETIME2 NOT NULL,
[UpdatedOnUtc] DATETIME2 NOT NULL,
[RowVersion] ROWVERSION NOT NULL,
CONSTRAINT [PK_dbo.BackgroundTaskAttachtment] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.BackgroundTaskAttachtment_BackgroundTask_Id]
FOREIGN KEY ([BackgroundTaskId])
REFERENCES [dbo].[BackgroundTask] ([Id])
ON DELETE CASCADE
);
Please try using SYSUTCDATETIME which returns datetime2.
The GETUTCDATE which you are using, returns datetime.
Related
I have 2 tables House and AlarmInstall...
I need to create a trigger that will update a boolean Atribute "isInstaled" to false in my House table when i insert a uninstallDate (diferent then Null) in my Alarm Install Table.
I am a bit of a noob when it comes to trigger in SQL server. Any help will be appreciated!!!
These are the 2 tables
[dbo].[AlarmInstall](
[AlarmInstallId] [int] IDENTITY(1,1) NOT NULL,
[HouseId] [int] NOT NULL,
[InstallDate] [date] NOT NULL,
[uninstallDate] [date] NOT NULL,
[Model] [nchar](10) NOT NULL,
[dbo].[House](
[HouseId] [int] IDENTITY(1,1) NOT NULL,
[StreetId] [int] NOT NULL,
[DoorNr] [nchar](10) NOT NULL,
[CityId] [int] NOT NULL,
[IsInstalled] [bit] NULL,
Both tables are related thru HouseId that is PK in House Table and FK in AlarmInstall
tried with this trigger but all my Houses get flagged as true!!!
GO
CREATE TRIGGER STATECHANGE
ON dbo.AlarmInstall
AFTER INSERT, UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
update dbo.House SET IsInstalled=1
From dbo.AlarmInstall a
inner join inserted i on a.HouseId=i.HouseId
and i.InstallDate is not null
END
I think this could help: https://www.mssqltips.com/sqlservertip/4024/sql-server-trigger-after-update-for-a-specific-value/
You could also use a calculated field for isInstalled with the following definition:
CREATE TABLE House(
HouseId int NOT NULL,
[...]
uninstallDate date NULL,
isInstalled bit AS CASE WHEN uninstallDate IS NULL THEN 0 ELSE 1 END
But I don't see the value of creating it like this (or using a trigger), because when you don't have this field, you can always calculate it in a query:
SELECT
HouseId,
[...]
uninstallDate,
CASE WHEN uninstallDate IS NULL THEN 0 ELSE 1 END AS isInstalled
FROM
House
// Added to originat answer
CREATE TRIGGER dbo.StateChange
ON dbo.AlarmInstall
AFTER INSERT, UPDATE
AS
SET NOCOUNT ON
BEGIN
IF UPDATE(uninstallDate)
BEGIN
DECLARE #houseId int
DECLARE #uninstallDate date
SELECT #houseId = HouseId, #uninstallDate = uninstallDate FROM inserted
UPDATE dbo.House
SET IsInstalled = CASE WHEN #uninstallDate IS NULL THEN 0 ELSE 1 END
WHERE HouseId = #houseId
END
END
I have a table called Products this is how it looks like and I am trying to create a constraint on [IsDefaultProductKey] column, that any time a value is added to it, it needs be an active product key.
CREATE TABLE [dbo].[Products](
[ProductId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](64) NOT NULL,
[IsActive] [bit] NOT NULL,
[IsDefaultProductKey] [int] NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY CLUSTERED
(
[ProductId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [DF_products_IsActive] DEFAULT ((1)) FOR [IsActive]
GO
ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [FK_Products_Product_IsDefaultProductKey] FOREIGN KEY([IsDefaultProductKey])
REFERENCES [dbo].[Products] ([ProductId])
GO
ALTER TABLE [dbo].[Products] CHECK CONSTRAINT [FK_Products_Product_IsDefaultProductKey]
GO
If these are the entries in the table, row 4 should not be allowed to have a value of 1, since 1 is inactive. How can I go about adding a constraint on the table for that
ProductId Name IsActive IsDefaultProductKey
1 Test1 0 NULL
2 Test2 1 NULL
3 Test3 0 2
4 Test4 0 1 (Should not let me do this)
Based on suggestion, I created this UDF. But still not acting 100% the way I want it.. Please suggest.
CREATE TABLE [dbo].[Products]( [ProductId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](64) NOT NULL,
[IsActive] [bit] NOT NULL,
[IsDefaultProductKey] [int] NULL,
)
go
Create FUNCTION dbo.CheckProduct (#IsDefaultProductKey int)
RETURNS int
AS
BEGIN
DECLARE #retval int
SELECT #retval = 0
Select #retval = 1
FROM [Products]
WHERE ProductId = #IsDefaultProductKey and IsActive = 1
RETURN #retval
END;
GO
--Select CheckProduct(1)
ALTER TABLE [Products]
ADD CONSTRAINT chkActiveProduct
CHECK (IsDefaultProductKey = null or dbo.CheckProduct(IsDefaultProductKey) = 1);
go
You can use a CHECK CONSTRAINT that calls a UDF that queries the table to see if the ProductId referenced by IsDefaultProductKey is Active or not.
EDIT:
Since you need the constraint to check both ways, you would create a UDF that has parameters for ProductId, IsActive and IsDefaultProductKey.
Inside the function, if there is a non-NULL value for IsDefaultProductKey, then you need to query the table to see if the row with that ProductId is Active. If not, then the function needs to return false.
ALSO, if the IsActive parameter is passed a value of 0, then you need to check the table to make sure that no row has a IsDefaultProductKey equal to the value of the ProductId parameter. If there is such a row, then the function needs to return false.
But if neither of those cases occur, the function returns true, and in the CHECK CONSTRAINT, you then just test to see if the function returns true.
I did not understand your questions completely. However looks like you want to apply a check constraint based on the value of other column. The issue which I see in your SQL is you are applying a column level constraint, while I think you need to apply a table level constraint. Please see below sample based on my understanding of your question.
CREATE TABLE [dbo].[Products](
[ProductId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](64) NOT NULL,
[IsActive] [bit] NOT NULL,
[IsDefaultProductKey] [int] NULL,
CONSTRAINT ck_contraint CHECK
(
(IsActive = 1 AND (IsDefaultProductKey>0) )
)
)
I think you need a trigger not a constraint to do this. Something like:
CREATE OR ALTER TRIGGER DefaultNotActive ON [dbo].[Products]
AFTER INSERT, UPDATE
AS
IF EXISTS (SELECT *
FROM [dbo].[Products] p
JOIN inserted AS i
ON p.[ProductId] = i.[IsDefaultProductKey]
WHERE p.[IsActive] = 0
)
BEGIN
RAISERROR ('Default Product is inactive.', 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;
It might need to be more complex if you also need the default product key to exist. Currently this will allow inserts\updates where the default product key is an id that does not have an existing row in the table
Thanks to Tab Allemnan, Here is the solution I found. Works both ways.
Create FUNCTION CheckProduct (#IsDefaultProductKey int, #ProductId int, #IsActive bit)
RETURNS bit
AS
BEGIN
BEGIN
DECLARE #ret bit;
if (#IsDefaultProductKey is not NULL)
begin
SELECT #ret = 1
FROM [Products] p
WHERE p.ProductID = #IsDefaultProductKey
AND p.IsActive = 1;
end
else -- If #IsDefaultProductKey is null
Select #ret = 1
If (#IsActive = 0) -- If Product is made inactive, make sure that its not a defaultkey for any product.
Begin
SELECT #ret = 0
FROM [Products] p
WHERE p.IsDefaultProductKey = #ProductId
End
IF (#ret IS NULL)
SET #ret = 0;
RETURN #ret;
END;
END;
--Select dbo.CheckProduct (2,1,0)
GO
ALTER TABLE [Products]
ADD CONSTRAINT chkActiveProduct
CHECK (dbo.CheckProduct(IsDefaultProductKey,ProductId, IsActive)=1);
go
In my database I have about 10 tables connected in one central table (Mobile). This table (Mobile) has a column called price which is the sum of the prices of all other nested tables. I would like that when price of another table (like Battery, Camera, ...) is updated, the price of the central table (Mobile) would be updated too.
I will show the schema of central table and two more (for reducing code, other nested tables are so similar)
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[table_mobile]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NOT NULL,
[processor] [int] NOT NULL,
[memory_ram] [int] NOT NULL,
[memory_rom] [int] NOT NULL,
[operating_system] [int] NOT NULL,
[graphic] [int] NOT NULL,
[screen] [int] NOT NULL,
[battery] [int] NOT NULL,
[camera] [int] NOT NULL,
[material] [int] NOT NULL,
[extra] [int] NOT NULL,
[price] [decimal](18, 2) NOT NULL,
[created_by] [int] NOT NULL,
[created_at] [timestamp] NOT NULL,
CONSTRAINT [PK_mobiles]
PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[table_battery]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NOT NULL,
[capacity] [int] NOT NULL,
[description] [varchar](250) NOT NULL,
[image] [image] NOT NULL,
[price] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_table_battery]
PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[table_camera]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NOT NULL,
[megapixels] [int] NOT NULL,
[description] [varchar](250) NOT NULL,
[image] [image] NOT NULL,
[price] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_table_camera]
PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
As I say, I think that my purpose should be achieve with a trigger but any other suggest is accepted.
I'll show you what I want to do by programming in C#:
table_mobile.price = table_battery.price + table_camera.price + ... + table_XXX.price
Any idea how can I achive my trouble?
Thank you.
EDIT 1:
Using SSMS... I have created this template for a Trigger:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER <Schema_Name, sysname, Schema_Name>.<Trigger_Name, sysname, Trigger_Name>
ON <Schema_Name, sysname, Schema_Name>.<Table_Name, sysname, Table_Name>
AFTER <Data_Modification_Statements, , INSERT,DELETE,UPDATE>
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
END
GO
I have not worked with SQL Server for a while, so forgive me if there are any typos, but basically you will need to create a trigger for each of the tables linked to mobile and add the difference of the new and the old value to the price of the mobile:
create trigger PriceChange on table_battery
after update
as
BEGIN
update table_mobile
set price = table_mobile.price + i.price
from table_mobile
inner join INSERTED i
on table.mobile.id = i.id;
update table_mobile
set price = table_mobile.price - d.price
from table_mobile
inner join DELETED d
on table.mobile.id = d.id;
END
Note that we do separate updates, because the id might have changed. If the id stays the same, then you can use a single update with a difference. The code is untested, so if there are any problems, then please, let me know.
EDIT
You may also do this from application level where you trigger the updates. After any such update you can run an update for table_mobile, adding the values. The benefit would be that you can do the calculation only once if you know that several prices for the same mobiles will be changed.
EDIT2
Apparently this command should be used inside the trigger:
UPDATE [dbo].[table_mobile]
SET price = table_mobile.price + i.price - d.price
FROM [dbo].[table_mobile],
INSERTED i,
DELETED d
WHERE battery = d.id
This is an example of the view I mentioned:
create view MobileWithPriceAggregate as
select [id]
, [name]
, [processor]
, [memory_ram]
, [memory_rom]
, [operating_system]
, [graphic]
, [screen]
, [battery]
, [camera]
, [material]
, [extra]
, price = m.price+b.price+c.price
from [table_mobile] m
join [table_battery] b on b.id=m.battery
join [table_camera] c on c.id=m.camera
Note: if not all Mobiles have a camera, then you need to use left join and null handle like ISNULL(c.price,0)
I have written the following stored procedure to return data based on a lat/long and category id being passed.
I need to return a list of traders whose coverage area falls within the passed lat long (and that they cover the category being passed). So I am looking to draw a circle around the traders lat/long position, x number of meters using the radius they will operate from (this is stored in the Traders.OperatingRadius column). If the passed lat long coord is within this, then they should be included in the return list.
CREATE PROCEDURE FindTradersWithinRadiusLatLong
#LAT decimal(9,6),
#LONG decimal(9,6),
#CATEGORY int
AS
BEGIN
SET NOCOUNT ON;
DECLARE #GEO1 GEOGRAPHY;
SET #GEO1 = geography::Point(#LAT, #LONG, 4326)
SELECT
x.Id, x.Name,
x.Latitude, x.Longitude,
x.Distance, x.IsArchived
FROM
(SELECT
Traders.Id, Traders.Name,
Latitude, Longitude,
CategoryId = TraderCategories.Id,
OperatingRadius,
Traders.IsArchived,
Distance = (#geo1.STDistance(geography::Point(ISNULL(Latitude, 0), ISNULL(Longitude, 0), 4326)))
FROM
((Addresses
INNER JOIN
Traders ON Addresses.TraderId = Traders.Id)
INNER JOIN
TraderCategories ON Traders.Id = TraderCategories.TraderId)) AS x
WHERE
x.Distance <= x.OperatingRadius
AND x.CategoryId = #CATEGORY
AND (x.IsArchived = 0 OR x.IsArchived = NULL);
END
GO
TraderCategories is a linking table as follows;
Table TraderCategories
int FK TraderId
int FK CategoryId
Now I have added an address with;
latitiude - 43.590000, Longitude - -111.120000
There is also a TraderCategory Relationship for category with Id 1
I have then tried calling the stored procedure with the above and no matches are being returned.
The table definitions are as follows:
CREATE TABLE [Bemfeito].[Addresses]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Address1] [nvarchar](max) NULL,
[Address2] [nvarchar](max) NULL,
[Address3] [nvarchar](max) NULL,
[TraderId] [int] NULL,
[Latitude] [decimal](9, 6) NOT NULL,
[Longitude] [decimal](9, 6) NOT NULL,
[OperatingRadius] [real] NOT NULL DEFAULT (CONVERT([real],(0)))
CONSTRAINT [PK_Addresses]
PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO
ALTER TABLE [Bemfeito].[Addresses] WITH CHECK
ADD CONSTRAINT [FK_Addresses_Traders_TraderId]
FOREIGN KEY([TraderId]) REFERENCES [Bemfeito].[Traders] ([Id])
GO
ALTER TABLE [Bemfeito].[Addresses] CHECK CONSTRAINT [FK_Addresses_Traders_TraderId]
GO
CREATE TABLE [Bemfeito].[Traders]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Email] [nvarchar](max) NULL
[Name] [nvarchar](max) NULL
CONSTRAINT [PK_Traders]
PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO
CREATE TABLE [Bemfeito].[TraderCategories]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[CategoryId] [int] NULL,
[TraderId] [int] NULL,
CONSTRAINT [PK_TraderCategories]
PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO
ALTER TABLE [Bemfeito].[TraderCategories] WITH CHECK
ADD CONSTRAINT [FK_TraderCategories_Categories_CategoryId]
FOREIGN KEY([CategoryId]) REFERENCES [Bemfeito].[Categories] ([Id])
GO
ALTER TABLE [Bemfeito].[TraderCategories] CHECK CONSTRAINT [FK_TraderCategories_Categories_CategoryId]
GO
ALTER TABLE [Bemfeito].[TraderCategories] WITH CHECK
ADD CONSTRAINT [FK_TraderCategories_Traders_TraderId]
FOREIGN KEY([TraderId]) REFERENCES [Bemfeito].[Traders] ([Id])
GO
ALTER TABLE [Bemfeito].[TraderCategories] CHECK CONSTRAINT [FK_TraderCategories_Traders_TraderId]
GO
and finally for completion the category
CREATE TABLE [Bemfeito].[Categories]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[Value] [int] NOT NULL,
CONSTRAINT [PK_Categories]
PRIMARY KEY CLUSTERED ([Id] ASC)
)
Can anyone tell me where I am going wrong here please?
If you look at this microsoft reference here you should notice that the argument passed to STDistance is a geometry datatype while you are passing a Point datatype.
the line currently written like this
,Distance = (#geo1.STDistance(geography::Point(ISNULL(Latitude, 0), ISNULL(Longitude, 0), 4326))
should be written as follows.
,Distance = (#geo1.STDistance(geography::STGeomFromText('Point('+ISNULL(Longitude, 0)+' '+ISNULL(Latitude, 0)')',4326))
I have these tables :
CREATE TABLE [dbo].[FileISOManagers]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[LineId] [int] NOT NULL,
[Revision] [nvarchar](max) NULL,
[FileName] [nvarchar](max) NULL,
[UserId] [int] NOT NULL,
[SubmitDateTime] [datetime] NOT NULL
)
CREATE TABLE [dbo].[Lines]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[LineNumber] [nvarchar](max) NULL,
[DocumentNumber] [nvarchar](max) NULL,
[Revision] [nvarchar](max) NULL
)
Every line has multiple revisions, so I just need to insert the last revision column value in FileISOManagers table inside Revision column in lines table automatically. I want do this in SQL Server. Is there any solution to do this? Should I use a trigger?
revision
-- Create trigger on table FileISOManagers for Insert statement
CREATE TRIGGER trgAfterInsert on FileISOManagers
FOR INSERT
AS declare #Revision varchar(100);
select #Revision=i.Revision from inserted i;
set #action='Inserted Record -- After Insert Trigger.'; insert into Lines(Revision)
values (#Revision);
PRINT 'AFTER INSERT trigger fired.
It can be done using joins and alias.
Example
INSERT INTO FileISOManagers (Revision ..columns)
SELECT *
FROM Lines ls
WHERE NOT EXISTS
(
SELECT *
FROM FileISOManagers
WHERE LineId = ls.id
) order by ls.id desc