MS SQL - 在特定行之前/之后获取
Posted
技术标签:
【中文标题】MS SQL - 在特定行之前/之后获取【英文标题】:MS SQL - Get Before / After Specific Rows 【发布时间】:2021-12-22 22:30:11 【问题描述】:我在 SQL 中有这个表。 SQL 中的 F 列和 G 列是空白的,但我已经展示了我希望如何在此表中填写它们。
当“有效”列为真时,我想在第一行开始之前捕获所有行,直到最后一行,例如它是假的。然后在 Pre Units 列中填充 Units 值。然后当它有最后一行时,我希望它填充“发布单位”列。请问您知道如何使用 MS SQL 做到这一点吗?
Table Sample
CREATE TABLE [dbo].[SODATA](
[PKID] [bigint] NOT NULL,
[Date] [date] NULL,
[ProductCode] [nvarchar](50) NULL,
[Units] [float] NULL,
[Valid] [bit] NULL,
[PreUnits] [decimal](18, 5) NULL,
[PostUnits] [decimal](18, 5) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (400, N'2019-04-01', N'Product1', 306, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (480, N'2019-04-08', N'Product1', 471, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (561, N'2019-04-15', N'Product1', 248, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (642, N'2019-04-22', N'Product1', 87, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (725, N'2019-04-29', N'Product1', 304, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (804, N'2019-05-06', N'Product1', 234, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (892, N'2019-05-13', N'Product1', 131, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (980, N'2019-05-20', N'Product1', 137, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1070, N'2019-05-27', N'Product1', 491, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1158, N'2019-06-03', N'Product1', 474, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1245, N'2019-06-10', N'Product1', 424, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1333, N'2019-06-17', N'Product1', 312, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1420, N'2019-06-24', N'Product1', 483, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1510, N'2019-07-01', N'Product1', 378, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1598, N'2019-07-08', N'Product1', 301, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1684, N'2019-07-15', N'Product1', 67, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1784, N'2019-07-22', N'Product1', 153, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1878, N'2019-07-29', N'Product1', 232, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (1974, N'2019-08-05', N'Product1', 145, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2069, N'2019-08-12', N'Product1', 84, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2163, N'2019-08-19', N'Product1', 231, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2257, N'2019-08-26', N'Product1', 454, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2351, N'2019-09-02', N'Product1', 297, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2445, N'2019-09-09', N'Product1', 274, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2539, N'2019-09-16', N'Product1', 331, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2633, N'2019-09-23', N'Product1', 348, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2727, N'2019-09-30', N'Product1', 220, NULL, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2821, N'2019-10-07', N'Product1', 300, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (2915, N'2019-10-14', N'Product1', 132, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (3009, N'2019-10-21', N'Product1', 68, 1, NULL, NULL)
GO
INSERT [dbo].[SODATA] ([PKID], [Date], [ProductCode], [Units], [Valid], [PreUnits], [PostUnits]) VALUES (3102, N'2019-10-28', N'Product1', 450, NULL, NULL, NULL)
GO
【问题讨论】:
数据图像不能帮助我们帮助您。花时间以可消耗的格式发布数据;首选 DDL 和 DML 语句。 要清楚,您是根据Date
列定义之前/之后?还是PKID
专栏?我知道两者似乎在您的图片中定义了相同的顺序,但最好清楚您使用哪些列来定义顺序。
@Damien_The_Unbeliever 它将基于产品代码然后日期。
@Larnu 我的道歉,我已经用你现在可以导入 SQL 的数据更新了这个问题。
[Date] [nvarchar](50) NULL,
不 - 重新开始。您将 DATES 存储在 DATE 数据类型中,而不是作为字符串
【参考方案1】:
您可以使用 CTE (Common Table Expressions)、LAG 和 LEAD 来做到这一点。
插入问题后的代码是:
;WITH mark_changes as (
select
PKID,Date,ProductCode,Units,Valid,
case when ISNULL(Valid,0)<>ISNULL(LAG(Valid,1,2) OVER(order by PKID),0) then 1 else 0 end as Changed
from SODATA
)
SELECT
PKID,Date,ProductCode,Units,Valid,
case when Valid=1 and Changed=1 then LAG(Units,1,0) OVER(order by PKID) else NULL end as PreUnits,
case when Valid=1 and ISNULL(LEAD(Valid,1) OVER(order by PKID),0)=0 then Units else NULL end as PostUnits
from mark_changes
order by PKID
您可以在 DB Fiddle here 上查看此示例。
【讨论】:
【参考方案2】:https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=3e35b5962e43c1466ee85b6cc08316f9lptr 作为评论发布,非常感谢。
update x
set preunits = newpreunits,
postunits = newpostunits
from
(
select *,
max(prvunits) over(partition by productcode, grpid) as newpreunits,
max(nxtunits) over(partition by productcode, grpid) as newpostunits
from
(
select *, sum(addme) over(partition by productcode order by date) as grpid
from
(
select *,
case when valid=1 and lead(valid) over(partition by productcode order by date) is null
then lead(units) over(partition by productcode order by date)
end as nxtunits,
case when valid=1 and lag(valid) over(partition by productcode order by date) is null
then lag(units) over(partition by productcode order by date)
end as prvunits,
case when
(valid=1 and lag(valid) over(partition by productcode order by date) is null)
or
(valid is null and lag(valid) over(partition by productcode order by date) = 1)
then 1 else 0 end as addme
from sodata
) as agg
) as g
) as x
where newpreunits is not null
or newpostunits is not null
【讨论】:
以上是关于MS SQL - 在特定行之前/之后获取的主要内容,如果未能解决你的问题,请参考以下文章