SQL Server,找到任意值的序列

时间:2009-11-30 16:32:23

标签: sql sql-server-2008

我们假设我们有一个维护表

Customer LastLogin ActionType
1        12/1/2007 2
1        12/2/2007 2
etc.

我们希望列出所有客户,这些客户在某一年内的任何时间点都有一个或多个不间断的序列,长达14天,以行动类型2登录。

我当然可以使用代码轻松完成此操作,甚至可以在小集合上快速完成。是否有一种非游标方式在SQL中执行它?

5 个答案:

答案 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分组,在群组中找到MAXMIN并对其进行过滤。

如果您需要选择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必须找不到符合条件的行。