【问题标题】:SQL query row_number() over partition by query result return wrong for some case在某些情况下,按查询结果对分区进行 SQL 查询 row_number() 会返回错误
【发布时间】:2021-09-03 21:49:30
【问题描述】:

我有两个 SQL Server 表:

FormSchema

SchemaId, SchemaName, Tenant

RoleTable

SchemaId, RoleId, RoleName, Tenant

预期结果(包括以下三个条件):

  • 在结果中选择租户“A”中存在单个 SchemaName 的行
  • 在结果中选择租户“全部”中存在单个 SchemaName 的行
  • 如果租户“A”和“全部”中都存在重复/相同的 SchemaName,则选择结果中属于租户“A”的行。

查询:

select * 
from 
    (select 
         fs1.schemaid, fs1.schemaName, ar1.roleId, ar1.roleName, ar1.tenant,
         rn = row_number() over (partition by fs1.schemaName
                                 order by case when ar1.tenant = 'ALL' then 2 else 1 end, ar1.tenant)
     from   
         RolesTable ar1
     full outer join 
         FormSchema fs1 on ar1.SchemaId = fs1.SchemaId) as t3
where 
    rn = 1
    and Tenant in ('B', 'All')

请参阅db fiddle 了解表格、记录以及预期和实际结果。

将租户作为“B”预期结果时我面临的问题是错误的,但如果我使用“A”预期结果是正确的。

“B”的预期结果是错误的**,因为它不满足以下条件:

  • 如果租户“B”和“全部”中存在重复/相同的 SchemaName,则选择结果中属于租户“B”的行。即架构名称“汽车”。所以查询应该在结果'B''Car'行中选择。

“B”的预期结果:

SchemaId   RoleId                                   RoleName    Tenant
--------------------------------------------------------------------
'664'      '40ecca83-7fd9-4d63-9f56-c7a48442d844'   '#Test-1'   'B'
'456'      '40ecca83-7fd9-4d63-9f56-c7a48442d844'   '#Test-1'   'B'

提前致谢。

【问题讨论】:

  • 发布您对“B”的预期结果并解释您当前结果错误的原因。
  • 我已经更新了两个问题

标签: sql sql-server tsql join


【解决方案1】:

您应该将Tenant 的条件从子查询的外部查询中移出:

select * 
from (
  select fs1.schemaid, fs1.schemaName, ar1.roleId, ar1.roleName, ar1.tenant,
         row_number() over (
           partition by fs1.schemaName
           order by case when ar1.tenant = 'ALL' then 2 else 1 end, ar1.tenant
         ) rn
  from RolesTable ar1 full outer join FormSchema fs1
  on ar1.SchemaId= fs1.SchemaId
  where ar1.Tenant in (?, 'All')
) as t3
where rn = 1

? 替换为'A''B'

此外,尚不清楚您为什么要进行 FULL 加入而不是 LEFT 加入。
如果您希望两个表中的结果不匹配,那么您可能应该使用COALESCE()

where coalesce(ar1.Tenant, fs1.Tenant) in (?, 'All')

请参阅demo

【讨论】:

    【解决方案2】:

    我觉得这个逻辑有点难以理解。我不明白你为什么在 inner join 工作时使用 full join

    但是,我认为您需要做的是在子查询中进行过滤:

    select * 
    from (select fs1.schemaid, fs1.schemaName, ar1.roleId, ar1.roleName, ar1.tenant ,
                 row_number() over (partition by fs1.schemaName
                              order by case when ar1.tenant = 'ALL' then 2 else 1 end, ar1.tenant) as seqnum
          from RolesTable ar1 join
               FormSchema fs1
              on ar1.SchemaId= fs1.SchemaId
          where ar1.Tenant in ('B', 'All')
         ) t3
    where seqnum = 1 ;
    

    【讨论】:

    • 我不确定,但我在想使用完全外连接的原因是 1.可能某些行仅属于租户“A”2.可能某些行仅属于租户“B”3 . 可能某行属于租户“A”和“B”
    • @r08 。 . .如果是这种情况,OP 应该提供证明这一点的数据。但是,表FormSchema 似乎是定义SchemaIdinner join 的实体——或者left join——是所有必要的。
    • 不,不需要完全外连接。我认为可能需要选择所有结果,所以我保留了......实现上述预期结果的最佳实践是什么......正如你提到的有点难跟随?
    猜你喜欢
    • 2012-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多