У меня есть приложение, которое отправляет до 20 штрих-кодов данных устройства в таблицу SQL -Server «Использование устройства».
В триггере AFTER INSERT
этой таблицы у меня есть хранимая процедура, которая передает штрих-коды из столбцов BC1, BC2, BC3 и др. c. в другую таблицу BC usage
.
Таблица BC usage
установлена на IGNORE_DUP_KEY = ON
, так как может случиться, что БК сканируются дважды.
Если сканируется дубликат B C, то также вставка в первую таблицу «использование устройства» не удалась!
ПОЧЕМУ?
Я не понимаю, почему не удается выполнить первоначальную вставку в таблицу «Использование устройства», хотя IGNORE_DUP_KEY = ON
в таблице BC usage
, которая используется только в хранимой процедуре.
ALTER TRIGGER [dbo].[UpdatePersonanenDaten]
ON [dbo].[ScanIT_tblGeraeteerfassung]
AFTER INSERT
AS
BEGIN
DECLARE @Personalnummer varchar(10)
DECLARE @Mitarbeiter varchar(100)
DECLARE @InOut char(1)=''
DECLARE @ID int
DECLARE @StartID int
SELECT
@Personalnummer = a.[Personalnummer],
@Mitarbeiter = a.[DeviceUser]
FROM
[dbo].[ScanIT_tblDevices] a
INNER JOIN
inserted i ON a.[DeviceID] = i.[DeviceID]
SELECT @ID = (SELECT ID FROM inserted)
BEGIN
UPDATE [dbo].[ScanIT_tblGeraeteerfassung]
SET [Mitarbeiter] = @Mitarbeiter,
[Personalnummer] = @Personalnummer,
[InOut] = CASE
WHEN t.[AufAbbau] = 'Aufbau (Start)'
THEN 'S'
ELSE 'E'
END
FROM ScanIT_tblGeraeteerfassung t
INNER JOIN inserted i ON t.ID = i.ID
END
-- IN THIS SP THE INSERT INTO tbl "BC USAGE" HAPPENS and the Dup Key error let the trigger fail!!
EXEC [dbo].[ScanIT_spGeraeteEinsatz1] @ID
Спасибо за любую помощь
Майкл
Редактировать:
это хранимая процедура ScanIT_spGeraeteEinsatz1 Я пытался просто описать всю работу, прежде чем , но в действительности за B C, отправляемым на серверную таблицу, следует показание счетчика устройства. Таким образом, он должен искать последнее показание счетчика записи для этого B C перед этим. S обозначает начало, E обозначает конец времени использования устройства. Также может случиться, что это устройство раньше не использовалось на стороне клиентов, тогда мы берем счетчик с инвентаризации, чтобы установить начальный счетчик на Start.
И теперь, если кто-то сканирует это устройство B C дважды он должен быть заблокирован, потому что ПЕРВОЕ вхождение этого B C является обязательным началом, а следующее вхождение должно быть окончанием. Даже если кто-то по ошибке добавит B C в стартовую запись, хотя устройство уже было отсканировано ранее в другой записи.
Я надеюсь, что смогу объяснить работу, что вы понимаете весь процесс.
ALTER PROCEDURE [dbo].[ScanIT_spGeraeteEinsatz1]
@GeraeteerfassungID int
AS
BEGIN
BEGIN TRANSACTION; --neu
SAVE TRANSACTION MySavePoint;
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare @tablename as varchar(255) -- tbl, aus der ausglesen wird: 'ScanIT_tblGeraeteerfassung'
declare @temptable table (BarcodeName varchar(100)) -- im @temptable werden die Feldnamen Geraetebarcode1, Zaehlerstand1, ... eingelesen
declare @sqlDynamicString nvarchar(400)
declare @sqlDynamicString2 nvarchar(400)
declare @NumberBC int, @Counter int
declare @temptable2 table (Barcode varchar(10), Zaehlerstand varchar(20))
declare @strCounter varchar(2)
declare @Geraetebarcode int
declare @ID int
declare @L_ID int
declare @L_InOut char(1)
declare @StartID int
declare @GeraeteerfassungIDOUT int
--declare @Einsatzvon datetime
Declare @StartZaehlerstand varchar(25)
Declare @StartDatum datetime
declare @BC_AnzahlVerwendungen int=0
DECLARE @LastDS TABLE
([ID] [int],
[GeraeteerfassungIDOUT] [int] ,
[GeraeteerfassungIDIN] [int] ,
[Projektnummer] [varchar](20) ,
[Geraetebarcode] [varchar](100) ,
[ZaehlerstandOUT] [varchar](50) ,
[ZaehlerstandIN] [varchar](50) ,
[Einsatzvon] [datetime] ,
[Einsatzbis] [datetime] ,
[InOut] [char](1) ,
[PaarID] [int] )
set @tablename = 'ScanIT_tblGeraeteerfassung'
set @Counter=1
-- Einsatzvon-Datum/Zeit un d Projektnummer bleiben für alle Datensätze gleich
--set @Projektnummer=(Select Arbeitsauftrag from [dbo].[ScanIT_tblGeraeteerfassung] where [ID]=@GeraeteerfassungsID);
--set @Einsatzvon=(Select Sendtime from [dbo].[ScanIT_tblGeraeteerfassung] where [ID]=@GeraeteerfassungsID);
--set @L_InOut=(select [InOut] from [dbo].[ScanIT_tblGeraeteerfassung] where [ID]=@GeraeteerfassungsID))
--select @Projektnummer=Arbeitsauftrag, @Einsatzvon=Sendtime, @L_InOut=InOut from [dbo].[ScanIT_tblGeraeteerfassung] where [ID]=@GeraeteerfassungsID
--Insert into @temptable (BarcodeName)
--wie viele Gerätefelder gibt es in dem SCAN-IT Formular bzw. in der Einlesetabelle?
set @NumberBC=(SELECT count(c.name) FROM sys.columns c WHERE c.object_id = OBJECT_ID(@tablename) and c.name like 'Geraetebarcode%' ) ;
-- hier beginnt der Loop NEU
while(@Counter <= @NumberBC)
begin
set @strCounter=convert(varchar(2),@Counter)
set @sqlDynamicString2 = 'SET @GeraeteBC=(SELECT Geraetebarcode' + @strCounter + ' FROM [dbo].[ScanIT_tblGeraeteerfassung] WHERE ID=' + convert(nvarchar(8),@GeraeteerfassungsID) + ' AND Len(Geraetebarcode' + @strCounter + ')>0)'
EXECUTE sp_executesql @sqlDynamicString2, N'@GeraeteBC nvarchar(8) OUTPUT',@GeraeteBC=@Geraetebarcode OUTPUT
--zuerst muß einmal geschaut werden, ob dieses Gerät überhaupt schon mal im Einsatz war, daher wird die [dbo].[ScanIT_tblGeraeteeinsatz] ausgezählt
--bei ERSTMALIGER Verwendung ist die Anzahl 0
Set @BC_AnzahlVerwendungen=(Select Count(*) from [dbo].[ScanIT_tblGeraeteeinsatz] where [Geraetebarcode]=@Geraetebarcode)
print @Geraetebarcode
print @BC_AnzahlVerwendungen
-- es wird vorerst der Zählerstand, die Sendtime und die ID für in/out eingegeben, weil erst danach bewertet wird, ob das ein IN oder OUT Datensatz ist
set @sqlDynamicString=concat('SELECT ID, ID ,', @Projektnummer, ', Geraetebarcode' + @strCounter, ', Zaehlerstand' + @strCounter, ', Zaehlerstand' + @strCounter, ', Sendtime',', Sendtime',', InOut',
' FROM [dbo].[ScanIT_tblGeraeteerfassung] WHERE ID= ', @GeraeteerfassungsID, ' AND Len(Geraetebarcode' + @strCounter, ')>0' )
print @sqlDynamicString
BEGIN TRY --neu
Insert into [dbo].[ScanIT_tblGeraeteeinsatz] ([GeraeteerfassungIDOUT],
[GeraeteerfassungIDIN],
[Projektnummer],
[Geraetebarcode],
[ZaehlerstandOUT],
[ZaehlerstandIN],
[Einsatzvon],
[Einsatzbis],
[InOut])
exec (@sqlDynamicString)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION MySavePoint; -- rollback to MySavePoint
END
END CATCH
--die ID des eben eingegebenen Datensatzes
set @ID=SCOPE_IDENTITY()
-- nun wird geschaut, ob der Barcode schon einmal vokam
-- Gerät war noch NIE im Einsatz, d.h. den Startzählerwert aus der [dbo].[Lager_tblInventarArtikel] suchen
if @BC_AnzahlVerwendungen = 0
begin
Update a set --a.InOut= 'S',
a.[ZaehlerstandOUT]=coalesce(a.[ZaehlerstandOUT],b.[Anfangsstand]), --zuerste wird geschaut, ob ein Zählerstand angliefert wird, wenn nicht wird im Lagerstand geschaut
a.[ZaehlerstandIN]='',
a.[Einsatzbis]=NULL,
a.[GeraeteerfassungIDIN]= NULL,
a.[PaarID]=a.ID
from [dbo].[ScanIT_tblGeraeteeinsatz] a
left join [dbo].[Lager_tblInventarArtikel] b on a.[Geraetebarcode]=b.[Barcode]
where a.ID=@ID
end
--Gerät war schon im Einsatz, daher suchen aus [dbo].[ScanIT_tblGeraeteeinsatz]
if @BC_AnzahlVerwendungen > 0
begin
--print @inout
--ID des Startdatensatzes suchen
print @Projektnummer
print @Einsatzvon
print @L_InOut
--set @L_InOut=(select [InOut] from @LastDS)
--print @L_InOut
if @L_InOut='S'
--zuerst den zuvor gelieferten Datensatz suchen, um den Anfangstand zu setzen
begin
--Befüllen der Table Variablen mit dem vorletzten Datensatz
INSERT INTO @LastDS
([ID] ,
[GeraeteerfassungIDOUT] ,
[GeraeteerfassungIDIN] ,
[Projektnummer] ,
[Geraetebarcode] ,
[ZaehlerstandOUT] ,
[ZaehlerstandIN] ,
[Einsatzvon] ,
[Einsatzbis] ,
[InOut] ,
[PaarID] )
SELECT Top 1 [ID] ,
[GeraeteerfassungIDOUT] ,
[GeraeteerfassungIDIN] ,
[Projektnummer] ,
[Geraetebarcode] ,
[ZaehlerstandOUT] ,
[ZaehlerstandIN] ,
[Einsatzvon] ,
[Einsatzbis] ,
[InOut] ,
[PaarID]
from [dbo].[ScanIT_tblGeraeteeinsatz]
where [Geraetebarcode]=@Geraetebarcode and ID < @ID order by ID desc
end
begin
Update a set
a.[GeraeteerfassungIDIN]=NULL --weil GeraeteerfassungIDOUT existiert, braucht man sie nicht setzen
,a.[ZaehlerstandOut]= coalesce(a.[ZaehlerstandOut],x.[ZaehlerstandOUT]) --wenn ZaehlerstandOut abgelesen wurde, braucht man ihn nicht setzen, wenn nicht, dann aus letztem DS verwenden
,a.[ZaehlerstandIN]=''
-- ,a.[Einsatzvon]=x.[Einsatzvon] --weil Einsatzvon existiert, braucht man sie nicht setzen
,a.[Einsatzbis]= null
,a.[PaarID]=a.ID
from [dbo].[ScanIT_tblGeraeteeinsatz] a
left join @LastDS x on a.[Geraetebarcode]=x.[Geraetebarcode]
where a.[ID]=@ID
end
if @L_InOut='E'
--hier muß man nach dem letzten DS suchen, weil es einen S geben muß!!
--da diese Nummer schon verwendet wurde, kann man nach dem S Datensatz zu diesem Gerät suchen
begin
--Befüllen der Table Variablen mit dem vorletzten Datensatz
INSERT INTO @LastDS
([ID] ,
[GeraeteerfassungIDOUT] ,
[GeraeteerfassungIDIN] ,
[Projektnummer] ,
[Geraetebarcode] ,
[ZaehlerstandOUT] ,
[ZaehlerstandIN] ,
[Einsatzvon] ,
[Einsatzbis] ,
[InOut] ,
[PaarID] )
SELECT Top 1 [ID] ,
[GeraeteerfassungIDOUT] ,
[GeraeteerfassungIDIN] ,
[Projektnummer] ,
[Geraetebarcode] ,
[ZaehlerstandOUT] ,
[ZaehlerstandIN] ,
[Einsatzvon] ,
[Einsatzbis] ,
[InOut] ,
[PaarID]
from [dbo].[ScanIT_tblGeraeteeinsatz]
where [Geraetebarcode]=@Geraetebarcode and ID < @ID order by ID desc
end
select * from @LastDS
begin
Update a set
a.[GeraeteerfassungIDIN]=x.[GeraeteerfassungIDOUT]
,a.[ZaehlerstandOut]= x.[ZaehlerstandOut]
--,a.[ZaehlerstandIN]= NULL --steht schon drinnen
,a.[Einsatzvon]=x.[Einsatzvon]
--,a.[Einsatzbis]= NULL --steht schon drinnen
,a.[PaarID]=x.ID
,a.[Verbrauch]= coalesce(cast(a.[ZaehlerstandIN] as float),0)-coalesce(cast(x.[ZaehlerstandOUT] as float),0)
,a.[Standzeit]= datediff(d, coalesce(x.[Einsatzvon],0), @Einsatzvon)+1
from [dbo].[ScanIT_tblGeraeteeinsatz] a
left join @LastDS x on a.[Geraetebarcode]=x.[Geraetebarcode]
where a.[ID]=@ID
end
end
-- zuletzt @Counter hochzählen, dann loop
set @Geraetebarcode=null
set @BC_AnzahlVerwendungen=null
delete from @LastDS --tablevariable leeren
set @Counter=@Counter+1
end
-- Select @StartZaehlerstand, @StartDatum, @StartID,@InOut, @ID, @Counter
END