我有一个需要在其中应用分组的结果集。但是,分组不仅是简单的group by子句(至少我可以弄清楚)。
我们的某些动物(我在一个非常大的动物庇护所里工作)需要兽医检查它们。有人会在动物的记录上输入“ VET CHECK”。历史记录被存储,因此当我目视查看历史记录时,我可以确定何时将动物首次放置在VET CHECK上以及何时将其移除。我遇到的问题是我需要在代码中确定这一点,似乎无法提出解决方案。下面的代码将创建表格,样本数据很好地表示了需要看兽医的大多数动物的样子。
创建表并插入数据后,选择*并按图章描述排序。您会看到在1/15的7:18 AM输入了VET CHECK(在result_request字段中查看)。如果我在上午7:18之前运行报告,则根本不应该显示该动物处于VET CHECK状态(请注意上一条记录中的NULL)。上午7:18之后(直到下午2:07的1/16为止),它应该显示动物处于VET CHECK状态。如果在动物停留期间仅进行一次兽医检查,我可以简单地使用MIN(stamp)来获取动物进入状态的日期。但是,您会注意到这只动物的状态有几次不同。
再往下看结果集,您将其置于下午12:07的1/10状态,并于下午4:02从1/14的状态中删除。如果我在下午12:07的1/10之前运行报告,则该动物不应显示状态。在12:07 pm的1/10和4:02 pm的1/14之间的任何时间运行报告,动物应显示状态,而状态的第一个日期应显示为1/10。同样,由于存在多个VET CHECK状态,因此我不能只使用MIN(stamp)。
CREATE TABLE kennel_history([kh_identity] [int] NOT NULL,
[kennel_identity] [int] NOT NULL,
[stamp] [datetime] NULL,
[userid] [varchar](8) NULL,
[impound_no] [varchar](10) NOT NULL,
[kennel_no] [varchar](10) NULL,
[kennel_stat] [varchar](10) NULL,
[hold_notify] [varchar](1) NULL,
[outcome_request] [varchar](10) NULL
);
INSERT INTO kennel_history
VALUES (9471697,881929,'2019-01-17 08:05:41','CHEITMAN','K18-847522','QCB','UNAVAILABL',NULL,NULL),
(9471254,881929,'2019-01-16 14:07:18','THUTCHIN','K18-847522','QCE','UNAVAILABL',NULL,NULL),
(9469550,881929,'2019-01-15 07:18:36','BBUSCEMI','K18-847522','QCE','UNAVAILABL','Y','VET CHECK'),
(9469390,881929,'2019-01-14 16:56:02','LRAYNER','K18-847522','QCE','UNAVAILABL',NULL,NULL),
(9469302,881929,'2019-01-14 16:02:41','SHUNT','K18-847522','QRL03','UNAVAILABL',NULL,NULL),
(9467613,881929,'2019-01-13 08:30:46','DEADS','K18-847522','QRL03','UNAVAILABL','Y','VET CHECK'),
(9465923,881929,'2019-01-11 10:16:52','DEADS','K18-847522','QRL06','UNAVAILABL','Y','VET CHECK'),
(9465225,881929,'2019-01-10 12:07:17','KMORRIS','K18-847522','QRL03','UNAVAILABL','Y','VET CHECK'),
(9465224,881929,'2019-01-10 12:07:07','KMORRIS','K18-847522','QRL03','UNAVAILABL','Y','VET CHECK'),
(9463051,881929,'2019-01-08 06:43:19','CSILVEY','K18-847522','QRL03','AVAILABLE',NULL,NULL),
(9461197,881929,'2019-01-06 09:24:07','APENAZUR','K18-847522','QRL08','AVAILABLE',NULL,NULL),
(9460067,881929,'2019-01-05 08:57:46','APENAZUR','K18-847522','QRL07','AVAILABLE',NULL,NULL),
(9459250,881929,'2019-01-04 10:13:45','DEADS','K18-847522','QRL01','AVAILABLE',NULL,NULL),
(9458551,881929,'2019-01-03 12:30:42','ACLARK','K18-847522','QRL08','AVAILABLE',NULL,NULL),
(9458499,881929,'2019-01-03 11:51:48','AGARFIAS','K18-847522','DHC04','AVAILABLE',NULL,NULL),
(9458484,881929,'2019-01-03 11:48:32','AGARFIAS','K18-847522','DHC04','AVAILABLE',NULL,NULL),
(9454810,881929,'2018-12-29 12:20:01','ACLARK','K18-847522','DHC04','UNAVAILABL',NULL,NULL),
(9454683,881929,'2018-12-29 11:08:39','AGARFIAS','K18-847522','SXA24','UNAVAILABL',NULL,NULL),
(9454680,881929,'2018-12-29 11:06:32','AGARFIAS','K18-847522','SXA24','UNAVAILABL',NULL,NULL),
(9454511,881929,'2018-12-29 09:13:22','BBUSCEMI','K18-847522','SXA24','UNAVAILABL',NULL,NULL),
(9453649,881929,'2018-12-28 08:46:12','TSIMONS','K18-847522','SXA24','UNAVAILABL','Y','VET TECH'),
(9453648,881929,'2018-12-28 08:46:07','TSIMONS','K18-847522','SXA24','UNAVAILABL','Y','VET CHECK'),
(9453624,881929,'2018-12-28 08:03:19','BBUSCEMI','K18-847522','SXA24','UNAVAILABL','Y','VET TECH'),
(9453533,881929,'2018-12-27 17:45:22','DEADS','K18-847522','DO03','UNAVAILABL','Y','VET TECH'),
(9453405,881929,'2018-12-27 15:28:02','DEADS','K18-847522','DO02','UNAVAILABL','Y','VET TECH'),
(9452597,881929,'2018-12-26 15:27:48','SSUTTON','K18-847522','DO02','UNAVAILABL',NULL,NULL),
(9452426,881929,'2018-12-26 13:05:26','THUTCHIN','K18-847522','DO02','UNAVAILABL',NULL,NULL),
(9452121,881929,'2018-12-26 10:18:55','THUTCHIN','K18-847522','SXA04','UNAVAILABL',NULL,NULL),
(9451959,881929,'2018-12-26 08:09:21','BBUSCEMI','K18-847522','SXA04','UNAVAILABL',NULL,NULL),
(9451886,881929,'2018-12-25 14:12:49','SBUCKMAN','K18-847522','DO02','UNAVAILABL',NULL,NULL),
(9451884,881929,'2018-12-25 14:11:58','SBUCKMAN','K18-847522','DO02','UNAVAILABL',NULL,NULL),
(9451870,881929,'2018-12-25 13:28:15','SBUCKMAN','K18-847522','DO02','UNAVAILABL',NULL,NULL),
(9450863,881929,'2018-12-23 17:24:17','BLAEHLE','K18-847522','DO02','UNAVAILABL',NULL,NULL),
(9449482,881929,'2018-12-22 13:12:34','V-KTAYLO','K18-847522','DO02','UNAVAILABL','Y','VET CHECK'),
(9448808,881929,'2018-12-21 16:23:03','SBUCKMAN','K18-847522','DO02','UNAVAILABL','Y','VET CHECK'),
(9448111,881929,'2018-12-21 09:10:49','CHEITMAN','K18-847522','DO02','UNAVAILABL',NULL,NULL),
(9448069,881929,'2018-12-21 08:36:47','BMERMAN','K18-847522','DO07','UNAVAILABL',NULL,NULL),
(9447864,881929,'2018-12-20 17:32:53','DEADS','K18-847522','DO07','UNAVAILABL','Y','VET TECH'),
(9446090,881929,'2018-12-19 09:54:33','MGELTZ','K18-847522','DO07','UNAVAILABL',NULL,NULL),
(9445884,881929,'2018-12-19 07:25:51','ZKNOX','K18-847522','DO07','UNAVAILABL','Y','PRIORITY 1'),
(9444928,881929,'2018-12-18 08:22:30','EKNEPPER','K18-847522','DO07','UNAVAILABL','Y','PRIORITY 1'),
(9438860,881929,'2018-12-12 09:15:33','CMCCANN','K18-847522','DO07','UNAVAILABL',NULL,NULL),
(9438820,881929,'2018-12-12 08:21:33','JAUSEC','K18-847522','DO07','UNAVAILABL',NULL,NULL);
以某种方式,我必须考虑到result_request为NULL的状态两侧的记录,因此我知道状态何时开始和停止。这个让我难过。
在解决这个问题上的任何帮助将不胜感激。
答案 0 :(得分:1)
没有理想的结果集,这实际上只是SWAG ...
我设置了一个带有日期说明的函数,该函数返回截至提供日期为止与当前状态有关的所有信息,以及 userid 和 stamp 与该特定状态跨度开始的时间相关的值。
所以'2019-01-10 12:06:00.000'的结果看起来像这样:
...并且'2019-01-12 15:00:00.000'的结果看起来像这样:
您可以在这里进行测试:https://rextester.com/XATW78525 在测试中,我为另外两个动物添加了一些信息-在 stamp , userid 和 outcome_request 的某些值之间切换。该功能应在当时为每只动物提供状态记录。
我采用的方法是使用CTE构建初始结果集,该结果集使用LAG来存储动物的先前result_request。有了这些信息后,我就使用CTE加入了两个子查询(一个用于获取最新状态,另一个用于标识状态跨度何时开始的初始实例)以获取每只动物的结果。可以将逻辑从函数中拉出并显式运行,但是以这种方式测试一系列日期更加容易。可能可以用更多的咖啡来清理...
CREATE FUNCTION StatusAtTime (@RequestedTime DATETIME)
RETURNS TABLE
AS
RETURN
(
WITH statusBuilder AS
(
SELECT
kh1.*,
lag(kh2.[outcome_request]) OVER (PARTITION BY kh2.[kennel_identity] ORDER BY kh2.[stamp]) AS [lagged_outcome_request]
FROM dbo.kennel_history kh1
JOIN dbo.kennel_history kh2
ON kh2.[stamp] = kh1.[stamp]
AND kh2.[kennel_identity] = kh1.[kennel_identity]
WHERE kh1.[stamp] <= @RequestedTime
)
SELECT
SB1.[kh_identity]
,SB1.[kennel_identity]
,SB1.[stamp]
,SB1.[userid]
,SB1.[impound_no]
,SB1.[kennel_no]
,SB1.[kennel_stat]
,SB1.[hold_notify]
,SB1.[outcome_request]
,SB2.[userid] AS [initial_outcome_request_userid]
,SB2.[stamp] AS [initial_outcome_request_stamp]
FROM
(
SELECT TOP 1 WITH ties *
FROM statusBuilder
ORDER BY ROW_NUMBER() OVER (PARTITION BY statusBuilder.kennel_identity ORDER BY statusBuilder.stamp desc)
) SB1
JOIN
(
SELECT TOP 1 WITH ties *
FROM statusBuilder
WHERE ISNULL([outcome_request],'NullOutcome') <> ISNULL([lagged_outcome_request],'NullOutcome')
ORDER BY ROW_NUMBER() OVER (PARTITION BY statusBuilder.kennel_identity ORDER BY statusBuilder.stamp desc)
)SB2
ON SB1.kennel_identity = SB2.kennel_identity
)
答案 1 :(得分:1)
我想分享自己的终极解决方案。使用上面从IsItGreyOrGray发布的答案(感谢您激发创意的力量,而您的答案就是让我选择此路径的原因)与另一个论坛的反馈以及我从供应商那里得到的反馈相结合,我的最终解决方案是创建一个函数来得到所需的日期:
CREATE FUNCTION StartDateOnOutcomeRequest (@KennelIdentity INT,
@OutcomeRequest VARCHAR(10),
@DBStamp DATETIME)
RETURNS DATE
AS
BEGIN
/* This section for testing. Leave commented out for normal execution.
DECLARE @KennelIdentity INT = 881929;
DECLARE @OutcomeRequest VARCHAR(10) = 'ADOPTIONS';
DECLARE @DBStamp DATETIME = '2019-01-21 12:56:17.457';
*/
DECLARE @CurrentRowNum INT;
DECLARE @StartRowNum INT;
DECLARE @StartDate DATETIME;
DECLARE @RowNumTmp TABLE (RowNumber INT IDENTITY(1, 1),
KHIdentity INT,
OutcomeRequest VARCHAR(10),
Stamp DATETIME);
-- Load kennel history records into temp table with seqential row number.
INSERT INTO @RowNumTmp (KHIdentity,
OutcomeRequest,
Stamp)
SELECT kh_identity,
ISNULL(outcome_request, 'None'),
stamp
FROM SYSADM.kennel_history
WHERE (kennel_identity = @KennelIdentity)
ORDER BY kh_identity;
-- Identify the history record matching the database timestamp and outcome request type. Store that record's row number in a variable.
SELECT @CurrentRowNum = RowNumber
FROM @RowNumTmp
WHERE (Stamp = @DBStamp)
AND (OutcomeRequest = @OutcomeRequest);
-- Identify the first row number on the current outcome request by looking at lesser rows where the outcome request does not match.
SELECT @StartRowNum = MAX(RowNumber) + 1
FROM @RowNumTmp
WHERE (RowNumber <= @CurrentRowNum)
AND (OutcomeRequest <> @OutcomeRequest);
-- Finally, get the date using the starting row number for the outcome request and return it below.
SELECT @StartDate = Stamp
FROM @RowNumTmp
WHERE (RowNumber = @StartRowNum);
RETURN(@StartDate);
END;
用法:
SELECT k.KENNEL_NO AS KennelNumber,
ISNULL(k.KENNEL_STAT, '') AS KennelStatus,
ISNULL(k.kennel_substat, '') AS KennelSubstatus,
a.ANIMAL_ID AS AnimalId,
a.ANIMAL_TYPE AS AnimalType,
a.age_long AS AgeLong,
CASE
WHEN a.SECONDARY_BREED IS NULL THEN a.PRIMARY_BREED
ELSE a.PRIMARY_BREED + ' / ' + a.SECONDARY_BREED
END + ' / ' +
CASE
WHEN a.SECONDARY_COLOR IS NULL THEN a.PRIMARY_COLOR
ELSE a.PRIMARY_COLOR + ' / ' + a.SECONDARY_COLOR
END AS BreedColor,
dbo.StartDateOnOutcomeRequest(k.kennel_identity, k.OUTCOME_REQUEST, k.STAMP) AS FirstHoldDate,
DATEDIFF(DD, dbo.StartDateOnOutcomeRequest(k.kennel_identity, k.OUTCOME_REQUEST, k.STAMP), GETDATE()) AS DaysOnHold,
k.extra3 AS [Level],
ISNULL(k.OUTCOME_TYPE, '') AS OutcomeType
FROM SYSADM.KENNEL AS k
INNER JOIN SYSADM.ANIMAL AS a ON k.ANIMAL_ID = a.ANIMAL_ID