I’m a very new beginner with SQL and I’m battling a problem I can’t seem to find the answer to. I’ve found similar questions here on Stack Overflow but I still can’t see what I’m supposed to change in my code to make it work.
I’ve got 4 tables in a relation as follows:
CREATE TABLE DeviceLocation (
LocationId int not null identity(1,1) primary key,
Latitude nvarchar(50) not null,
Longitude nvarchar(50) not null
)
GO
CREATE TABLE TempHumidSensor (
TempHumidSensorId int not null identity(1,1) primary key,
Humidity float not null,
Temperature float not null
)
GO
CREATE TABLE LightSensor (
LightSensorId int not null identity(1,1) primary key,
LightPercentage int not null
)
GO
CREATE TABLE DeviceMessage (
MessageId nvarchar(50) not null primary key,
DeviceId nvarchar(20) not null,
temperatureAlert nvarchar(20) not null,
institutionClassName nvarchar(20) not null,
institutionName nvarchar(20) not null,
developerName nvarchar(20) not null,
MessageTimestamp nvarchar(20) not null,
TempSensorId int not null references TempHumidSensor(TempHumidSensorId),
LightSensorId int not null references LightSensor(LightSensorId),
DeviceLocationId int not null references DeviceLocation(LocationId)
)
GO
Then, I’ve got three procedures that are returning the identity value primary key for DeviceLocation, TempHumidSensor, LightSensor before inserting in to DeviceMessage, as follows:
GetDeviceLocation
ALTER procedure [dbo].[GetDeviceLocation]
@Latitude nvarchar(50),
@Longitude nvarchar(50)
as
begin
if not exists (select 1 from DeviceLocation where Latitude = @Latitude and Longitude = @Longitude)
insert into DeviceLocation output inserted.LocationId values (@Latitude, @Longitude)
else
select LocationId from DeviceLocation where Latitude = @Latitude and Longitude = @Longitude
end
GetLightSensor
ALTER procedure [dbo].[GetLightSensor]
@LightPercentage int
as
begin
if not exists (select 1 from LightSensor where LightPercentage = @LightPercentage)
insert into LightSensor output inserted.LightSensorId values (@LightPercentage)
else
select LightSensorId from LightSensor where LightPercentage = @LightPercentage
end
GetTempHumidSensor
ALTER procedure [dbo].[GetTempHumidSensor]
@Humidity float,
@Temperature float
as
begin
if not exists (select 1 from TempHumidSensor where Humidity = @Humidity and Temperature = @Temperature)
insert into TempHumidSensor output inserted.TempHumidSensorId values (@Humidity, @Temperature)
else
select TempHumidSensorId from TempHumidSensor where Humidity = @Humidity and Temperature = @Temperature
end
Finally, there’s the SaveSqlData procedure that is called upon by .NET code in the end.
ALTER PROCEDURE [dbo].[SaveSqlData]
@Latitude NVARCHAR(50),
@Longitude NVARCHAR(50),
@Humidity FLOAT,
@Temperature FLOAT,
@LightPercentage INT,
@MessageTimestamp NVARCHAR(20),
@MessageId NVARCHAR(50),
@DeviceId NVARCHAR(20),
@temperatureAlert NVARCHAR(20),
@institutionClassName NVARCHAR(20),
@institutionName NVARCHAR(20),
@developerName NVARCHAR(20)
AS
BEGIN
DECLARE @DeviceLocationId INT,
@TempHumidSensorId INT,
@LightSensorId INT
EXEC @TempHumidSensorId = GetTempHumidSensor @Humidity = @Humidity, @Temperature = @Temperature
EXEC @LightSensorId = GetLightSensor @LightPercentage = @LightPercentage
EXEC @DeviceLocationId = GetDeviceLocation @Latitude = @Latitude, @Longitude = @Longitude
INSERT INTO dbo.DeviceMessage(
MessageId,
DeviceId,
temperatureAlert,
institutionClassName,
institutionName,
developerName,
MessageTimestamp,
TempHumidSensorId,
DeviceLocationId,
LightSensorId
)
VALUES (
@MessageId,
@DeviceId,
@temperatureAlert,
@institutionClassName,
@institutionName,
@developerName,
@MessageTimestamp,
@TempHumidSensorId,
@DeviceLocationId,
@LightSensorId
)
END
I usually test this procedure with this snippet:
exec dbo.SaveSqlData
@Latitude = '55.5555',
@Longitude = '66.6666',
@Humidity = 41.2,
@Temperature = 25.0,
@LightPercentage = 91,
@MessageTimestamp = 123123123,
@temperatureAlert = 'false',
@MessageId = 'sd344d3j-guid-mock-124-123',
@DeviceId = 'device-name-from-device',
@institutionClassName = 'class',
@developerName = 'test',
@institutionName = 'institution'
The error that shows both when this method is invoked from C# and by the test call above says:
(1 row affected)
(1 row affected)
(1 row affected)
Msg 547, Level 16, State 0, Procedure dbo.SaveSqlData, Line 27 [Batch Start Line 0]
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__DeviceMes__TempH__11D4A34F". The conflict occurred in database "telemetry-sqldb", table "dbo.TempHumidSensor", column 'TempHumidSensorId'.
The statement has been terminated.
I’ve read that it has to do with the order of inserts, but I’m not sure how to fix it or where the problem lies exactly.
I can see that there are inserts to all tables except for DeviceMessage, when this error occures. Any help is vastly appreciated.
Advertisement
Answer
You can see the problem by putting a diagnostic SELECT @DeviceLocationId
etc in SaveSqlData
– you will get zeroes.
The way you’re getting the Ids back from your Get
sprocs doesn’t work. Each sproc SELECT
s the id, but the caller (the EXEC
in SaveSqlData
) expects a return value.
Change your Get
sprocs to RETURN
the id, eg:
ALTER procedure [dbo].[GetTempHumidSensor]
@Humidity float,
@Temperature float
as
begin
declare @id int
if not exists (select 1 from TempHumidSensor where Humidity = @Humidity and Temperature = @Temperature)
insert into TempHumidSensor values (@Humidity, @Temperature)
select @id = TempHumidSensorId from TempHumidSensor where Humidity = @Humidity and Temperature = @Temperature
return @id
end
Note you also have a typo in SaveSqlData
, the column in DeviceMessage
is named TempSensorId
not TempHumidSensorId
.