gpt4 book ai didi

sql-server - 键集分页 - 按跨多列的搜索词过滤

转载 作者:行者123 更新时间:2023-12-04 13:34:03 27 4
gpt4 key购买 nike

我正试图远离 OFFSET/FETCH分页到 Keyset分页(也称为搜索方法)。因为我刚刚开始,所以我有很多问题,但这是我尝试与过滤器一起正确分页的众多问题之一。
所以我有2张 table

  • aspnet_users

  • 有列
    PK
    UserId uniquidentifier
    字段
    UserName NVARCHAR(256) NOT NULL, 
    AffiliateTag varchar(50) NULL
    .....other fields
  • aspnet_membership

  • 有列
    PK+FK
    UserId uniquidentifier
    字段
    Email NVARCHAR(256) NOT NULL
    .....other fields
    索引
  • Non Clustered表索引 aspnet_users (用户名)
  • Non Clustered表索引 aspnet_users (AffiliateTag)
  • Non Clustered表索引 aspnet_membership (邮箱)

  • 我有一个页面将列出用户(基于 search term),页面大小设置为 20。我想跨多个列进行搜索,而不是执行 OR我发现每个都有一个单独的查询,然后 Union他们将使索引正确使用。
    所以有将采取 search term 的存储过程和可选 UserNameUserId下一页的最后一条记录。
    Create proc [dbo].[sp_searchuser]
    @take int,
    @searchTerm nvarchar(max) NULL,
    @lastUserName nvarchar(256)=NULL,
    @lastUserId nvarchar(256)=NULL
    AS

    IF(@lastUserName IS NOT NULL AND @lastUserId IS NOT NULL)
    Begin
    select top (@take) *
    from
    (
    select u.UserId, u.UserName, u.AffiliateTag, m.Email
    from aspnet_Users as u
    inner join aspnet_Membership as m
    on u.UserId=m.UserId
    where u.UserName like @searchTerm

    UNION

    select u.UserId, u.UserName, u.AffiliateTag, m.Email
    from aspnet_Users as u
    inner join aspnet_Membership as m
    on u.UserId=m.UserId
    where u.AffiliateTag like convert(varchar(50), @searchTerm)
    ) as u1
    where u1.UserName > @lastUserName
    OR (u1.UserName=@lastUserName And u1.UserId > convert(uniqueidentifier, @lastUserId))
    order by u1.UserName
    End

    Else
    Begin

    select top (@take) *
    from
    (
    select u.UserId, u.UserName, u.AffiliateTag, m.Email
    from aspnet_Users as u
    inner join aspnet_Membership as m
    on u.UserId=m.UserId
    where u.UserName like @searchTerm

    UNION

    select u.UserId, u.UserName, u.AffiliateTag, m.Email
    from aspnet_Users as u
    inner join aspnet_Membership as m
    on u.UserId=m.UserId
    where u.AffiliateTag like convert(varchar(50), @searchTerm)
    ) as u1

    order by u1.UserName
    End
    现在使用搜索词 mua 获取第一页的结果
    exec [sp_searchuser] 20, 'mua%'
    它使用为 UserName 列创建的索引和为 AffiliateTag 列创建的另一个索引,这很好
    但问题是我发现内部联合查询返回所有匹配的行
    就像在这种情况下,执行计划显示
    UserName Like SubQuery
    Number of Rows Read= 5
    Actual Number of Rows= 4
    AffiliateTag Like SubQuery
    Number of Rows Read= 465
    Actual Number of Rows= 465
    所以总共内部查询返回 469匹配行
    然后外部查询取出 20 用于最终结果重置。所以真的要读取比需要更多的数据。
    当进入下一页
    exec [sp_searchuser] 20, 'mua%', 'lastUserName', 'lastUserId'
    执行计划显示
    UserName Like SubQuery
    Number of Rows Read= 5
    Actual Number of Rows= 4
    AffiliateTag Like SubQuery
    Number of Rows Read= 465
    Actual Number of Rows= 445
    总共内部查询返回 449匹配行
    所以无论有没有分页,它都会读取比需要更多的数据。
    我的期望是以某种方式限制内部查询,因此它不会返回所有匹配的行。

    最佳答案

    您可能对 Logical Processing Order 感兴趣,这决定了在一个步骤中定义的对象何时可用于后续步骤中的子句。 Logical Processing Order步骤是:

  • 发件人
  • 开启
  • 加入
  • 哪里
  • GROUP BY
  • WITH CUBE 或 WITH ROLLUP
  • 选择
  • 区别
  • 订购者
  • 首页

  • 当然,正如文档所述:

    The actual physical execution of the statement is determined by thequery processor and the order may vary from this list.


    这意味着有时某些语句可以在上一个完成之前开始。
    在您的情况下,您的查询如下所示:
  • 部分数据提取
  • user_name 排序
  • 获得TOP记录

  • 没有办法减少数据提取部分中的行以获得确定性结果(我们实际上可能需要按 user_name, user_id 排序以获得这样的结果)我们需要获取所有匹配的行,对它们进行排序,然后获取所需的行.
    例如,想象第一个查询返回 20 个以“Z”开头的名字。第二个查询只返回一个以“A”开头的名字。如果您以某种方式停止执行并跳过第二个查询,您将得到错误的结果 - 20 个名称以“Z”开头,而不是一个以“A”开头和 19 个以“Z”开头。
    在这种情况下,我更喜欢使用动态 T-SQL 语句以获得更好的执行时间并减少代码长度。你是说:

    And I want to search across multiple columns so instead of doing OR Ifind out having a separate query for each and then Union them willmake the index use correctly.


    当您使用时 UNION您正在对表执行双重读取。在您的情况下,您正在阅读 aspnet_Membership表两次和 aspnet_Users两次(是的,这里您使用了两个不同的索引,但我相信它们不是 covering 并且您最终执行查找以提取用户 nameemail
    我猜你已经开始覆盖索引,如下例所示:
    DROP TABLE IF EXISTS [dbo].[StackOverflow];

    CREATE TABLE [dbo].[StackOverflow]
    (
    [UserID] INT PRIMARY KEY
    ,[UserName] NVARCHAR(128)
    ,[AffiliateTag] NVARCHAR(128)
    ,[UserEmail] NVARCHAR(128)
    ,[a] INT
    ,[b] INT
    ,[c] INT
    ,[z] INT
    );

    CREATE INDEX IX_StackOverflow_UserID_UserName_AffiliateTag_I_UserEmail ON [dbo].[StackOverflow]
    (
    [UserID]
    ,[UserName]
    ,[AffiliateTag]
    )
    INCLUDE ([UserEmail]);

    GO

    INSERT INTO [dbo].[StackOverflow] ([UserID], [UserName], [AffiliateTag], [UserEmail])
    SELECT TOP (1000000) ROW_NUMBER() OVER(ORDER BY t1.number)
    ,CONCAT('UserName',ROW_NUMBER() OVER(ORDER BY t1.number))
    ,CONCAT('AffiliateTag', ROW_NUMBER() OVER(ORDER BY t1.number))
    ,CONCAT('UserEmail', ROW_NUMBER() OVER(ORDER BY t1.number))
    FROM master..spt_values t1
    CROSS JOIN master..spt_values t2;


    GO
    因此,对于以下查询:
    SELECT TOP 20 [UserID]
    ,[UserName]
    ,[AffiliateTag]
    ,[UserEmail]
    FROM [dbo].[StackOverflow]
    WHERE [UserName] LIKE 'UserName200%'
    OR [AffiliateTag] LIKE 'UserName200%'
    ORDER BY [UserName];


    GO
    这里的问题是我们正在读取所有行,即使我们正在使用索引。
    enter image description here
    好处是索引正在覆盖并且我们没有执行查找。根据搜索条件,它的性能可能比您的方法更好。
    如果性能不好,我们可以使用触发器到 UNPIVOT原始数据和记录在单独的表中。它可能看起来像这样(最好使用 attribute_id 而不是像我这样的文本):
    DROP TABLE IF EXISTS [dbo].[StackOverflowAttributes];

    CREATE TABLE [dbo].[StackOverflowAttributes]
    (
    [UserID] INT
    ,[AttributeName] NVARCHAR(128)
    ,[AttributeValue] NVARCHAR(128)
    ,PRIMARY KEY([UserID], [AttributeName], [AttributeValue])
    );

    GO

    CREATE INDEX IX_StackOverflowAttributes_AttributeValue ON [dbo].[StackOverflowAttributes]
    (
    [AttributeValue]
    )

    INSERT INTO [dbo].[StackOverflowAttributes] ([UserID], [AttributeName], [AttributeValue])
    SELECT [UserID]
    ,'Name'
    ,[UserName]
    FROM [dbo].[StackOverflow]
    UNION
    SELECT [UserID]
    ,'AffiliateTag'
    ,[AffiliateTag]
    FROM [dbo].[StackOverflow];
    之前的查询将如下所示:
    SELECT TOP 20 U.[UserID]
    ,U.[UserName]
    ,U.[AffiliateTag]
    ,U.[UserEmail]
    FROM [dbo].[StackOverflowAttributes] A
    INNER JOIN [dbo].[StackOverflow] U
    ON A.[UserID] = U.[UserID]
    WHERE A.[AttributeValue] LIKE 'UserName200%'
    ORDER BY U.[UserName];
    enter image description here
    现在,我们只读取索引行的一部分,然后执行查找。
    为了比较性能,最好使用:
    SET STATISTICS IO, TIME ON; 
    因为它将为您提供如何从索引中读取页面。结果可以可视化 here .

    关于sql-server - 键集分页 - 按跨多列的搜索词过滤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63316273/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com