|
假设我们有一张表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。 |
|