0

我遇到了一个关于表格中缺少行的问题,这让我很头疼。

作为基础数据,我有下表:

declare @table table
(
  id1   int,
  id2   int,
  ch    char(1) not null,
  val   int     
)

insert into @table values (1112, 121, 'A', 12)
insert into @table values (1351, 121, 'A', 13)
insert into @table values (1411, 121, 'B', 81)
insert into @table values (1312, 7, 'C', 107)
insert into @table values (1401, 2, 'A', 107)
insert into @table values (1454, 2, 'D', 107)
insert into @table values (1257, 6, 'A', 1)
insert into @table values (1269, 6, 'B', 12)
insert into @table values (1335, 6, 'C', 12)
insert into @table values (1341, 6, 'D', 5)
insert into @table values (1380, 6, 'A', 3)

输出应按 id2 排序并遵循固定的 ch 序列,该序列应重复直到下一个 id2 开始。

序列:

'A'
'B'
'C'
'D'

如果序列或模式被中断,它应该用 null 填充缺失的行,以便我得到这个结果表:

id1     id2     ch     val
----------------------------    
1112    121     'A'    12
NULL    121     'B'    NULL
NULL    121     'C'    NULL
NULL    121     'D'    NULL
1351    121     'A'    13
1411    121     'B'    81
NULL    121     'C'    NULL
NULL    121     'D'    NULL
NULL    7       'A'    NULL
NULL    7       'B'    NULL
1312    7       'C'    107
NULL    7       'D'    NULL
1401    2       'A'    107
NULL    2       'B'    NULL
NULL    2       'C'    NULL
1454    2       'D'    107

等等...

我正在寻找的是一种无需迭代即可做到这一点的方法。

我希望有人能帮帮忙!

提前致谢!

4

3 回答 3

1

一个解决方案可能是这样的:

declare @table table (  id1   int,  id2   int,  ch    char(1) not null,  val   int     )
insert into @table values (1112, 121, 'A', 12)
    ,(1351, 121, 'A', 13),(1411, 121, 'B', 81),(1312, 7, 'C', 107),(1401, 2, 'A', 107)
    ,(1454, 2, 'D', 107),(1257, 6, 'A', 1),(1269, 6, 'B', 12),(1335, 6, 'C', 12)
    ,(1341, 6, 'D', 5),(1380, 6, 'A', 3)
;with foo as
(select 
    *
    ,row_number() over (partition by id2 order by id1) rwn
    ,ascii(isnull(lag(ch,1) over (partition by id2 order by id1),'A'))-ascii('A')  prev
    ,count(*) over (partition by id2,ch) nr
    ,ascii(ch)-ascii('A') cur
from @table

)
,bar as
(
select 
*,case when cur<=prev and rwn>1 then 4 else 0 end + cur-prev step
from foo
)
,foobar as
(
select *,sum(step) over (partition by id2 order by id1 rows unbounded preceding) rownum
from bar
)
,iterations as
(
select id2,max(nr) nr from foo
group by id2
)
,blanks as
(
select
id2,ch chnr,char(ch+ascii('A') )ch,ROW_NUMBER() over (partition by id2 order by c.nr,ch)-1 rownum,c.nr
from iterations a
inner join (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) c(nr)
on c.nr<=a.nr
cross join (values (0),(1),(2),(3)) b(ch)
)
select
b.id1,a.id2,a.ch,b.val
from blanks a
left join foobar b
on a.id2=b.id2 and a.rownum=b.rownum 
order by a.id2,a.rownum

我首先进行查询“foo”,它查看行号并为每个 id2 获取 ch 的前一个值。

“bar”然后查找行之间有多少缺失值。例如,如果前一个是 A,当前是 ac,那么有 2 个。如果前一个是 A,当前是 A,那么有 4 个!

“foobar”然后添加步骤,从而对原始行进行编号,它们应该在最终输出中的位置。

“迭代”计算“ABCD”行应该出现的次数。

“BLANKS”然后是所有最终行,即对于每个 id2,它输出应该在最终输出中的所有“ABCD”行,并在 rownum 中对它们进行编号

最后,我在 id2 和 rownum 上离开了加入“foobar”和“BLANKS”。这样我们就得到了正确的行数,并且输出了原始中有值的地方。

于 2017-08-01T13:35:32.367 回答
0

如果您可以设法在表中添加一个额外的列,它定义了哪个 [id2] 是同一序列的一部分,您可以尝试以下操作:

declare @table table
(
  id1   int,
  id2   int,
  ch    char(1) not null,
  val   int,
  category  int -- extra column
)

insert into @table values (1112, 121, 'A', 12, 1)
insert into @table values (1351, 121, 'A', 13, 2)
insert into @table values (1411, 121, 'B', 81, 2)
insert into @table values (1312, 7, 'C', 107, 3)
insert into @table values (1401, 2, 'A', 107, 4)
insert into @table values (1454, 2, 'D', 107, 4)
insert into @table values (1257, 6, 'A', 1, 5)
insert into @table values (1269, 6, 'B', 12, 5)
insert into @table values (1335, 6, 'C', 12, 5)
insert into @table values (1341, 6, 'D', 5, 5)
insert into @table values (1380, 6, 'A', 3, 5)


DECLARE @sequence table (seq varchar(1))
INSERT INTO @sequence values ('A'), ('B'), ('C'), ('D')


SELECT b.id1, a.id2, a.seq, b.val, a.category
INTO #T1
FROM (
    SELECT *
    FROM  @table
    CROSS JOIN @sequence
) A
LEFT JOIN (
    SELECT * FROM @table
) B
    ON 1=1
    AND a.id1 = b.id1
    AND a.id2 = b.id2
    AND a.seq = b.ch
    AND a.val = b.val


;WITH rem_duplicates AS (
    SELECT *, dup = ROW_NUMBER() OVER (PARTITION by id2, seq, category ORDER BY id1 DESC)
    FROM #T1
) DELETE FROM rem_duplicates WHERE dup > 1


SELECT * FROM #T1 ORDER BY id2 DESC, category ASC, seq ASC

DROP TABLE #T1
于 2017-07-27T15:00:03.960 回答
0

我对你的输出有点困惑,试试这个:

更新

   DECLARE @table TABLE
    (
      row INT IDENTITY(1, 1) ,
      id1 INT ,
      id2 INT ,
      ch CHAR(1) NOT NULL ,
      val INT
    );

 DECLARE @Sequence TABLE ( ch3 CHAR(1) NOT NULL );


 INSERT INTO @Sequence
 VALUES ( 'A' );
 INSERT INTO @Sequence
 VALUES ( 'B' );
 INSERT INTO @Sequence
 VALUES ( 'C' );
 INSERT INTO @Sequence
 VALUES ( 'D' );

 INSERT INTO @table
 VALUES ( 1112, 121, 'A', 12 );
 INSERT INTO @table
 VALUES ( 1351, 121, 'A', 13 );
 INSERT INTO @table
 VALUES ( 1411, 121, 'B', 81 );
 INSERT INTO @table
 VALUES ( 1312, 7, 'C', 107 );
 INSERT INTO @table
 VALUES ( 1401, 2, 'A', 107 );
 INSERT INTO @table
 VALUES ( 1454, 2, 'D', 107 );
 INSERT INTO @table
 VALUES ( 1257, 6, 'A', 1 );
 INSERT INTO @table
 VALUES ( 1269, 6, 'B', 12 );
 INSERT INTO @table
 VALUES ( 1335, 6, 'C', 12 );
 INSERT INTO @table
 VALUES ( 1341, 6, 'D', 5 );
 INSERT INTO @table
 VALUES ( 1380, 6, 'A', 3 );




 SELECT r.id1 ,
        fin.id2 ,
        ch3 ,
        r.val
 FROM   ( SELECT    *
          FROM      ( SELECT    CASE WHEN r.chd - l.chd = 1 THEN 0
                                     ELSE 1
                                END [gap in sq] ,
                                l.*
                      FROM      ( SELECT    id2 ,
                                            ASCII(ch) chd ,
                                            ch ,
                                            val ,
                                            id1 ,
                                            row
                                  FROM      @table
                                ) AS l
                                LEFT JOIN ( SELECT  id2 ,
                                                    ASCII(ch) chd ,
                                                    row
                                            FROM    @table
                                          ) AS r ON l.row = r.row - 1
                    ) AS temp ,
                    @Sequence s
          WHERE     temp.[gap in sq] = 1
                    OR ( temp.[gap in sq] = 0
                         AND s.ch3 = temp.ch
                       )
        ) AS fin
        LEFT JOIN @table r ON r.id2 = fin.id2
                              AND r.id1 = fin.id1
                              AND r.ch = fin.ch3
于 2017-07-27T09:05:52.847 回答