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 - 在特定行之前/之后获取的主要内容,如果未能解决你的问题,请参考以下文章

如何从 MS ACCESS 数据库中的特定行和列中获取值?

MySQL:如果在日期之后达到特定值,则获取所有行

SQL:在连接后获取列上的特定行值

获取在特定日期之前创建且在该日期之后未引用的 ID

MS SQL 2008 - 获取数据库中的所有表名及其行数

基于一个值获取特定行的 SQL 查询