【问题标题】:SQL, using rank to generate columnsSQL,使用排名生成列
【发布时间】:2017-09-12 15:20:36
【问题描述】:

我有一个属性的占用者列表,并且需要处理数据,因此它将属性显示为一行,每个额外的占用者出现在一个新列中。

这是我到目前为止所做的:

with RANKING AS 
(   Select 
    Postcode
    , Number
    , Occupant
    , RANK() OVER
    (Partition by Postcode order by Occupant) as [Rank]

from Reporting.dbo.Test --order by [Rank] desc
)

RANKING中的查询输出下表:

Postcode | Number | Occupant | Rank
AA001AA  |  12    |    D     |  1
AA001AA  |  12    |    E     |  2
AA001AA  |  12    |    K     |  3
AA001AA  |  12    |    M     |  4
AA001AA  |  12    |    T     |  5
BB001BB  |   8    |    M     |  1
BB001BB  |   8    |    R     |  2

等等

然后我尝试使用排名的值来创建列,如下所示:

Select distinct
i.Postcode
, i.Number
    , case when i.[rank] = 1 then i.Occupant end as [First Tennant]
    , case when i.[rank] = 2 then i.Occupant end as [Second Tennant]
    , case when i.[rank] = 3 then i.Occupant end as [Third Tennant]
    , case when i.[rank] = 4 then i.Occupant end as [Fourth Tennant]
    , case when i.[rank] = 5 then i.Occupant end as [Fifth Tennant]

    from Reporting.dbo.Test u
            inner join RANKING i on i.Postcode = u.Postcode

现在,两个问题:

1) 有没有办法自动化这个过程,所以对于 x 的排名,我们有 x 个租户行

2) 这个输出的表格是

Postcode | Number | First Tennant | Second Tennant | Third Tennant | Fourth Tennant | Fifth Tennant |
AA001AA  |   12   |        D      |       NULL     |       NULL    |       NULL     |      NULL     |
AA001AA  |   12   |      NULL     |         E      |       NULL    |       NULL     |      NULL     |

等等

如何压缩此列表,以便每个邮政编码只有一行,并且所有非空值都出现在该行上。

【问题讨论】:

  • 您需要实际的单词“First”、“second”等吗?或者那里有数字就足够了?它不会从根本上改变答案,但如果您需要这些,您可能需要一个额外的映射表来将数字映射到它们的字符串表示形式。
  • 您可以使用动态枢轴自动执行此操作。列数将等于给定邮政编码/数字所需的最大值。所有不包含这些租户的行都将出现 NULL。
  • 任何区分它的方法都可以,如果有一种方法,例如,Tennant(排名值),那很好。
  • 您可以使用像这样的动态枢轴stackoverflow.com/questions/10404348/…

标签: sql-server rank


【解决方案1】:

要首先回答您的第二个问题(如何压缩列表),最简单的方法是 group by postcodenumber。您可以只取每一列的max()(即,如果有一个值,它就会被选中;所有的空值都会掉出来)。

要回答第一个问题(这是否可以自动化),您可能需要研究动态枢轴。 cmets 中发布的一篇这样的文章是:SQL Server dynamic PIVOT query?

这个想法基本上是序列化不同的租户,并将其连接到一个动态的 SQL 字符串中,该字符串执行一个数据透视。有几种方法可以给这只猫剥皮,但我是这样处理的。

use tempdb
go

if object_id('tempdb.dbo.#data') is not null drop table #data
create table #data
(
    PostCode varchar(10),
    Number int,
    Occupant char(1),
    Rnk int,
    ColName as 'Tennant ' + cast(Rnk as varchar(10))

)

insert into #Data(PostCode, Number, Occupant, Rnk)
select 'AA001AA', 12, 'D',1
union all select 'AA001AA', 12, 'E',2
union all select 'AA001AA', 12, 'K',3
union all select 'AA001AA', 12, 'M',4
union all select 'AA001AA', 12, 'T',5
union all select 'BB001BB',  8, 'M',1
union all select 'BB001BB',  8, 'R',2

declare 
    @PivotColumns nvarchar(max),
    @SelectColumns nvarchar(max),
    @sql nvarchar(max)

select 
    @PivotColumns = stuff((select ',' + quotename(ColName)
                      from #data
                      group by ColName
                      order by ColName
                      for xml path('')), 1, 1, ''),
    @SelectColumns = stuff((select ',' + quotename(ColName) + ' = max(' + quotename(ColName) + ')'
                            from #data
                            group by ColName
                            order by ColName
                            for xml path('')), 1, 1, ''),
    @sql = '
        select 
            PostCode, 
            Number,
            ' + @SelectColumns + '
        from #data d
        pivot (max(Occupant) for ColName in (' + @PivotColumns + ' )) p
        group by PostCode, Number'


print @sql
exec sp_executesql @sql

【讨论】:

    【解决方案2】:

    您可以使用动态枢轴来获得结果。请看下面的代码 -

    create  table #tab (Postcode varchar(10) , Number int , Occupant char(1) , Rank int)
    
    insert into #tab
    select 'AA001AA'  ,  12    ,    'D'     ,  1
    union all select 'AA001AA'  ,  12    ,    'E'     ,  2
    union all select 'AA001AA'  ,  12    ,    'K'     ,  3
    union all select 'AA001AA'  ,  12    ,    'M'     ,  4
    union all select 'AA001AA'  ,  12    ,    'T'     ,  5
    union all select 'BB001BB'  ,   8    ,    'M'     ,  1
    union all select 'BB001BB'  ,   8    ,    'R'     ,  2
    union all select 'CC001CC'  ,   8    ,    'N'     ,  1
    union all select 'CC001CC'  ,   8    ,    'O'     ,  2
    union all select 'CC001CC'  ,   8    ,    'P'     ,  3
    union all select 'CC001CC'  ,   8    ,    'Q'     ,  4
    union all select 'CC001CC'  ,   8    ,    'R'     ,  5
    union all select 'CC001CC'  ,   8    ,    'S'     ,  6
    union all select 'CC001CC'  ,   8    ,    'T'     ,  7
    union all select 'CC001CC'  ,   8    ,    'U'     ,  8
    union all select 'CC001CC'  ,   8    ,    'V'     ,  9
    union all select 'CC001CC'  ,   8    ,    'W'     ,  10
    
    declare @mx int , @min int = 1 , @sql nvarchar(max) = '' , @select1 nvarchar(max) = '',@select2 nvarchar(max) = ''
    
    select @mx = MAX(rank) from #tab
    
    while @min<= @mx
    begin
    set @select1  = @select1 + '[' + cast(@min as varchar(10))+ '] ,' 
    set @select2  = @select2 + '[' + cast(@min as varchar(10))+ ']  as '+ '[Tennant_' + cast(@min as varchar(10))+ '] ,' 
    set @min = @min + 1
    end 
    
    set @select1  = SUBSTRING( @select1 , 1 , LEN(@select1)-1)
    set @select2  = SUBSTRING( @select2 , 1 , LEN(@select2)-1)
    
    
    set @sql = '
     SELECT Postcode    , Number ,'+@select2+'
    FROM  #tab
    PIVOT
    (
        max(Occupant)
        FOR [Rank] IN ('+@select1+')
    )AS pvt '
    
     exec sp_executesql @sql
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-22
      • 1970-01-01
      • 2011-05-18
      • 2013-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多