3

如果我有一个客户在 30 天内多次回复同一个调查,我只想算一次。有人可以给我看代码吗?

创建表#Something
(
         客户 ID Char(10),
    SurveyId char(5),
    响应日期日期时间
)

插入#某事
选择“Cust1”、“100”、“5/6/13”联合所有
选择“Cust1”、“100”、“5/13/13”联合所有
选择“Cust2”、“100”、“13 年 4 月 20 日”联合所有
选择“Cust2”、“100”、“13 年 5 月 22 日”


从#Something 中选择不同的 custid、SurveyId、Count(custid) 作为 CountResponse
按客户 ID、SurveyId 分组

上面的代码只给了我响应的总数,不知道如何编码每 30 天只计算一次。

我正在寻找的输出应该是这样的:

CustomerID SurveyId CountResponse
客户1 100 1
客户 2 100 2

4

6 回答 6

0

下面的代码是产生示例输出的一种方法。但是,如果您添加select 'Cust1', '100', '4/20/13',结果仍然是Cust1 100 1因为它们都在每个先前调查响应的 30 天内,因此只会计算第一个。这是期望的行为吗?

SELECT     CustID, SurveyID, COUNT(*) AS CountResponse
FROM         #SurveysTaken
WHERE     (NOT EXISTS
                      (SELECT     1
                        FROM          #SurveysTaken AS PriorSurveys
                        WHERE      (CustID = #SurveysTaken.CustID)
                               AND (SurveyId = #SurveysTaken.SurveyId)
                               AND (ResponseDate >= DATEADD(d, - 30, #SurveysTaken.ResponseDate))
                               AND (ResponseDate < #SurveysTaken.ResponseDate)))
GROUP BY CustID, SurveyID

或者,您可以将一年分成任意 30 天的时间段,并在每个新年重新设置。

SELECT     CustID, SurveyID, COUNT(*) AS CountResponse
FROM         (SELECT DISTINCT CustID, SurveyID, YEAR(ResponseDate) AS RepsonseYear,
                              DATEPART(DAYOFYEAR, ResponseDate) / 30 AS ThirtyDayPeriod
              FROM          #SurveysTaken) AS SurveysByPeriod
GROUP BY CustID, SurveyID

你也可以按月去。

SELECT     CustID, SurveyID, COUNT(*) AS CountResponse
FROM         (SELECT DISTINCT CustID, SurveyID, YEAR(ResponseDate) AS ResponseYear,
                              MONTH(ResponseDate) AS ResponseMonth
              FROM          #SurveysTaken) AS SurveysByMonth
GROUP BY CustID, SurveyID

您可以使用任意纪元日期的 30 天时间段。(也许通过从另一个查询中提取首次创建调查的日期?)

SELECT     CustID, SurveyID, COUNT(*) AS CountResponse
FROM         (SELECT DISTINCT CustID, SurveyID, DATEDIFF(D, '1/1/2013', ResponseDate) / 30 AS ThirtyDayPeriod
                       FROM          #SurveysTaken) AS SurveysByPeriod
GROUP BY CustID, SurveyID

任意三十个时期的最后一个变体是基于客户第一次对相关调查做出回应。

SELECT     CustID, SurveyID, COUNT(*) AS CountResponse
FROM         (SELECT DISTINCT CustID, SurveyID, DATEDIFF(DAY,
                                                  (SELECT     MIN(ResponseDate)
                                                    FROM          #SurveysTaken AS FirstSurvey
                                                    WHERE      (CustID = #SurveysTaken.CustID)
                                                           AND (SurveyId = #SurveysTaken.SurveyId)), ResponseDate) / 30 AS ThirtyDayPeriod
                       FROM          #SurveysTaken) AS SurveysByPeriod
GROUP BY CustID, SurveyID

您在使用 epoch/period 技巧时会遇到一个问题,即计数的调查每个周期仅发生一次,但不一定相隔 30 天。

于 2013-07-10T20:53:27.637 回答
0

这是我相信的一种处理方法。我快速测试了,它适用于小样本记录,所以我希望它能帮助你。祝你好运。

SELECT s.CustID, COUNT(s.SurveyID) AS SurveyCount
FROM #something s
INNER JOIN (SELECT CustID, SurveyId, ResponseDate
            FROM (SELECT #Something.*,
            ROW_NUMBER() OVER (PARTITION BY custid ORDER BY ResponseDate ASC) AS RN
            FROM #something) AS t
            WHERE RN = 1 ) f ON s.CustID = f.CustID
WHERE s.ResponseDate BETWEEN f.ResponseDate AND f.ResponseDate+30
GROUP BY s.CustID
HAVING COUNT(s.SurveyID) > 1
于 2013-07-10T21:02:42.213 回答
0

我不是 SQL Server 的人,但在 Oacle 中,如果你从“日期”中减去整数值,你实际上是在减去“天”,所以这样的事情可以工作:

SELECT custid, surveyid
FROM Something a
WHERE NOT EXISTS (
    SELECT 1
    FROM Something b
    WHERE a.custid = b.custid
    AND a.surveyid = b.surveyid
    AND b.responseDate between a.responseDate AND a.responseDate - 30
    );    

要获得您的计数(如果我不理解您的要求):

-- Count of times custID returned surveyID, not counting same
-- survey within 30 day period.
SELECT custid, surveyid, count(*) countResponse
FROM Something a
WHERE NOT EXISTS (
    SELECT 1
    FROM Something b
    WHERE a.custid = b.custid
    AND a.surveyid = b.surveyid
    AND b.responseDate between a.responseDate AND a.responseDate - 30
    )
GROUP BY custid, surveyid

更新:根据下面提出的案例,这实际上是行不通的。您可能应该做的是遍历您的something表格并将您想要保留在results表格中的调查插入行,然后与results表格进行比较以查看在过去 30 天内是否已经收到您想要考虑的调查。我可以向您展示如何在 oracle PL/SQL 中执行此类操作,但我不知道 SQL Server 的语法。也许知道 sql server 的其他人想窃取此策略来为您编写答案,或者这足以让您继续。

于 2013-07-10T20:30:22.857 回答
0

称我为疯狂和疯狂,但我会通过在每次调查中存储更多状态来解决这个问题。我将采取的方法是添加一个bit类型列,指示是否应计算特定调查(即Countable列)。这解决了解决此相关问题中固有的状态跟踪问题。

我会在插入时将值设置为 1,如果在前 30 天内找不到Countable具有相同CustID/的调查并设置为 1。否则,我会将其设置为 0。SurveyIdCountable

然后问题变得很容易解决。只需按CustID/分组SurveyId并总结Countable列中的值。

这种方法的一个警告是,它要求调查必须按时间顺序添加,并且在不重新计算值的情况下不能删除Countable

于 2013-07-10T20:34:49.317 回答
0

你的问题模棱两可,这可能是你困难的根源。

insert #Something values
('Cust3', '100', '1/1/13'),
('Cust3', '100', '1/20/13'),
('Cust3', '100', '2/10/13')

Cust3 的计数应该是 1 还是 2?'2/10/13' 响应是否无效,因为它在 '1/20/13' 响应之后不到 30 天?或者“2/10/13”响应是否有效,因为“1/20/13”被“1/1/13”响应无效,因此在上一个有效响应之后超过 30 天?

于 2013-07-10T21:24:13.420 回答
0

根据您希望从第一次提交调查开始计算您的周期为 30 天的理论,这是一个(总)解决方案。

declare @Something table
(
    CustID Char(10),
    SurveyId char(5),
    ResponseDate datetime
)

insert @Something
select 'Cust1', '100', '5/6/13' union all
select 'Cust1', '100', '5/13/13' union all
select 'Cust1', '100', '7/13/13' union all
select 'Cust2', '100', '4/20/13' union all
select 'Cust2', '100', '5/22/13' union all
select 'Cust2', '100', '7/20/13' union all
select 'Cust2', '100', '7/24/13' union all
select 'Cust2', '100', '9/28/13' 

--SELECT CustID,SurveyId,COUNT(*) FROM (

select a.CustID,a.SurveyId,b.ResponseStart,--CONVERT(int,a.ResponseDate-b.ResponseStart),
CASE 
    WHEN CONVERT(int,a.ResponseDate-b.ResponseStart) > 30 
    THEN ((CONVERT(int,a.ResponseDate-b.ResponseStart))-(CONVERT(int,a.ResponseDate-b.ResponseStart) % 30))/30+1
    ELSE 1
END CustomPeriod -- defines periods 30 days out from first entry of survey
from @Something a
inner join
(select CustID,SurveyId,MIN(ResponseDate) ResponseStart
from @Something
group by CustID,SurveyId) b
on a.SurveyId=b.SurveyId
and a.CustID=b.CustID
group by a.CustID,a.SurveyId,b.ResponseStart,
CASE 
    WHEN CONVERT(int,a.ResponseDate-b.ResponseStart) > 30 
    THEN ((CONVERT(int,a.ResponseDate-b.ResponseStart))-(CONVERT(int,a.ResponseDate-b.ResponseStart) % 30))/30+1
    ELSE 1
END

--) x GROUP BY CustID,SurveyId

至少您可能希望使 CASE 语句成为一个函数,以便它读起来更清晰。最好在单独的表中定义显式窗口。如果您想避免在第一个周期结束时返回调查,几天后在第二个周期返回另一个调查,这可能不可行。

如果可能,您应该考虑在输入时处理此问题。例如,如果您要在在线调查中识别客户,请拒绝填写调查的尝试。或者,如果有人邮寄这些,如果有人在 30 天内收到,让数据输入人员拒绝它。

或者,与“wild and crazy”一样,添加一点点和一个 INSERT 触发器。如果在该时间段内没有为该客户找到该类型的调查,则仅打开该位。

总的来说,更完整地表述这个问题会有所帮助。但是,我确实很欣赏实际的编码示例。

于 2013-07-10T21:38:39.530 回答