回答

收藏

SQL Server,查找任意值序列

技术问答 技术问答 195 人阅读 | 0 人回复 | 2023-09-14

假设我们有一张表Maintenance2 S+ X# j4 o/ }
Customer LastLogin ActionType
# _6 ~  A' c$ B9 v' ?1        12/1/2007 2
: q+ o3 c+ d3 ~4 |/ P1        12/2/2007 2% c. j  G, j2 r8 g% J
etc.
8 P8 c- Q5 Z' y7 ?9 _- N# ~我们想要一个在给定年份中的任何时间点上具有一个或多个不间断序列(长14天)且操作类型为2的登录的所有客户的列表。* {3 D: R% {0 j/ x
我当然可以轻松地用代码来做到这一点,甚至可以在小型代码集上相当快地做到这一点。在SQL中有非游标方法吗?
7 D7 z  C* l" V* Q  r: ?               
3 P2 B5 E& m0 z7 A! q- T6 ?! }解决方案:5 H% A$ j) X/ |3 v; ^1 C
               
% |5 ~4 D' X$ h4 }7 T" I/ i/ s: s9 f  B

/ G: B; \- E) }5 Q( j" q! v! ]                这将选择具有至少两个连续的相同类型操作的所有客户。
) a4 o- a$ |+ I( m6 G4 d: V, QWITH    rows AS 1 F3 B: x( Q/ M' f
        (
/ P1 a2 @0 e2 y! @- ~        SELECT  customer, action,: z: [) C% S3 l+ \+ _7 \
                ROW_NUMBER() OVER (PARTITION BY customer ORDER BY lastlogin) AS rn
1 T& C) ^$ l. b0 f# f: M/ h        FROM    mytable8 m; i% e+ h6 M& e
        )$ Q. D; j! I0 z, g6 l3 a' Z
SELECT  DISTINCT customer0 ~2 V; k+ Q- [7 `" [
FROM    rows rp' F) G$ f6 P+ N
WHERE   EXISTS
9 ?( C1 H: W! v" X0 _        (
4 F! V8 U) V9 x% C! b9 s        SELECT  NULL
& t* S: N3 B. L9 o9 {        FROM    rows rl
( ^) Q! Y- o, u0 A: c' t        WHERE   rl.customer = rp.customer4 o1 \$ _8 o: X, L; _. w2 P' w
                AND rl.rn = rp.rn + 1$ [, C1 {' R1 y. F9 B% _
                AND rl.action = rp.action
2 x! k) o; z/ ^: n0 b        )
) }5 Y  h, R+ Y% r$ z/ I' M# ?' N这是正义行动的更有效查询2:
" e1 K5 r7 e9 J$ z8 A8 ZWITH    rows AS * r/ m( O0 d1 x: X1 P
        (
0 M" x' n% c) ?3 m        SELECT  customer, ROW_NUMBER() OVER (PARTITION BY customer ORDER BY lastlogin) AS rn3 o* |5 \* x- a; }! e2 O
        FROM    mytable
. q! L4 G* t6 I  C2 b2 t        WHERE   action = 2
& b6 u/ u5 r/ Z" O! L- r8 K        ): C+ W$ i+ F* w& {3 g/ v" R1 N# v
SELECT  DISTINCT customer" s, ], E! O  d% U: g
FROM    rows rp
* t. O! F& D! q, xWHERE   EXISTS3 m: t6 }, C4 z
        (
/ o: R/ i: T9 Q2 f: q  o        SELECT  NULL) s3 F7 M  S% I
        FROM    rows rl! H4 {* b3 m8 j) F6 Y; e
        WHERE   rl.customer = rp.customer
: \8 @! C; g8 R3 [0 v' g                AND rl.rn = rp.rn + 1
0 M) j, _$ k8 G6 ?5 y        )( N. Y4 @/ w. t4 A' S2 M
更新2:$ N; G$ o* Y& ^7 e3 {
要选择不间断范围:
# j( }- f- z, x  X) ]WITH    rows AS - B0 Y+ A2 p7 n8 j7 z2 Z8 A
        (
) B/ t0 a: B, r7 X( p* B3 b        SELECT  customer, action, lastlogin- R  b/ r" {& N, }3 Z
                ROW_NUMBER() OVER (PARTITION BY customer ORDER BY lastlogin) AS rn
8 C6 ~5 d5 k; X( ]$ U                ROW_NUMBER() OVER (PARTITION BY customer, action ORDER BY lastlogin) AS series( d% J4 }: l4 O' w! h
        FROM    mytable4 u; `* U; g& \6 T# ?; w! @! m' \
        )
- {  D. E; e* {& K/ ZSELECT  DISTINCT customer% C+ y* a& m6 e, ?
FROM    (
5 p6 d; _, w& r+ R        SELECT  customer2 Z0 n0 q  G" Y5 x5 N, Y( h; c
        FROM    rows rp( v5 ^- l7 i. S
        WHERE   action
/ F/ U: j1 j# {- R! `        GROUP BY
; ^/ h7 E6 V0 `. O! `+ u; G                customer, actioncode, series - rn
; Q6 p5 L0 ?( L9 }& Y" P4 G        HAVING6 e2 T0 l9 f: V1 ?
                DETEDIFF(day, MIN(lastlogin), MAX(lastlogin)) >= 146 Z5 d& O2 m/ i+ U/ @! K+ F6 X* L
        ) q
$ w& P- W+ P* F: w" }此查询计算两个序列:一个返回连续的序列ORDER BY lastlogin,第二个通过action另外划分:, y; ]9 i2 A" e& S% ?2 E+ M9 g
action  logindate rn  series diff = rn - series6 U1 ]' u; E/ r) m
1       Jan 01    1   1      01 p; D  J  u  f$ Z: z  U
1       Jan 02    2   2      0
$ X0 c2 q: K! i& E2       Jan 03    3   1      25 H! _" u! Y4 E. {
2       Jan 04    4   2      25 v/ S1 i. `4 s+ F* j- y. U
1       Jan 05    5   3      2! F4 m! x9 d* E9 h9 \4 l
1       Jan 06    6   4      2
* q0 T$ F1 _2 z  A只要两种方案之间的差异相同,该系列就不会中断。每次打断都会打断系列。3 g% _7 Y2 n4 L# J; W
这意味着(action, diff)的组合定义了不间断的组。/ h8 {7 w: j, e
我们可以按分组action, diff,在分组中找到MAX和MIN,然后对其进行过滤。: k" U, R+ ~* t- L" F; q* ^
如果您需要选择14行而不是14连续几天,只是过滤的COUNT(*),而不是DATEDIFF。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则