硬 tsql 问题 - 有多少行值按顺序排列

发布于 2024-11-04 16:13:22 字数 432 浏览 4 评论 0原文

假设我有一个表

date,personid
1/1/2001 1
1/2/2001 3
1/3/2001 2
1/4/2001 2
1/5/2001 5
1/6/2001 5
1/7/2001 6

,我将使用 personid 2 更新 1/2/2001 或 1/5/2001,但在更新之前,我必须确保它通过了一条规则,表明您不能拥有一个人连续三天。 我该如何在 mssql 存储过程中解决这个问题?

更新:它还需要解决这个布局以及我更新 1/5/2001 的地方

date,personid
1/1/2001 1
1/2/2001 3
1/3/2001 2
1/4/2001 2
1/5/2001 1
1/6/2001 2
1/7/2001 2
1/8/2001 5
1/9/2001 5
1/10/2001 6

lets say I have a table with

date,personid
1/1/2001 1
1/2/2001 3
1/3/2001 2
1/4/2001 2
1/5/2001 5
1/6/2001 5
1/7/2001 6

and I'm going to either update 1/2/2001 or 1/5/2001 with personid 2 but before I can update I have to make sure it passes a rule that says you can't have a person three days in a row.
how can i solve this in a mssql stored procedure?

update: It also need to solve this layout as well where I'd update 1/5/2001

date,personid
1/1/2001 1
1/2/2001 3
1/3/2001 2
1/4/2001 2
1/5/2001 1
1/6/2001 2
1/7/2001 2
1/8/2001 5
1/9/2001 5
1/10/2001 6

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

以可爱出名 2024-11-11 16:13:23

我假设 date 是唯一的,如果情况并非如此,请告诉我!

DECLARE @basedata TABLE ([date] UNIQUE DATE,personid INT)
INSERT INTO @basedata
SELECT GETDATE()+1, 2 union all
SELECT GETDATE()+2, 3 union all
SELECT GETDATE()+3, 2 union all
SELECT GETDATE()+4, 2 union all
SELECT GETDATE()+5, 5 union all
SELECT GETDATE()+6, 5 union all
SELECT GETDATE()+7, 6


DECLARE @date date = GETDATE()+5
DECLARE @personid int = 2


;WITH T AS
(
SELECT TOP 2 [date],personid   
FROM @basedata 
WHERE [date] < @date
ORDER BY [date] DESC
UNION ALL
SELECT @date, @personid 
UNION ALL
SELECT TOP 2 [date],personid   
FROM @basedata
WHERE [date] > @date
ORDER BY [date]   
),T2 AS
(
SELECT *, 
       ROW_NUMBER() OVER (ORDER BY [date]) - 
       ROW_NUMBER() OVER (PARTITION BY personid ORDER BY [date]) AS Grp
FROM T
)
SELECT COUNT(*) /*Will return a result if that date/personid 
                  would cause a sequence of 3*/
FROM T2 
GROUP BY personid,Grp
HAVING COUNT(*) >=3

I've assumed that date is unique let me know if that is not the case!

DECLARE @basedata TABLE ([date] UNIQUE DATE,personid INT)
INSERT INTO @basedata
SELECT GETDATE()+1, 2 union all
SELECT GETDATE()+2, 3 union all
SELECT GETDATE()+3, 2 union all
SELECT GETDATE()+4, 2 union all
SELECT GETDATE()+5, 5 union all
SELECT GETDATE()+6, 5 union all
SELECT GETDATE()+7, 6


DECLARE @date date = GETDATE()+5
DECLARE @personid int = 2


;WITH T AS
(
SELECT TOP 2 [date],personid   
FROM @basedata 
WHERE [date] < @date
ORDER BY [date] DESC
UNION ALL
SELECT @date, @personid 
UNION ALL
SELECT TOP 2 [date],personid   
FROM @basedata
WHERE [date] > @date
ORDER BY [date]   
),T2 AS
(
SELECT *, 
       ROW_NUMBER() OVER (ORDER BY [date]) - 
       ROW_NUMBER() OVER (PARTITION BY personid ORDER BY [date]) AS Grp
FROM T
)
SELECT COUNT(*) /*Will return a result if that date/personid 
                  would cause a sequence of 3*/
FROM T2 
GROUP BY personid,Grp
HAVING COUNT(*) >=3
岁月流歌 2024-11-11 16:13:23

还有第三种情况没有列出,它是间隔日期的情况。我将其包含在下面的解决方案中。

输出是

PersonId    TrackDate  UnallowedBefore UnallowedAfter
----------- ---------- --------------- --------------
2           01/04/2001 01/02/2001      01/05/2001
5           01/06/2001 01/04/2001      01/07/2001
6           01/08/2001 01/08/2001      01/08/2001

USE tempdb
GO
IF OBJECT_ID('PersonDates') IS NOT NULL DROP TABLE PersonDates
CREATE TABLE PersonDates
(
  PersonId  int      NOT NULL,
  TrackDate datetime NOT NULL
)

INSERT INTO PersonDates
(
  TrackDate, 
  PersonId
)
SELECT '1/1/2001', 1
UNION ALL
SELECT '1/2/2001', 3
UNION ALL
SELECT '1/3/2001', 2
UNION ALL
SELECT '1/4/2001', 2
UNION ALL
SELECT '1/5/2001', 5
UNION ALL
SELECT '1/6/2001', 5
UNION ALL
SELECT '1/7/2001', 6
UNION ALL
SELECT '1/8/2001', 2
UNION ALL
SELECT '1/9/2001', 6

SELECT
  P.PersonId,
  TrackDate = CONVERT(varchar(10), DATEADD(day,  1, P.TrackDate), 101),
  T.UnallowedBefore,
  T.UnallowedAfter
FROM
  PersonDates P

  CROSS APPLY
  (
    SELECT TOP 1 
      UnallowedAfter  = CASE
        WHEN DATEDIFF(day, P.TrackDate, TrackDate) = 1
        THEN CONVERT(varchar(10), DATEADD(day,  1, TrackDate), 101)
        ELSE CONVERT(varchar(10), DATEADD(day, -1, TrackDate), 101)
      END,
      UnallowedBefore = CASE
        WHEN DATEDIFF(day, P.TrackDate, TrackDate) = 1
        THEN CONVERT(varchar(10), DATEADD(day, -2, TrackDate), 101)
        ELSE CONVERT(varchar(10), DATEADD(day, -1, TrackDate), 101)
      END
    FROM 
      PersonDates 
    WHERE 
      PersonId = P.PersonId
      AND
      DATEDIFF(day, P.TrackDate, TrackDate) IN (1,2)
  ) T

There is a third case not listed, it is the between date case. I included it in the solution below.

The output is

PersonId    TrackDate  UnallowedBefore UnallowedAfter
----------- ---------- --------------- --------------
2           01/04/2001 01/02/2001      01/05/2001
5           01/06/2001 01/04/2001      01/07/2001
6           01/08/2001 01/08/2001      01/08/2001

USE tempdb
GO
IF OBJECT_ID('PersonDates') IS NOT NULL DROP TABLE PersonDates
CREATE TABLE PersonDates
(
  PersonId  int      NOT NULL,
  TrackDate datetime NOT NULL
)

INSERT INTO PersonDates
(
  TrackDate, 
  PersonId
)
SELECT '1/1/2001', 1
UNION ALL
SELECT '1/2/2001', 3
UNION ALL
SELECT '1/3/2001', 2
UNION ALL
SELECT '1/4/2001', 2
UNION ALL
SELECT '1/5/2001', 5
UNION ALL
SELECT '1/6/2001', 5
UNION ALL
SELECT '1/7/2001', 6
UNION ALL
SELECT '1/8/2001', 2
UNION ALL
SELECT '1/9/2001', 6

SELECT
  P.PersonId,
  TrackDate = CONVERT(varchar(10), DATEADD(day,  1, P.TrackDate), 101),
  T.UnallowedBefore,
  T.UnallowedAfter
FROM
  PersonDates P

  CROSS APPLY
  (
    SELECT TOP 1 
      UnallowedAfter  = CASE
        WHEN DATEDIFF(day, P.TrackDate, TrackDate) = 1
        THEN CONVERT(varchar(10), DATEADD(day,  1, TrackDate), 101)
        ELSE CONVERT(varchar(10), DATEADD(day, -1, TrackDate), 101)
      END,
      UnallowedBefore = CASE
        WHEN DATEDIFF(day, P.TrackDate, TrackDate) = 1
        THEN CONVERT(varchar(10), DATEADD(day, -2, TrackDate), 101)
        ELSE CONVERT(varchar(10), DATEADD(day, -1, TrackDate), 101)
      END
    FROM 
      PersonDates 
    WHERE 
      PersonId = P.PersonId
      AND
      DATEDIFF(day, P.TrackDate, TrackDate) IN (1,2)
  ) T
瀞厅☆埖开 2024-11-11 16:13:23
SET @TargetDate = '1/2/2001'

SELECT @ForwardCount = COUNT(*) FROM table WHERE ([date] BETWEEN @TargetDate AND DATEADD(dd, 2, @TargetDate)) WHERE PersonID = @PersonID

SELECT @BackwardCount = COUNT(*) FROM table WHERE ([date] BETWEEN @TargetDate AND DATEADD(dd, -2, @TargetDate)) WHERE PersonID = @PersonID

SELECT @BracketCount = COUNT(*) FROM table WHERE ([date] BETWEEN DATEADD(dd, -1, @TargetDate) AND DATEADD(dd, 1, @TargetDate)) WHERE PersonID = @PersonID

IF (@ForwardCount < 2) AND (@BackwardCount < 2) AND (@BracketCount < 2)
BEGIN
-- Do your update here
END
SET @TargetDate = '1/2/2001'

SELECT @ForwardCount = COUNT(*) FROM table WHERE ([date] BETWEEN @TargetDate AND DATEADD(dd, 2, @TargetDate)) WHERE PersonID = @PersonID

SELECT @BackwardCount = COUNT(*) FROM table WHERE ([date] BETWEEN @TargetDate AND DATEADD(dd, -2, @TargetDate)) WHERE PersonID = @PersonID

SELECT @BracketCount = COUNT(*) FROM table WHERE ([date] BETWEEN DATEADD(dd, -1, @TargetDate) AND DATEADD(dd, 1, @TargetDate)) WHERE PersonID = @PersonID

IF (@ForwardCount < 2) AND (@BackwardCount < 2) AND (@BracketCount < 2)
BEGIN
-- Do your update here
END
农村范ル 2024-11-11 16:13:23

这是我的参数化解决方案:

WITH nearby AS (
  SELECT
    date,
    personid = CASE date WHEN @date THEN @personid ELSE personid END
  FROM atable
  WHERE date BETWEEN DATEADD(day, -@MaxInARow, @date)
                 AND DATEADD(day,  @MaxInARow, @date)
),
nearbyGroups AS (
  SELECT
    *,
    Grp = DATEDIFF(day, 0, date) -
          ROW_NUMBER() OVER (PARTITION BY personid ORDER BY date)
  FROM nearby
)
UPDATE atable
SET personid = @personid
WHERE date = @date
  AND NOT EXISTS (
    SELECT Grp 
    FROM nearbyGroups
    GROUP BY Grp
    HAVING COUNT(*) > @MaxInARow
  )

@date 表示应更新 personid 列的日期。 @personid 是要存储的新值。 @MaxInARow 是允许存储相同personid 的连续最大天数。

Here's my parametrised solution:

WITH nearby AS (
  SELECT
    date,
    personid = CASE date WHEN @date THEN @personid ELSE personid END
  FROM atable
  WHERE date BETWEEN DATEADD(day, -@MaxInARow, @date)
                 AND DATEADD(day,  @MaxInARow, @date)
),
nearbyGroups AS (
  SELECT
    *,
    Grp = DATEDIFF(day, 0, date) -
          ROW_NUMBER() OVER (PARTITION BY personid ORDER BY date)
  FROM nearby
)
UPDATE atable
SET personid = @personid
WHERE date = @date
  AND NOT EXISTS (
    SELECT Grp 
    FROM nearbyGroups
    GROUP BY Grp
    HAVING COUNT(*) > @MaxInARow
  )

@date represents the date for which the personid column should be updated. @personid is the new value to be stored. @MaxInARow is the maximum number of days in a row for which the same personid is allowed to be stored.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文