我们假设我们有一个维护表
Customer LastLogin ActionType
1 12/1/2007 2
1 12/2/2007 2
etc.
我们希望列出所有客户,这些客户在某一年内的任何时间点都有一个或多个不间断的序列,长达14天,以行动类型2登录。
我当然可以使用代码轻松完成此操作,甚至可以在小集合上快速完成。是否有一种非游标方式在SQL中执行它?
答案 0 :(得分:5)
这将选择至少两个相同类型的连续操作的所有客户。
WITH rows AS
(
SELECT customer, action,
ROW_NUMBER() OVER (PARTITION BY customer ORDER BY lastlogin) AS rn
FROM mytable
)
SELECT DISTINCT customer
FROM rows rp
WHERE EXISTS
(
SELECT NULL
FROM rows rl
WHERE rl.customer = rp.customer
AND rl.rn = rp.rn + 1
AND rl.action = rp.action
)
以下是仅针对操作2
的更有效查询:
WITH rows AS
(
SELECT customer, ROW_NUMBER() OVER (PARTITION BY customer ORDER BY lastlogin) AS rn
FROM mytable
WHERE action = 2
)
SELECT DISTINCT customer
FROM rows rp
WHERE EXISTS
(
SELECT NULL
FROM rows rl
WHERE rl.customer = rp.customer
AND rl.rn = rp.rn + 1
)
更新2:
选择不间断的范围:
WITH rows AS
(
SELECT customer, action, lastlogin
ROW_NUMBER() OVER (PARTITION BY customer ORDER BY lastlogin) AS rn
ROW_NUMBER() OVER (PARTITION BY customer, action ORDER BY lastlogin) AS series
FROM mytable
)
SELECT DISTINCT customer
FROM (
SELECT customer
FROM rows rp
WHERE action
GROUP BY
customer, actioncode, series - rn
HAVING
DETEDIFF(day, MIN(lastlogin), MAX(lastlogin)) >= 14
) q
此查询计算两个系列:一个返回连续ORDER BY lastlogin
,第二个系列另外按action
分区:
action logindate rn series diff = rn - series
1 Jan 01 1 1 0
1 Jan 02 2 2 0
2 Jan 03 3 1 2
2 Jan 04 4 2 2
1 Jan 05 5 3 2
1 Jan 06 6 4 2
只要两种方案之间的差异相同,该系列就不会中断。每次中断都会打破这个系列。
这意味着(action, diff
)的组合定义了不间断的组。
我们可以按action, diff
分组,在群组中找到MAX
和MIN
并对其进行过滤。
如果您需要选择14
行而不是14
个连续日,只需过滤COUNT(*)
而不是DATEDIFF
。
答案 1 :(得分:1)
编辑:这对原来的问题大约有两个问题。 14连续是一个不同的答案
首先你需要一个序列,所以你将使用ROWNUMBER
你可以使用ROWNUMBER = ROWNUMBER + 1
为自己做一个自我加入的维护具有相同客户ID的任何两个具有连续性的行,以及具有“2”ActionType的两行将为您提供CUSTOMER列表作为您的答案。
试试这个
WITH Maintenance AS
(
SELECT 1 as Customer, CONVERT (DateTime, '1/1/2008') DateTimeStamp, 1 ActionType
UNION
SELECT 1, '3/1/2009', 1
UNION
SELECT 1, '3/1/2006', 2
UNION
SELECT 2, '3/1/2009', 1
UNION
SELECT 2, '3/1/2006', 2
)
,RowNumberMaintenance AS
(SELECT ROW_NUMBER () OVER (ORDER BY Customer, DateTimeStamp) AS RowNumber, *
FROM Maintenance)
SELECT m1.Customer
From RowNumberMaintenance M1
INNER JOIN RowNumberMaintenance M2
ON M1.Customer = M2.Customer
AND M1.RowNumber = M2.RowNumber + 1
WHERE 1=1
AND M1.ActionType <> 2
AND M2.ActionType <> 2
答案 2 :(得分:1)
使用:
WITH dates AS (
SELECT CAST('2007-01-01' AS DATETIME) 'date'
UNION ALL
SELECT DATEADD(dd, 1, t.date)
FROM dates t
WHERE DATEADD(dd, 1, t.date) <= GETDATE())
SELECT m.customer,
m.actiontype
FROM dates d
LEFT JOIN MAINTENANCE m ON m.last_login = d.date
WHERE m.last_login IS NULL
答案 3 :(得分:0)
select customerID, count(customerID)
from maintenance
where actiontype = 2
group by customerID
having count(customerID) >= 1
答案 4 :(得分:0)
我将假设一个序列表示两行或更多行具有连续的日期时间值,而对于具有不同操作类型的同一用户,其间没有其他行。既然如此,这应该会给你你想要的东西:
SELECT DISTINCT
T1.customer
FROM
Maintenance T1
INNER JOIN Maintenance T2 ON
T2.customer = T1.customer AND
T2.action_type = 2 AND
T2.last_login > T1.last_login
LEFT OUTER JOIN Maintenance T3 ON
T3.customer = T1.customer AND
T3.last_login > T1.last_login AND
T3.last_login < T2.last_login AND
T3.action_type <> 2
WHERE
T1.actiontype = 2 AND
T3.customer IS NULL
SQL就是我上面所说的 - 在它(T2)之后的另一行找到一行(T1),其中action_type = 2,其中没有行(T3)与不同的动作类型。 T3.customer IS NULL检查NULL,因为如果列为NULL(我假设它是一个NOT NULL列),那么这意味着LEFT OUTER JOIN必须找不到符合条件的行。