硬 tsql 问题 - 有多少行值按顺序排列
Posted
技术标签:
【中文标题】硬 tsql 问题 - 有多少行值按顺序排列【英文标题】:hard tsql problem - how many row values are in a sequential order 【发布时间】:2011-04-29 19:10:56 【问题描述】:假设我有一张桌子
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 更新 2001 年 1 月 2 日或 2001 年 1 月 5 日,但在我可以更新之前,我必须确保它通过了一条规则,即三天内你不能有一个人连续。 如何在 mssql 存储过程中解决这个问题?
更新:它还需要解决这个布局以及我将在 2001 年 1 月 5 日更新的地方
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
【问题讨论】:
请澄清您的问题。您是在寻求有关 T-SQL 部分的帮助或如何编写存储过程吗?除非您另有说明,否则我将假设前者。 我不清楚。是只有预先知道的 personid 并且您正在尝试找到可以放置它们的日期,或者是预先知道的可能日期列表并且您需要知道哪个可以接受该 personid,或者您只是在寻找在不违反规则的情况下,您可以(单独)将每个日期更改为所有可能的 personid? 【参考方案1】:我假设 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
【讨论】:
我回答的第一行说我假设date
是唯一的。不是这样吗?在哪种情况下,您如何处理关系?
嗨,马丁- 如果只检查一个而不是三个连续,我将如何让上面的示例工作?
不确定您的意思?表中的每一行都将是至少一行的序列。【参考方案2】:
还有第三种情况没有列出,它是日期之间的情况。我将它包含在下面的解决方案中。
输出是
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
【讨论】:
【参考方案3】: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
【讨论】:
+1 这个简洁易用的答案。请注意,它缺少关于@personid 的约束:每个SELECT
都应该以WHERE ... AND personid=@personid
结尾。
@krubo - 这个答案可能看起来更简单,但它不起作用,您提出的修复也不会使它起作用! (您最终会排除其他 personid
,因此可能会错误地推断出连续有 3 个错误)
@Martin:请解释您的评论。这个解决方案怎么行不通?
@Boo - 如果您在修复程序中进行编辑可能会更容易,然后我会给您一些它不适用的示例数据。
例如,它不适用于我回答中的数据。见data.stackexchange.com/***/qt/99533(您需要选择“结果到文本”才能看到第二次选择的结果)【参考方案4】:
这是我的参数化解决方案:
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
的最大天数。
【讨论】:
以上是关于硬 tsql 问题 - 有多少行值按顺序排列的主要内容,如果未能解决你的问题,请参考以下文章