SQL Server,查找任意值序列
让我们假设我们有一个表维护SQL Server,查找任意值序列,sql,sql-server-2008,Sql,Sql Server 2008,让我们假设我们有一个表维护 Customer LastLogin ActionType 1 12/1/2007 2 1 12/2/2007 2 etc. 我们想要一份列表,列出在给定的一年中的任何时候有一个或多个连续14天的操作类型为2的登录序列的所有客户 当然,我可以很容易地用代码来实现这一点,甚至可以让它在小集合上运行得相当快。在SQL中是否有一种非光标方式来执行此操作?编辑:这将适用于原始问题,即一行两个。连续14个是不同的答案 首先,您需要一个序列,以便使用
Customer LastLogin ActionType
1 12/1/2007 2
1 12/2/2007 2
etc.
我们想要一份列表,列出在给定的一年中的任何时候有一个或多个连续14天的操作类型为2的登录序列的所有客户
当然,我可以很容易地用代码来实现这一点,甚至可以让它在小集合上运行得相当快。在SQL中是否有一种非光标方式来执行此操作?编辑:这将适用于原始问题,即一行两个。连续14个是不同的答案
首先,您需要一个序列,以便使用ROWNUMBER
您可以使用ROWNUMBER=ROWNUMBER+1对自身进行自连接维护
具有相同客户id的任意两个conrequisive行以及具有2个ActionType are的两个行都将为您提供客户列表作为您的答案
试试这个
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
这将选择至少有两个相同类型的连续操作的所有客户
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
)
以下是针对just action 2的更高效查询:
更新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
此查询计算两个系列:一个按lastlogin返回连续顺序,另一个按操作另外分区:
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
只要两种方案之间的差异相同,序列就不会中断。每次中断都会中断序列
这意味着动作diff的组合定义了不间断组
我们可以按动作、差异分组,在组中找到最大值和最小值,并对它们进行过滤
如果您需要选择14行而不是连续14天,只需根据计数*而不是日期差异进行筛选。我将假设,对于具有不同操作类型的同一用户,序列是指两行或多行具有连续的日期时间值,中间没有其他行。在这种情况下,这将为您提供您想要得到的:
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执行的正是我上面所说的-查找一行T1,在它的T2之后有另一行,这两行都具有action_type=2,其中T3之间没有具有不同action类型的行。T3.customer IS NULL检查NULL,因为如果该列为NULL,我假设它是一个非NULL列,那么这意味着左侧外部联接一定没有找到符合条件的行。使用:
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
为清晰起见编辑。我们正在寻找更长的运行序列,而不仅仅是两个连续的序列。在我发布代码后你会这样做吗?你知道我必须坐在椅子上写代码,但是面朝上坡?我应该更具体一点!你是怎么做的?特别是14个,没有14个不同的子选择。好的,我很快会测试它。我绝对需要顺序,而不仅仅是计数*。我们在寻找行为模式。
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
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