|
我有一个表articles,其中包含以下格式的数据:' Q1 p6 @% U6 p I
id|categories
' C; P) R" Z0 h! K2 N% Y4 u--+----------/ [6 A, Z5 { q& m/ @
1|123,13,43
4 h" O$ M# P( Y; T. J' [8 O4 Z2|1,3,151 I Y4 h2 G, P* H l
3|9,17,44,18,3
) o) P D$ E: K出于测试目的,您可以使用以下SQL命令创建该表:
$ a" _3 P1 Z. \CREATE TABLE articles(id INTEGER PRIMARY KEY, categories TEXT);
1 y5 S7 c A' Y+ vINSERT INTO articles VALUES(1, '123,13,43'), (2, '1,3,15'), (3, '9,17,44,18,3');
' y- X: [4 w, N! C3 v \' n; ?现在,我想拆分categories列的值,以便结果如下表所示:! o1 I2 A/ C0 k8 U* w
id|category
& g# ?. h% x! D# l" k--+--------
6 y; L' u5 ?& n9 u4 B( A8 w: \8 ~* J1|123
: @, a1 D. y6 `7 r% u: F- p1|13) D3 m' Q6 s I1 B' y$ ]' u
1|43' |- O) I; X+ ]
2|10 O( y) A c5 K0 s
2|3
( }' q( z9 T' Q1 \- N2|15
$ u( M8 f# R+ o4 i3 L, R y3|90 \" R; E8 [: e$ d7 g
3|172 G/ @" ]0 U7 v+ X1 ^7 e; S; P
3|44/ {. p$ g( j& i
3|18
7 L4 W' ~) L H$ v( _% V3|3
3 m; J9 v F' H" [1 t% M- A* B6 M' B如您所见,我想将原始表转换为First普通形式。% ]* l) P" T: _ N8 o8 {
我已经知道如何分割只是 一个
. e; B* r* {$ v3 p这样行,从这个答案。下面的代码示例仅获取第二行(即id =2的那一行),并以所需的方式拆分它们:, W- I' E, L. l9 x
WITH split(article_id, word, str, offsep) AS
) S- I) z& m' Q# e/ `3 n, X(
& Q* r6 S0 l% A$ J6 e9 O. k* p' D VALUES/ j' ]: ^6 Q7 L4 p
(3 F, j; a. |, w% }( V
2,0 T5 c# b, q2 w, p# d: O. J
'',- K6 x1 {0 ?. h
(SELECT categories FROM articles WHERE id=2),
5 Q3 w# ^7 D) A8 G B3 b2 ? 1$ r8 u1 p+ f7 B& x5 w4 U
): P& f/ a) S. O# h. O
UNION ALL
9 N8 _9 T* l7 Y# K SELECT X5 m' d! b9 }; i- `( p" j
article_id,
" }, z! ]7 M. b. L# L# Z" @% o substr(str, 0, CASE WHEN instr(str, ',') THEN instr(str, ',') ELSE length(str)+1 END),; f5 v2 l0 c, { P8 p) Z
ltrim(substr(str, instr(str, ',')), ','),
c- n* u6 q: F& M instr(str, ',')
# R) }8 ?+ M' T3 J7 [$ |4 I" _ FROM split9 _, }8 k3 c3 R
WHERE offsep* ?. f" B5 P$ S( z2 y+ q" g# A0 A
) SELECT article_id, word FROM split WHERE word!='';
# m$ s% d! J4 M当然,这是非常不灵活的,因为商品ID需要进行硬编码。因此,现在我的问题是:我必须添加或更改上面的SQLite代码以使其对所有行进行操作并输出所需的结果吗?' k2 z6 k# g; ?+ b3 ]( d5 G
' _) ]5 t7 I* }2 Y5 {
解决方案:% i- u) B7 o7 v3 P- A C
/ {. H# R& a0 n0 ^" H3 ]" I! C7 }4 K* N+ j$ Z% t/ B
. b' D2 M4 ^. i; D0 a- f( r
经过一番摸索,我终于自己弄清楚了解决方案。它还照顾具有''或NULL作为值的行categories:+ v( i' p3 q- D3 y! V7 `5 p/ w- \" n
-- create temporary table which buffers the maximum article ID, because SELECT MAX can take a very long time on huge databases
# r2 [& W- P3 f) p& wDROP TABLE IF EXISTS max_article_id;
2 ]1 c' N2 \/ a3 i4 eCREATE TEMP TABLE max_article_id(num INTEGER);% Q$ D+ |% ^; p- w, I" `2 u
INSERT INTO max_article_id VALUES((SELECT MAX(id) FROM articles));7 [+ z+ O8 x/ ^3 f5 {7 J" T: N
WITH RECURSIVE split(article_id, word, str, offsep) AS
9 @1 E' q; i6 s, }3 d9 Q B) @( }( r! e" b( F0 K: H2 H: e: i
VALUES ( 0, '', '', 0 ) -- begin with dummy article 0 (which does not actually exist) to avoid code duplication9 [! C8 d! K" J" V8 Q
UNION ALL
% q) E o M& A, o" Y( F e- w! C SELECT. \7 h$ }6 U' x) P+ P1 D9 }
CASE WHEN offsep==0 OR str IS NULL: r+ U' w0 h) [( E
THEN article_id+1 -- go to next article if the current one is finished$ o- A* X5 ]4 k# c. X2 j) `+ E
ELSE article_id -- and keep the current one in the opposite case/ O& v; i1 u. `
END,
0 P- r2 t4 F* e; e7 z. v CASE WHEN offsep==0 OR str IS NULL
) x* j: c1 Q3 e4 Q0 Q THEN ''9 C1 h5 H( C6 _
ELSE substr(str, 0, CASE WHEN instr(str, ',') THEN instr(str, ',') ELSE length(str)+1 END), ` f# S; D/ I
END,& g5 p a7 g# |# {
CASE WHEN offsep==0 OR str IS NULL -- when str==NULL, then there has been a NULL value for the categories cell of the current article6 b0 _$ ^( Q7 \9 Z* _+ K
THEN (SELECT categories FROM articles WHERE id=article_id+1)
& b' V% X) N: e7 A ELSE ltrim(substr(str, instr(str, ',')), ',')
8 V/ B7 h$ p S. l& q- Q: T, S END,9 n' X* i2 P. ~( }# R
CASE WHEN offsep==0 OR str IS NULL -- offsep==0 means that the splitting was finished in the previous iteration6 O6 Q# ^& Z4 X/ ]2 v! S- A" d
THEN 1 -- offsep==1 means that splitting the categories for a new article will begin in the next iteration& D) f1 }8 W! [4 Q
ELSE instr(str, ',') -- the actual string splitting stuff is explained and taken from here: http://stackoverflow.com/a/32051164
* J }- h- }# I! W, M END
. D0 }0 Z/ O9 w1 \6 `$ R FROM split# m) t, `( G# J6 y$ j
WHERE article_id<=(SELECT * FROM max_article_id) -- stop getting new articles when the maximum article ID is reached/ H! p; n5 b; m3 E' \5 J
) SELECT article_id, word AS category FROM split WHERE word!=''; -- only select article_id and word from the result to use output the desired table layout |
|