【问题标题】:How to return filtered rows using SQL Cursors如何使用 SQL 游标返回过滤的行
【发布时间】:2022-01-05 22:07:58
【问题描述】:

我一直在查看存储过程中的游标示例,所有这些似乎都在打印到控制台或在主循环中操作数据。

我的情况如下:

  • 创建一个从单个表返回过滤行的存储过程。
  • 为没有WHERE 子句的简单SELECT 语句创建一个游标。
  • 遍历提取周期。
  • 仅返回满足特定条件的行。

伪代码(C#、EntityFrameworkCore):

var name = "xyz";
var email = "";
var users = new List<User>();

foreach (var user in context.Users.AsQueryable())
{
    var added = false;

    if (!added)
    {
        if ((!string.IsNullOrWhitespace(name)) && (user.Name.Contains(name)))
        {
            users.Add(user);
        }
    }

    if (!added)
    {
        if ((!string.IsNullOrWhitespace(email)) && (user.Name.Contains(email)))
        {
            users.Add(user);
        }
    }
}

这些条件可能非常复杂,无法放入WHERE 子句中。因此查看游标。

只是不知道该怎么想。游标是一个迭代器,但如何累积过滤后的行以返回?

任何建议将不胜感激。

这是我想在其中使用游标的存储过程:

CREATE OR ALTER PROCEDURE SpUserSearch
    @Condition BIT = 0, -- AND=0, OR=1.
    @Name NVARCHAR(100) = NULL,
    @Email NVARCHAR(100) = NULL
AS
BEGIN

    SET NOCOUNT ON;

    DECLARE @UseName BIT
    DECLARE @UseEmail BIT

    IF ((@Name IS NULL) OR (LEN(@Name) = 0)) SET @UseName = 0 ELSE SET @UseName = 1
    IF ((@Email IS NULL) OR (LEN(@Email) = 0)) SET @UseEmail = 0 ELSE SET @UseEmail = 1

    IF (@Condition = 0)
        SELECT [Id], [Name], [Email]
        FROM [User]
        WHERE
            ((@UseName = 1) OR ([Name] LIKE '%' + @Name + '%'))
            AND
            ((@UseEmail = 1) OR ([Email] LIKE '%' + @Email + '%'))
    ELSE
        SELECT [Id], [Name], [Email]
        FROM [User]
        WHERE
            ((@UseName = 1) OR ([Name] LIKE '%' + @Name + '%'))
            OR
            ((@UseEmail = 1) OR ([Email] LIKE '%' + @Email + '%'))

    RETURN (@@ROWCOUNT)

END

【问题讨论】:

  • 太复杂的WHERE子句是什么?你似乎只有相当直截了当的那些。至少只要您使用标准的kitchen-sink query 技术
  • @Charlieface,该示例旨在使其易于在 SO 上阅读。您包含的链接确实有一个在运行时动态构造和执行查询的示例,因此确实为我的另一个问题here 提供了替代方案。谢谢。
  • 好的,你能不能不假思索地给我们一个实际的问题。您觉得必须使用游标的WHERE子句是什么?
  • 语句“这里是我想在其中使用游标的存储过程”是 100% 错误的。你一开始就不想使用游标!在适当的时候使用光标(这种情况很少见)!

标签: c# sql stored-procedures database-cursor


【解决方案1】:

这种类型的查询称为Kitchen Sink query

由于参数嗅探,这可能会出现问题。所以最好的方法不是使用游标,而是动态构建条件,并使用sp_executesql 执行。

注意实际值是如何不串联的,而是作为参数传递给动态端的。

CREATE OR ALTER PROCEDURE SpUserSearch
    @Condition BIT = 0, -- AND=0, OR=1.
    @Name NVARCHAR(100) = NULL,
    @Email NVARCHAR(100) = NULL
AS
BEGIN

    SET NOCOUNT ON;

    DECLARE @sql nvarchar(max) = N'
SELECT [Id], [Name], [Email]
FROM [User]
WHERE 1=1 AND (
    ';

    DECLARE @conditions nvarchar(max);
    IF NULLIF(@Name, '') IS NOT NULL
        SET @conditions = '([Name] LIKE ''%'' + @Name + ''%'')' + '
  ';

    IF NULLIF(@Email, '') IS NOT NULL
        SET @conditions = CONCAT(
                @conditions + CASE WHEN @Condition = 0 THEN 'AND ' ELSE 'OR ' END + '
    ',
                '([Email] LIKE ''%'' + @Email + ''%'')
');

    SET @sql += @conditions + '
);

    RETURN (@@ROWCOUNT);
';
    PRINT @sql; --for testing

    DECLARE @rc int;
    EXEC @rc = sp_executesql
        @sql,
        N'@Name NVARCHAR(100), @Email NVARCHAR(100)',
        @Name = @Name,
        @Email = @Email;

    RETURN @rc;

END

【讨论】:

    【解决方案2】:

    我同意其他人的观点,即光标听起来非常不适合您的用例。但是要回答您的问题,您将首先创建一个临时表,然后游标可以在查看数据时将任何匹配的行插入到临时表中。最后,从临时表中选择所有行。

    【讨论】:

    • 谢谢。这就是为什么我很难理解它是如何工作的。我同意,如果临时表是使用游标的唯一方法,那么这对我的场景来说是个坏主意。也许他们可以处理一些非常重复的查询。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-14
    相关资源
    最近更新 更多