Здравствуйте, у меня есть следующий запрос с транзакцией в блоке try catch, и я откатываю транзакцию в блоке catch в случае сбоя. Проблема в том, что я создаю временную таблицу и не знаю, как с ней справиться с транзакцией. Итак, как я могу безопасно избавиться от временной таблицы в этой ситуации? Я пытался добавить его в блок catch, но не уверен, что это хорошая практика.
ALTER PROCEDURE GetDataForNewListaAbastecimento
@UAP NVARCHAR(20),
@ColaboradorId INT
AS
BEGIN
DECLARE @TransactionName nvarchar(20) = 'GetDataForNewListaAbastecimento'
DECLARE @Status INT
DECLARE @CurrentWeekDay INT
SET DATEFIRST 1
SET @CurrentWeekDay = DATEPART (WEEKDAY, GETDATE()) - 1
CREATE TABLE #tempTable
(
Id INT PRIMARY KEY,
Referencia NVARCHAR(15),
UAP NVARCHAR(20),
ConsumoWeek01 FLOAT,
ConsumoWeek02 FLOAT,
Stock INT,
QtdPecasPorCaixa INT
UNIQUE (Id)
)
BEGIN TRY
BEGIN TRAN @TransactionName
DECLARE @SQL NVARCHAR(MAX)
SELECT @SQL = 'INSERT INTO #tempTable
SELECT
Id,
Referencia,
UAP,
ConsumoWeek01 AS ConsumoWeek01,
ConsumoWeek02,
CASE
WHEN Stock IS NULL THEN 0
ELSE
Stock
END AS Stock,
QtdPecasPorCaixa
FROM OPENQUERY(MACPAC,
''WITH maxFornecedorByDate AS
(
SELECT
YDA3REP.A3ARCD,
YDA3REP.A3D5CD,
ROW_NUMBER() OVER ( PARTITION BY YDA3REP.A3D5CD ORDER BY YDA3REP.A3A3DT DESC) AS Number
FROM
AUTO.YSACHAPOR.YDA3REP YDA3REP
)
SELECT
ROW_NUMBER() OVER(ORDER BY A.RH6001 ASC) AS Id,
A.RH6001 as Referencia,
A.RH6002 as UAP,
A.RH6030 as ConsumoWeek01,
A.RH6031 as ConsumoWeek02,
IC130M.LLBLT1 as Stock,
M.AUQCON AS QtdPecasPorCaixa
FROM AUTO.D805DATPOR.TRP060H AS A
LEFT JOIN AUTO.D805DATPOR.IC130M IC130M
ON A.RH6001 = IC130M.LLPPN AND
IC130M.LLSTLC =
CASE A.RH6002
WHEN ''''UAP1'''' THEN ''''M1''''
WHEN ''''UAP2'''' THEN ''''M2''''
WHEN ''''UAP3'''' THEN ''''M3''''
WHEN ''''UAP4'''' THEN ''''M4''''
WHEN ''''UAP5'''' THEN ''''M5''''
WHEN ''''UAP6'''' THEN ''''M6''''
WHEN ''''UAPP'''' THEN ''''PROTOS''''
WHEN ''''EXT'''' THEN ''''EXTR''''
END
LEFT JOIN
(
SELECT
YDAUREP.AUD5CD,
YDAUREP.AUQCON
FROM maxFornecedorByDate F
join AUTO.YSACHAPOR.YDAUREP YDAUREP
ON F.A3ARCD = YDAUREP.AUARCD
AND F.A3D5CD = YDAUREP.AUD5CD
WHERE F.Number = 1 AND YDAUREP.AUD5CD LIKE ''''M%''''
AND YDAUREP.AUD5CD NOT LIKE ''''%P%''''
AND YDAUREP.AUA0NB > 1
AND YDAUREP.AUG6ST= ''''O''''
) M
ON M.AUD5CD = A.RH6001
WHERE A.RH6001 Not Like ''''FS%''''
AND A.RH6030 <> 0
AND A.RH6002 = ''''' + @UAP + ''''' '')'
EXEC sp_executesql @SQL
INSERT INTO hListasAbastecimento (UAP,DataCriacao,ColaboradorId) VALUES (@UAP,GETDATE(),@ColaboradorId)
INSERT INTO
hReferenciasAbastecimento
(
Referencia,
QtdAbastecimento,
QtdCaixas,
QtdPecasPorCaixa,
ListaAbastecimentoId
)
SELECT
C.Id,
C.Referencia,
( T.ConsumoWeek01 / ( ( P.NumDias - @CurrentWeekDay ) * P.NumPAB )) * P.AlcanceAbastecimento AS QtdAbastecimento,
T.QtdPecasPorCaixa,
CASE
WHEN ((T.ConsumoWeek01 / ( ( P.NumDias - @CurrentWeekDay ) * P.NumPAB )) * P.AlcanceAbastecimento) / NULLIF(T.QtdPecasPorCaixa,0) IS NULL THEN NULL
ELSE
CAST( CEILING(((T.ConsumoWeek01 / ( ( P.NumDias - @CurrentWeekDay ) * P.NumPAB )) * P.AlcanceAbastecimento) / T.QtdPecasPorCaixa ) AS INT)
END AS QtdCaixas,
SCOPE_IDENTITY()
FROM
#tempTable T
INNER JOIN hParametros P
ON P.Referencia = T.Referencia
AND P.UAP = @UAP
INNER JOIN hConsumos C
ON C.Referencia = P.Referencia
AND C.UAP = @UAP
WHERE T.Stock < ( T.ConsumoWeek01 / ( ( P.NumDias - @CurrentWeekDay ) * P.NumPAB )) * P.QtdMin
ORDER BY QtdAbastecimento DESC
COMMIT TRANSACTION @TransactionName
DROP TABLE #tempTable
-- Success, Lista de abastecimento foi criada sem errors --
SELECT @Status = 200
RETURN @Status
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRAN @TransactionName
DROP TABLE #tempTable
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_STATE() AS ErrorState,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
-- Erro, Nao foi possivel criar lista de abastecimento, verificar dynamic query --
SELECT @Status = 400
RETURN @Status
END CATCH
END
РЕДАКТИРОВАТЬ
Я вызываю SP из приложения Asp .NET Core с Entity Framework Core
await _context.Database
.ExecuteSqlCommandAsync("EXEC @Status = GetDataForNewListaAbastecimento @UAP, @ColaboradorId", @params);
Временная таблица будет сохраняться только на время соединения, которое ее создало. Предполагая, что вы открываете соединение для запуска SP (например, из приложения), SQL Server в любом случае будет корректно DROP
временную таблицу. Как вы выполняете СП?
О, я вижу, я вызываю это, используя структуру сущности в основном приложении asp .net. Кроме того, я должен выполнить sp_executeSql, потому что это единственный способ передать параметры из SQL на связанный с db 2 сервер, если только я что-то не упустил, но так делают мои коллеги.
Ааа, это OPENQUERY
(довольно трудно понять, когда буквальное жало); в этом случае, да, "инъекция" - единственный способ, однако я все же предлагаю сделать его безопасным. QUOTENAME
отлично подходит для этого. Я не могу проверить, но я думаю, что вы хотите AND A.RH6002 = ''''' + @UAP + ''''' ''
на AND A.RH6002 = N' + QUOTENAME(QUOTENAME(@UAP,N''''),N'''') + N'
(обратите внимание, я также добавил N
, так как @UAP
— это nvarchar
, и он вводился как varchar
)
Я пробовал это, но дает неправильный синтаксис рядом с ключевым словом @UAP, «AND A.RH6002 = N» + QUOTENAME(QUOTENAME(@UAP,N''''),N'''') + N' '')' EXEC sp_executesql @SQL´
Как я уже сказал, я не мог проверить, @Jackal, но я предлагаю использовать QUOTENAME
. Использование PRINT
или SELECT
для проверки значения @SQL поможет. Почему QUOTENAME важно
хорошо, то, что вы написали, дает мне печать AND A.RH6002 = N'''UAP1''' '), я попытался удалить 2 кавычки из каждой, и результат AND A.RH6002 = N[[UAP1]]] ')
Бларг, вот почему динамический динамический SQL - это боль XD. Попробуйте N'N''' + QUOTENAME(QUOTENAME(@UAP,N''''),N'''') + N'''';
Это должно охватывать основы сейчас.
вот то, что я пробовал, выглядит ближе к решению И A.RH6002 = N' + QUOTENAME(@UAP,N'''') + N' '')' это pritns AND A.RH6002 = N'UAP1' '), кажется, что единственная проблема сейчас - это начальная N ', я не уверен, что она делает, или она неправильно читает?
так что я читал, что это что-то о nvarchar, @UAP уже является NVARCHAR кстати
Это означает, что буквальная строка — это nvarchar
@Jackal. Вы не передаете @UAP
в OPENQUERY
, вы инъекция это значение. 'UAP1'
не является nvarchar
(N'UAP1'` является), поэтому, если вы вводите nvarchar
, вам нужно убедиться, что вы делаете строковый литерал, который вы вводите, объявлен как один. Обратите внимание, что здесь происходит, если вы опускаете N
, например: дб <> рабочий пример. Несмотря на то, что в значении @SQL
все символы отображаются правильно, при его выполнении они совершенно другие.
это сработало И A.RH6002 = ''' + QUOTENAME(@UAP,N'''') + ''''')', но не уверен, требуется ли иметь N'''' до и после каждого знака плюс ? Поскольку 1-й N всегда появляется в запросе как текст
Если ваш параметр (@UAP
) будет иметь символ Юникода, то вышеописанное не будет работать так, как вы ожидаете. И если у него никогда не будет символа Юникода, тогда вы должны объявить @UAP
как varchar
, а не как nvarchar
(во избежание путаницы).
Ах, я вижу, как это работает И A.RH6002 = N''' + QUOTENAME(@UAP,N'''') + N''' '')', да, это может быть юникод, так что это окончательный результат, Спасибо!
попробуйте добавить это:
ЕСЛИ OBJECT_ID('tempdb..#Results') НЕ NULL DROP TABLE #Results
(Замените имя своей временной таблицы #Result)
Почему вы используете
sp_executesql
для запуска этого нединамического запроса и почему вы вводите в него значение? Этот запрос не должен выполняться как «динамический» оператор, и он должен быть параметризован должен; в противном случае он открыт для инъекции (дажеnvarchar(20)
может подвергнуться инъекции).