动态游标 SQL 服务器
Dynamic Cursor SQL Server
我有一个由这样的字符串创建的全局游标,但是在执行时,我收到此错误消息:
A cursor with the name 'crsDTO' does not exist.
代码:
DECLARE @Cursor NVARCHAR(MAX);
SET @Cursor = 'DECLARE crsDTO CURSOR FOR SELECT p.ID, p.Price, p.Count FROM Business.Products';
exec sp_executesql @Cursor;
OPEN crsDTO; -- fails here <<<<<<<<
BEGIN TRY
FETCH NEXT FROM crsDTO INTO @ID, @Price, @Count;
WHILE 0 = @@fetch_status
BEGIN
PRINT(@ID)
FETCH NEXT FROM crsDTO INTO @ID, @Price, @Count;
END;
CLOSE crsDTO;
DEALLOCATE crsDTO;
END TRY
BEGIN CATCH
CLOSE crsDTO;
DEALLOCATE crsDTO;
END CATCH
我环顾四周一切看起来都很好..但我找不到为什么它不起作用。
更新
此 SP 将批量更新价格或库存或两者。我可能是错的,可能有比这更好的替代方法,我愿意接受所有更正。
但是,此游标将根据用户意见进行过滤。它可以根据过滤器更改 stock/prices(百分比金额或基本金额)。
因此,例如,用户只想批量更改特定 brandId 或 BrandId/CategoryId 和 SupplierId 的组合或它们的 none(这意味着每个产品)的价格。
CREATE procedure [Business].[Product_BulkUpdate]
(
@PO_Error int OUTPUT,
@PO_ErrorMessage Nvarchar(Max) OUTPUT,
@PO_Step int OUTPUT,
@CallerUserId uniqueidentifier,
@CategoryId uniqueidentifier = null,
@BrandId uniqueidentifier = null,
@SupplierId uniqueidentifier = null,
@ProductName nvarchar(max) = null,
@Amount float = null,
@AmountPercentage float = null,
@IsInStock bit = null
)
as
DECLARE @ID Uniqueidentifier;
DECLARE @Price int;
DECLARE @Count int;
DECLARE @KW nvarchar(max);
DECLARE @Cursor nvarchar(max);
DECLARE @WhereClause nvarchar(max);
set @WhereClause = ' 1=1 ';
if (@ProductName is not null)
set @WhereClause =@WhereClause + ' And p.Name like N'''+'%'+cast(@ProductName as nvarchar(4000))+'%'+''' ';
if (@CategoryId is not null)
set @WhereClause =@WhereClause + ' And c.ID in (SELECT cf.id FROM Business.GetCategoryChilds('''+CAST(@CategoryId as nvarchar(50)) +''') cf) ';
if(@SupplierId is not null)
set @WhereClause = @WhereClause + ' AND p.SupplierId in (' + CAST(@SupplierId as nvarchar(50)) + ') ';
IF(@BrandId is not null)
set @WhereClause = @WhereClause + ' AND bb.ID in (' + CAST(@BrandId as nvarchar(50)) + ')';
SET @Cursor = ' DECLARE crsDTO cursor for
SELECT p.ID, p.Price, p.Count FROM Business.Products p
INNER JOIN Kernel.BaseEntity b on b.ID = p.ID AND b.IsDelete = 0
LEFT JOIN Business.Brand bb on bb.ID = p.BrandId
LEFT JOIN Business.Category c on c.ID = p.CategoryId
LEFT JOIN MarketPlace.Supplier s on s.SupplierId = p.SupplierId
WHERE '+@WhereClause+' AND c.CategoryTypeId = 10700';
begin
--- Auto generated procedure
SET NOCOUNT ON;
SET @PO_Error = 0;
SET @PO_Step = 0;
SET @PO_ErrorMessage = '';
BEGIN TRY
exec sp_executesql @Cursor;
SET @PO_Step = 1;
OPEN crsDTO;
BEGIN TRY
FETCH NEXT FROM crsDTO INTO @ID, @Price, @Count;
while 0 = @@fetch_status
BEGIN
IF(@IsInStock = 0) BEGIN
IF(@Amount is not null and @AmountPercentage is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Price = @Price + @Amount
WHERE ID = @ID
END
END else IF(@AmountPercentage is not null and @Amount is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Price = (@Price * (@AmountPercentage / 100))
WHERE ID = @ID
END
END
END ELSE IF(@IsInStock = 1) BEGIN
IF(@Amount is not null and @AmountPercentage is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Price = @Price + @Amount,
Count = 0
WHERE ID = @ID
END
END else IF(@AmountPercentage is not null and @Amount is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Price = (@Price * (@AmountPercentage / 100)),
Count = 0
WHERE ID = @ID
END
END ELSE IF(@Amount is null and @AmountPercentage is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Count = 0
WHERE ID = @ID
END
END
END
SET @PO_Step = 2;
FETCH NEXT FROM crsDTO INTO @ID, @Price, @Count;
END;
CLOSE crsDTO;
DEALLOCATE crsDTO;
END TRY
BEGIN CATCH
CLOSE crsDTO;
DEALLOCATE crsDTO;
SET @PO_Error = ERROR_NUMBER();
SET @PO_ErrorMessage = ERROR_MESSAGE();
END CATCH
END TRY
BEGIN CATCH
SET @PO_Error = ERROR_NUMBER();
SET @PO_ErrorMessage = ERROR_MESSAGE();
END CATCH
END;
我会添加检查光标是否存在:
-- ....
BEGIN CATCH
IF CURSOR_STATUS('global','crsDTO')>=-1
BEGIN
CLOSE crsDTO;
DEALLOCATE crsDTO;
END
END CATCH
使用全局 cursor/row-by-row 方法似乎不是最佳解决方案。
我有一个由这样的字符串创建的全局游标,但是在执行时,我收到此错误消息:
A cursor with the name 'crsDTO' does not exist.
代码:
DECLARE @Cursor NVARCHAR(MAX);
SET @Cursor = 'DECLARE crsDTO CURSOR FOR SELECT p.ID, p.Price, p.Count FROM Business.Products';
exec sp_executesql @Cursor;
OPEN crsDTO; -- fails here <<<<<<<<
BEGIN TRY
FETCH NEXT FROM crsDTO INTO @ID, @Price, @Count;
WHILE 0 = @@fetch_status
BEGIN
PRINT(@ID)
FETCH NEXT FROM crsDTO INTO @ID, @Price, @Count;
END;
CLOSE crsDTO;
DEALLOCATE crsDTO;
END TRY
BEGIN CATCH
CLOSE crsDTO;
DEALLOCATE crsDTO;
END CATCH
我环顾四周一切看起来都很好..但我找不到为什么它不起作用。
更新
此 SP 将批量更新价格或库存或两者。我可能是错的,可能有比这更好的替代方法,我愿意接受所有更正。 但是,此游标将根据用户意见进行过滤。它可以根据过滤器更改 stock/prices(百分比金额或基本金额)。 因此,例如,用户只想批量更改特定 brandId 或 BrandId/CategoryId 和 SupplierId 的组合或它们的 none(这意味着每个产品)的价格。
CREATE procedure [Business].[Product_BulkUpdate]
(
@PO_Error int OUTPUT,
@PO_ErrorMessage Nvarchar(Max) OUTPUT,
@PO_Step int OUTPUT,
@CallerUserId uniqueidentifier,
@CategoryId uniqueidentifier = null,
@BrandId uniqueidentifier = null,
@SupplierId uniqueidentifier = null,
@ProductName nvarchar(max) = null,
@Amount float = null,
@AmountPercentage float = null,
@IsInStock bit = null
)
as
DECLARE @ID Uniqueidentifier;
DECLARE @Price int;
DECLARE @Count int;
DECLARE @KW nvarchar(max);
DECLARE @Cursor nvarchar(max);
DECLARE @WhereClause nvarchar(max);
set @WhereClause = ' 1=1 ';
if (@ProductName is not null)
set @WhereClause =@WhereClause + ' And p.Name like N'''+'%'+cast(@ProductName as nvarchar(4000))+'%'+''' ';
if (@CategoryId is not null)
set @WhereClause =@WhereClause + ' And c.ID in (SELECT cf.id FROM Business.GetCategoryChilds('''+CAST(@CategoryId as nvarchar(50)) +''') cf) ';
if(@SupplierId is not null)
set @WhereClause = @WhereClause + ' AND p.SupplierId in (' + CAST(@SupplierId as nvarchar(50)) + ') ';
IF(@BrandId is not null)
set @WhereClause = @WhereClause + ' AND bb.ID in (' + CAST(@BrandId as nvarchar(50)) + ')';
SET @Cursor = ' DECLARE crsDTO cursor for
SELECT p.ID, p.Price, p.Count FROM Business.Products p
INNER JOIN Kernel.BaseEntity b on b.ID = p.ID AND b.IsDelete = 0
LEFT JOIN Business.Brand bb on bb.ID = p.BrandId
LEFT JOIN Business.Category c on c.ID = p.CategoryId
LEFT JOIN MarketPlace.Supplier s on s.SupplierId = p.SupplierId
WHERE '+@WhereClause+' AND c.CategoryTypeId = 10700';
begin
--- Auto generated procedure
SET NOCOUNT ON;
SET @PO_Error = 0;
SET @PO_Step = 0;
SET @PO_ErrorMessage = '';
BEGIN TRY
exec sp_executesql @Cursor;
SET @PO_Step = 1;
OPEN crsDTO;
BEGIN TRY
FETCH NEXT FROM crsDTO INTO @ID, @Price, @Count;
while 0 = @@fetch_status
BEGIN
IF(@IsInStock = 0) BEGIN
IF(@Amount is not null and @AmountPercentage is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Price = @Price + @Amount
WHERE ID = @ID
END
END else IF(@AmountPercentage is not null and @Amount is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Price = (@Price * (@AmountPercentage / 100))
WHERE ID = @ID
END
END
END ELSE IF(@IsInStock = 1) BEGIN
IF(@Amount is not null and @AmountPercentage is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Price = @Price + @Amount,
Count = 0
WHERE ID = @ID
END
END else IF(@AmountPercentage is not null and @Amount is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Price = (@Price * (@AmountPercentage / 100)),
Count = 0
WHERE ID = @ID
END
END ELSE IF(@Amount is null and @AmountPercentage is null) BEGIN
IF EXISTS (SELECT ID FROM Business.Products WHERE ID = @ID) BEGIN
UPDATE Business.Products SET
Count = 0
WHERE ID = @ID
END
END
END
SET @PO_Step = 2;
FETCH NEXT FROM crsDTO INTO @ID, @Price, @Count;
END;
CLOSE crsDTO;
DEALLOCATE crsDTO;
END TRY
BEGIN CATCH
CLOSE crsDTO;
DEALLOCATE crsDTO;
SET @PO_Error = ERROR_NUMBER();
SET @PO_ErrorMessage = ERROR_MESSAGE();
END CATCH
END TRY
BEGIN CATCH
SET @PO_Error = ERROR_NUMBER();
SET @PO_ErrorMessage = ERROR_MESSAGE();
END CATCH
END;
我会添加检查光标是否存在:
-- ....
BEGIN CATCH
IF CURSOR_STATUS('global','crsDTO')>=-1
BEGIN
CLOSE crsDTO;
DEALLOCATE crsDTO;
END
END CATCH
使用全局 cursor/row-by-row 方法似乎不是最佳解决方案。