按顺序排列行并填补缺失行的空白
Order rows in a sequence and fill gaps for missing rows
我遇到了一个 table 中缺少行的问题,这让我很头疼。
作为基础数据,我有以下 table:
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'
如果序列或模式被打断,它应该用空值填充缺失的行,这样我就得到了这个结果table:
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
等等...
我正在寻找一种无需迭代即可完成此操作的方法。
希望有人能帮忙!
提前致谢!
我对你的输出有点困惑,试试这个:
更新
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
如果您可以设法在 table 中添加一个额外的列来定义哪些 [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
解决方案可能是这样的:
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,当前是一个c,则有2个。如果前一个是A,当前是一个A,则有4个!
"foobar" 然后添加步骤,从而对原始行进行编号,它们应该在最终输出中的位置。
"iterations" 计算 "ABCD" 行应该出现的次数。
"BLANKS" 然后是所有最后的行,即对于每个 id2,它输出所有应该在最终输出中的 "ABCD" 行,并在 rownum[=11= 中对它们进行编号]
最后我在 id2 和 rownum 上离开加入 "foobar" 和 "BLANKS"。这样我们就得到了正确的行数,把原来有值的地方输出了。
我遇到了一个 table 中缺少行的问题,这让我很头疼。
作为基础数据,我有以下 table:
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'
如果序列或模式被打断,它应该用空值填充缺失的行,这样我就得到了这个结果table:
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
等等...
我正在寻找一种无需迭代即可完成此操作的方法。
希望有人能帮忙!
提前致谢!
我对你的输出有点困惑,试试这个:
更新
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
如果您可以设法在 table 中添加一个额外的列来定义哪些 [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
解决方案可能是这样的:
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,当前是一个c,则有2个。如果前一个是A,当前是一个A,则有4个!
"foobar" 然后添加步骤,从而对原始行进行编号,它们应该在最终输出中的位置。
"iterations" 计算 "ABCD" 行应该出现的次数。
"BLANKS" 然后是所有最后的行,即对于每个 id2,它输出所有应该在最终输出中的 "ABCD" 行,并在 rownum[=11= 中对它们进行编号]
最后我在 id2 和 rownum 上离开加入 "foobar" 和 "BLANKS"。这样我们就得到了正确的行数,把原来有值的地方输出了。