Valid From 和 Valid To 列 - 仅显示基于日期之间的值的最新行
Valid From and Valid To columns - Only Show Latest Row based on Value between Dates
我为这件事焦头烂额,不是 100% 确定这是可能的,但在 SQL 中一切皆有可能所以它一定是?!
目的是能够历史地跟踪每个月底有多少活跃帐户。
我有两个table:
star_UserAccounts
- 每小时保存一次帐户信息。
- 如果数据相同,则不会发生任何事情。
- 如果数据发生变化,现有行将被标记为旧行并插入新行。这使一个很好的小历史可以看到正在发生的变化。
最后一天
- 保存我想要数据的日期(仅作为示例)
在“star_UserAccounts”table 我有(升序):
UserId
UserStatus
InsertedDate
ValidToDate
Row Version
JoeBloggs
Active
2019-07-19 13:43:09.083
2019-10-31 16:08:27.633
1
JoeBloggs
Active
2019-10-31 16:08:28.027
2020-01-09 10:08:27.840
2
JoeBloggs
Active
2020-01-09 10:08:28.013
2020-01-09 11:08:28.813
3
JoeBloggs
Active
2020-01-09 11:08:28.970
2020-01-16 11:08:24.547
4
现在 - 我想 return 2019 年 10 月 31 日有效的详细信息。那天有两次更新,因此那天的最新 RowVersion 应该是 returned .
当我将日期硬编码到我的 select 脚本中时,我可以获得帐户的单独行版本,但是当我尝试一次性将其应用于多个不同的日期时,它同时获取了两行2019 年 10 月 31 日,但我只希望它在需要的日期获取最新的行。
LastDayOfMonth
ActiveAtDate
2019-07-31
1
2019-08-31
1
2019-09-30
1
2019-10-31
2 <-- The problem row
2019-11-30
1
2019-12-31
1
我怎么才能确保为每个推入的日期提取最新的行?
这里有一个 SQL Fiddle 可以玩:http://sqlfiddle.com/#!18/ed372/12
我保留了子查询中的语法错误,因为这种显示了我想要实现的目标,因为我需要将日期传递到收集该日期的 MAX 记录的子查询中。
感谢任何帮助。很难用简单的方式解释,如果很混乱,请见谅。
谢谢。
更新
重新阅读您的 post 后,我意识到在回答之前我没有完全理解您的问题。我会留下我原来的答案,因为它可能会有所帮助。
您的情况可能与使用 COUNT DISTINCT
一样简单,例如:
SELECT
LastDayOfMonth,
COUNT ( DISTINCT UserId ) AS ActiveAtDate
FROM @star_UserAccounts AS ua
CROSS APPLY (
SELECT LastDayOfMonth FROM @LastDayOfMonth AS ld
WHERE ld.LastDayOfMonth BETWEEN CAST( ua.InsertedDate AS date ) AND CAST( ua.ValidToDate AS date )
) AS x
GROUP BY
LastDayOfMonth
ORDER BY
LastDayOfMonth;
RETURNS
+----------------+--------------+
| LastDayOfMonth | ActiveAtDate |
+----------------+--------------+
| 2019-07-31 | 1 |
| 2019-08-31 | 1 |
| 2019-09-30 | 1 |
| 2019-10-31 | 1 |
| 2019-11-30 | 1 |
| 2019-12-31 | 1 |
+----------------+--------------+
使用 CROSS APPLY
在 LastDayOfMonth table.
期间将结果集限制为具有 activity 的用户仍然具有相同的效果
下面的原始答案
尝试如下操作:
DECLARE @star_UserAccounts table (
[UserId] nvarchar (20) COLLATE Latin1_General_CI_AS NOT NULL,
[InsertedDate] datetime NULL,
[ValidToDate] datetime NULL,
[RowVersion] int NOT NULL
);
INSERT INTO @star_UserAccounts ( [UserId], [InsertedDate], [ValidToDate], [RowVersion] )
VALUES
('JoeBloggs', '2020-01-09 11:08:28.970', '2020-01-16 11:08:24.547', 4 ),
('JoeBloggs', '2020-01-09 10:08:28.013', '2020-01-09 11:08:28.813', 3 ),
('JoeBloggs', '2019-10-31 16:08:28.027', '2020-01-09 10:08:27.840', 2 ),
('JoeBloggs', '2019-07-19 13:43:09.083', '2019-10-31 16:08:27.633', 1 );
DECLARE @LastDayOFMonth table (
LastDayOfMonth date
);
INSERT INTO @LastDayOFMonth ([LastDayOfMonth])
VALUES
('2019-07-31 00:00:00' ),
('2019-08-31 00:00:00' ),
('2019-09-30 00:00:00' ),
('2019-10-31 00:00:00' ),
('2019-11-30 00:00:00' ),
('2019-12-31 00:00:00' );
;WITH current_activity AS (
SELECT
UserID, MAX( RowVersion ) AS CurrentVersion
FROM @star_UserAccounts
GROUP BY
UserID
)
SELECT
ua.*
FROM current_activity
INNER JOIN @star_UserAccounts AS ua
ON current_activity.UserID = ua.UserID
AND current_activity.CurrentVersion = ua.[RowVersion]
WHERE EXISTS (
SELECT * FROM @star_UserAccounts AS u
CROSS APPLY (
SELECT * FROM @LastDayOfMonth AS ld
WHERE CAST( ld.LastDayOfMonth AS date ) BETWEEN CAST( u.InsertedDate AS date ) AND CAST( u.ValidToDate AS date )
) AS d
WHERE
u.UserId = current_activity.UserId
)
ORDER BY
current_activity.UserId;
RETURNS
+-----------+-------------------------+-------------------------+------------+
| UserId | InsertedDate | ValidToDate | RowVersion |
+-----------+-------------------------+-------------------------+------------+
| JoeBloggs | 2020-01-09 11:08:28.970 | 2020-01-16 11:08:24.547 | 4 |
+-----------+-------------------------+-------------------------+------------+
CTE 获取最新的 UserId 的 RowVersion,然后查看用户是否有任何 activity在 LastDayOfMonth.
中列出的日期期间
使用 CROSS APPLY
将结果限制为具有包含 LastDayOfMonth 的任何 activity 的用户。
你把它复杂化了。您可以使用简单的行编号来做到这一点
SELECT
LastDayOfMonth,
COUNT(*) ActiveAtDate
FROM (
SELECT
ld.LastDayOfMonth,
ua.UserId,
rn = ROW_NUMBER() OVER (PARTITION BY ld.LastDayOfMonth, ua.UserId ORDER BY ua.ValidToDate DESC)
FROM dbo.star_UserAccounts ua
INNER JOIN LastDayOfMonth ld
ON ld.LastDayOfMonth >= ua.InsertedDate
AND DATEADD(day, 1, ld.LastDayOfMonth) < ua.ValidToDate
WHERE ua.UserId = 'JoeBloggs'
) latestRowForDate
WHERE rn = 1
GROUP BY LastDayOfMonth;
我为这件事焦头烂额,不是 100% 确定这是可能的,但在 SQL 中一切皆有可能所以它一定是?!
目的是能够历史地跟踪每个月底有多少活跃帐户。
我有两个table:
star_UserAccounts
- 每小时保存一次帐户信息。
- 如果数据相同,则不会发生任何事情。
- 如果数据发生变化,现有行将被标记为旧行并插入新行。这使一个很好的小历史可以看到正在发生的变化。
最后一天
- 保存我想要数据的日期(仅作为示例)
在“star_UserAccounts”table 我有(升序):
UserId | UserStatus | InsertedDate | ValidToDate | Row Version |
---|---|---|---|---|
JoeBloggs | Active | 2019-07-19 13:43:09.083 | 2019-10-31 16:08:27.633 | 1 |
JoeBloggs | Active | 2019-10-31 16:08:28.027 | 2020-01-09 10:08:27.840 | 2 |
JoeBloggs | Active | 2020-01-09 10:08:28.013 | 2020-01-09 11:08:28.813 | 3 |
JoeBloggs | Active | 2020-01-09 11:08:28.970 | 2020-01-16 11:08:24.547 | 4 |
现在 - 我想 return 2019 年 10 月 31 日有效的详细信息。那天有两次更新,因此那天的最新 RowVersion 应该是 returned .
当我将日期硬编码到我的 select 脚本中时,我可以获得帐户的单独行版本,但是当我尝试一次性将其应用于多个不同的日期时,它同时获取了两行2019 年 10 月 31 日,但我只希望它在需要的日期获取最新的行。
LastDayOfMonth | ActiveAtDate |
---|---|
2019-07-31 | 1 |
2019-08-31 | 1 |
2019-09-30 | 1 |
2019-10-31 | 2 <-- The problem row |
2019-11-30 | 1 |
2019-12-31 | 1 |
我怎么才能确保为每个推入的日期提取最新的行?
这里有一个 SQL Fiddle 可以玩:http://sqlfiddle.com/#!18/ed372/12
我保留了子查询中的语法错误,因为这种显示了我想要实现的目标,因为我需要将日期传递到收集该日期的 MAX 记录的子查询中。
感谢任何帮助。很难用简单的方式解释,如果很混乱,请见谅。
谢谢。
更新
重新阅读您的 post 后,我意识到在回答之前我没有完全理解您的问题。我会留下我原来的答案,因为它可能会有所帮助。
您的情况可能与使用 COUNT DISTINCT
一样简单,例如:
SELECT
LastDayOfMonth,
COUNT ( DISTINCT UserId ) AS ActiveAtDate
FROM @star_UserAccounts AS ua
CROSS APPLY (
SELECT LastDayOfMonth FROM @LastDayOfMonth AS ld
WHERE ld.LastDayOfMonth BETWEEN CAST( ua.InsertedDate AS date ) AND CAST( ua.ValidToDate AS date )
) AS x
GROUP BY
LastDayOfMonth
ORDER BY
LastDayOfMonth;
RETURNS
+----------------+--------------+
| LastDayOfMonth | ActiveAtDate |
+----------------+--------------+
| 2019-07-31 | 1 |
| 2019-08-31 | 1 |
| 2019-09-30 | 1 |
| 2019-10-31 | 1 |
| 2019-11-30 | 1 |
| 2019-12-31 | 1 |
+----------------+--------------+
使用 CROSS APPLY
在 LastDayOfMonth table.
下面的原始答案
尝试如下操作:
DECLARE @star_UserAccounts table (
[UserId] nvarchar (20) COLLATE Latin1_General_CI_AS NOT NULL,
[InsertedDate] datetime NULL,
[ValidToDate] datetime NULL,
[RowVersion] int NOT NULL
);
INSERT INTO @star_UserAccounts ( [UserId], [InsertedDate], [ValidToDate], [RowVersion] )
VALUES
('JoeBloggs', '2020-01-09 11:08:28.970', '2020-01-16 11:08:24.547', 4 ),
('JoeBloggs', '2020-01-09 10:08:28.013', '2020-01-09 11:08:28.813', 3 ),
('JoeBloggs', '2019-10-31 16:08:28.027', '2020-01-09 10:08:27.840', 2 ),
('JoeBloggs', '2019-07-19 13:43:09.083', '2019-10-31 16:08:27.633', 1 );
DECLARE @LastDayOFMonth table (
LastDayOfMonth date
);
INSERT INTO @LastDayOFMonth ([LastDayOfMonth])
VALUES
('2019-07-31 00:00:00' ),
('2019-08-31 00:00:00' ),
('2019-09-30 00:00:00' ),
('2019-10-31 00:00:00' ),
('2019-11-30 00:00:00' ),
('2019-12-31 00:00:00' );
;WITH current_activity AS (
SELECT
UserID, MAX( RowVersion ) AS CurrentVersion
FROM @star_UserAccounts
GROUP BY
UserID
)
SELECT
ua.*
FROM current_activity
INNER JOIN @star_UserAccounts AS ua
ON current_activity.UserID = ua.UserID
AND current_activity.CurrentVersion = ua.[RowVersion]
WHERE EXISTS (
SELECT * FROM @star_UserAccounts AS u
CROSS APPLY (
SELECT * FROM @LastDayOfMonth AS ld
WHERE CAST( ld.LastDayOfMonth AS date ) BETWEEN CAST( u.InsertedDate AS date ) AND CAST( u.ValidToDate AS date )
) AS d
WHERE
u.UserId = current_activity.UserId
)
ORDER BY
current_activity.UserId;
RETURNS
+-----------+-------------------------+-------------------------+------------+
| UserId | InsertedDate | ValidToDate | RowVersion |
+-----------+-------------------------+-------------------------+------------+
| JoeBloggs | 2020-01-09 11:08:28.970 | 2020-01-16 11:08:24.547 | 4 |
+-----------+-------------------------+-------------------------+------------+
CTE 获取最新的 UserId 的 RowVersion,然后查看用户是否有任何 activity在 LastDayOfMonth.
中列出的日期期间使用 CROSS APPLY
将结果限制为具有包含 LastDayOfMonth 的任何 activity 的用户。
你把它复杂化了。您可以使用简单的行编号来做到这一点
SELECT
LastDayOfMonth,
COUNT(*) ActiveAtDate
FROM (
SELECT
ld.LastDayOfMonth,
ua.UserId,
rn = ROW_NUMBER() OVER (PARTITION BY ld.LastDayOfMonth, ua.UserId ORDER BY ua.ValidToDate DESC)
FROM dbo.star_UserAccounts ua
INNER JOIN LastDayOfMonth ld
ON ld.LastDayOfMonth >= ua.InsertedDate
AND DATEADD(day, 1, ld.LastDayOfMonth) < ua.ValidToDate
WHERE ua.UserId = 'JoeBloggs'
) latestRowForDate
WHERE rn = 1
GROUP BY LastDayOfMonth;