有些条件的序号

时间:2016-12-08 18:46:05

标签: sql sql-server sql-server-2016

我有一个包含 SQL Server 2016 的多个字段的视图,如果确定条件满足,我想要输入一个序列号。 举例来说,请考虑此表:

表格

GLOBALID | SERVICE_GLOBALID | PHOTO_A_GLOBALID | PHOTO_B_GLOBALID
---------+------------------+------------------+-----------------
EB8F9BE7 | 0CD85BE7         | 490794EC         | 181C5426
EB8F9BE7 | 04405456         | C3A75999         | 5BA26B15
ECF5F7D1 | 8E8A63C2         | 1A225178         | NULL
ECF5F7D1 | 4EB18887         | 2139CF18         | C7B96E0E

一些细节:

  • GLOBALID可以重复(因为这是来自多个表CROSS APPLYOUTER APPLY)的视图;
  • SERVICE_GLOBALID永远不会重复;
  • PHOTO_A_GLOBALID总是有值(永不重复);
  • PHOTO_B_GLOBALID可以有一个值,但有时候是NULL(在有值时不会重复)。

因此,我想为PHOTO_A_GLOBALIDPHOTO_B_GLOBALID添加序号,但序列号必须使用视图中的两列。如果PHOTO_B_GLOBALIDNULL,则序号不得递增。

这是我想要的结果(请看第三行):

结果

GLOBALID | SERVICE_GLOBALID | PHOTO_A_GLOBALID | PHOTO_B_GLOBALID | SEQ_A | SEQ_B
---------+------------------+------------------+------------------+-------+------
EB8F9BE7 | 0CD85BE7         | 490794EC         | 181C5426         | 1     | 2
EB8F9BE7 | 04405456         | C3A75999         | 5BA26B15         | 3     | 4
ECF5F7D1 | 8E8A63C2         | 1A225178         | NULL             | 5     | NULL
ECF5F7D1 | 4EB18887         | 2139CF18         | C7B96E0E         | 6     | 7

我尝试过使用ROW_NUMBER(),但我真的不知道如何用两列来做这件事。

5 个答案:

答案 0 :(得分:2)

试试这个:

rextester:http://rextester.com/IPBQPM64562

if exists (select * from tempdb.sys.objects where name like '#global%') begin; drop table #global; end;
if not exists (select * from tempdb.sys.objects where name like '#global%')
begin
create table #global (globalid varchar(32) ,service_globalid varchar(32) ,photo_a_globalid varchar(32) ,photo_b_globalid varchar(32) ) 
insert into #global values  
    ('EB8F9BE7' ,'0CD85BE7' ,'490794EC' ,'181C5426') 
  , ('EB8F9BE7' ,'4405456' ,'C3A75999' ,'5BA26B15')
  , ('ECF5F7D1' ,'8E8A63C2' ,'1A225178' ,null) 
  , ('ECF5F7D1' ,'4EB18887' ,'2139CF18' ,'C7B96E0E') 
  , ('XXXXXXXX' ,'VVVVVVVV' ,'ZZZZAAAA' ,null) 
  , ('XXXXXXXX' ,'YYYYYYYY' ,'ZZZZBBBB' ,'ZZZZCCCC') 
end;

with cte as (
  select 
      globalid
    , service_globalid
    , photo_globalid=photo_a_globalid 
    , AorB = convert(char(1), 'A')
    from #global
  union all
  select 
      globalid
    , service_globalid
    , photo_globalid=photo_b_globalid 
    , AorB = convert(char(1), 'B')
    from #global
    where photo_b_globalid is not null
    )
, x as (
  select 
      globalid
    , service_globalid
    , photo_globalid
    , seq = row_number() over (order by globalid asc, service_globalid, AorB  asc)
  from cte  
  )

  select 
      g.globalid
    , g.service_globalid
    , g.photo_a_globalid 
    , g.photo_b_globalid 
    , seq_a = xa.seq
    , seq_b = xb.seq
    from #global g 
      left join x as xa on g.globalid=xa.globalid and g.service_globalid=xa.service_globalid and g.photo_a_globalid=xa.photo_globalid
      left join x as xb on g.globalid=xb.globalid and g.service_globalid=xb.service_globalid and g.photo_b_globalid=xb.photo_globalid
    order by g.globalid, g.service_globalid

答案 1 :(得分:1)

正如其他答案所示,有不止一种方法可以做到这一点,但对我来说最直接的翻译是使用count函数计算:

select *,
  case
  when PHOTO_A_GLOBALID is not null
  then count(PHOTO_A_GLOBALID)
        over (order by DATE rows unbounded preceding)
     + count(PHOTO_B_GLOBALID)
        over (order by DATE rows between unbounded preceding and 1 preceding)
  end,
  case
  when PHOTO_B_GLOBALID is not null
  then count(PHOTO_A_GLOBALID)
        over (order by DATE rows unbounded preceding)
     + count(PHOTO_B_GLOBALID)
        over (order by DATE rows unbounded preceding)
  end
from t;

基本思路是计算当前行所见的所有PHOTO_A_GLOBALID值,以及直到当前行所见的所有PHOTO_B_GLOBALID值,只需稍微调整即可确保当前行和#39; PHOTO_B_GLOBALID值不会影响SEQ_A的计算值。

现在,您可以通过PHOTO_A_GLOBALID永远不会NULL的知识来缩短这一点,但在我看来,对于这两个列具有相同的逻辑使查询更容易理解。

您还可以在指定rows unbounded precedingorder by是默认值的情况下缩短这一点,但您需要明确rows between unbounded preceding and 1 preceding位。

答案 2 :(得分:0)

这有点棘手。我建议做一个累积和和一些额外的算术:

select t.*,
       (sum(incThisRow) over (order by globalid, service_globalid) - incThisRow + 1
       ) as seq_A,
       (case when photo_b_globalid is not null
             then sum(incThisRow) over (order by globalid, service_globalid)
        end) as seq_B
from (select t.*,
             (case when PHOTO_B_GLOBALID is null then 1 else 2 end) as incThisRow
      from t
     ) t;

答案 3 :(得分:0)

你可以......

  • 将照片数据分解为两组(A,B)并将它们合并为
  • 从B&消除空白照片的记录编号
  • 加入回到基本集以获取完整数据

UNTESTED

With CTE AS (
SELECT GLOBALID, SERVICE_GLOBALID,  PHOTO_A_GLOBALID, 'a' as RowID
union
SELECT GLOBALID, SERVICE_GLOBALID,  PHOTO_B_GLOBALID, 'b'),

CTE2 as (
SELECT A.*, row_number() over order by (date) RN
FROM CTE A
WHERE PHOTO_B_GLOBALID is not null)

SELECT A.*, B.PHOTO_B_GLOBALID, A.RN as SEQ_A, B.RN as SEQ_B
FROM cte A
LEFT JOIN CTE2 B
  on A.GLOBALID =B.GLOBALID
 and A.SERVICE_GLOBALID = B.SERVICE_GLOBALID
 and A.RowID = 'a'
 and b.rowID = 'b'

答案 4 :(得分:0)

你可以尝试这样......

;WITH cte
AS (SELECT *, RowN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL) )
FROM (SELECT GLOBALID, SERVICE_GLOBALID, PHOTO_A_GLOBALID
FROM yourglobal WHERE PHOTO_A_GLOBALID IS NOT NULL
UNION ALL
SELECT GLOBALID, SERVICE_GLOBALID, PHOTO_B_GLOBALID
FROM yourglobal WHERE PHOTO_B_GLOBALID IS NOT NULL) a)
SELECT
  yg.*, c.RowN AS SEQ_A, cb.RowN AS SEQ_B
FROM yourglobal yg
LEFT JOIN cte c
  ON yg.PHOTO_A_GLOBALID = c.PHOTO_A_GLOBALID
LEFT JOIN cte cb
  ON yg.PHOTO_B_GLOBALID = cb.PHOTO_A_GLOBALID