回答

收藏

将数据库API游标与JDBC和SQLServer结合使用以选择批处理结果

技术问答 技术问答 78 人阅读 | 0 人回复 | 2023-09-13

已解决(请参见下面的答案。)$ r% v  m. [8 P  }. A
我没有在适当的背景下理解我的问题。真正的问题是我的查询返回了多个ResultSet对象,而我之前从未遇到过。我在下面发布了解决问题的代码。, R7 Q/ [4 D) W' d, i

6 \+ ^1 ~# c& T( p2 c# `0 e( ]问题6 |7 E6 r5 S' u1 ?" g' k! Q; ]4 `$ w
我有一个包含数千行的SQL/ j" e" |! w5 H, E5 D
Server数据库表。我的目标是从源数据库中拉回数据并将其写入第二个数据库。由于应用程序内存的限制,我将无法一次全部拉回数据。另外,由于该特定表的模式(我无法控制),因此我没有一种使用某种ID列来剔除行的好方法。* S. U% s6 ?' ^6 ~7 [- m( Z2 i
数据库管理员StackExchange的一位绅士通过组合一个称为数据库API游标的东西帮助了我,并基本上编写了这个复杂的查询,我只需要将语句放入其中即可。当我在SQL) l- n4 B4 D& g6 `! V
Management Studio(SSMS)中运行查询时,它的效果很好。我取回所有数据,一次一千行。
+ D- A% A; X! |+ Y不幸的是,当我尝试将其转换为JDBC代码时,我只获得了前几千行。
- b7 g" b6 i* i3 p/ W" V4 ~5 `) {问题
6 X: @/ I! y6 W; ~- z1 h- K+ H& m是否可以使用JDBC检索数据库API游标,从中拉出第一行,允许游标前进,然后一次拉出后一组?(在这种情况下,一次执行一千行。)
! v' b# F& D0 w7 g% e1 m" eSQL代码
5 b0 I& |- P# A4 t! z这变得很复杂,因此我将对其进行分解。# j5 d+ j. p& ?7 Y4 Q# A
实际查询可以是简单的也可以是复杂的。没关系
9 J6 N0 r* Y$ }  o我在实验过程中尝试了几种不同的查询,它们都可以工作。您只需将其放入适当位置的SQL代码中即可。因此,让我们将此简单语句作为查询:
* s" M- X6 D5 W; g; Q5 d2 cSELECT MyColumn FROM MyTable;$ P/ m# z6 U% g: v$ k
实际的SQL数据库API游标要复杂得多。我将在下面打印出来。您可以看到上面的查询隐藏在其中:7 i0 E0 H9 C8 g4 L$ i
-- http://dba.stackexchange.com/a/828067 d7 M" N  ?0 S$ F
DECLARE @cur INTEGER
4 h7 |1 x$ J5 ^, m: Y    ,7 U. K. T0 }) u% {: r* V
    -- FAST_FORWARD | AUTO_FETCH | AUTO_CLOSE
& W( R4 K- ]( }  W    @scrollopt INTEGER = 16 | 8192 | 16384
, H$ Y. D. C8 a' @    ,
* |! ~1 V! B. W    -- READ_ONLY, CHECK_ACCEPTED_OPTS, READ_ONLY_ACCEPTABLE
/ g, b9 I& P  r! S" L2 Y    @ccopt INTEGER = 1 | 32768 | 65536  Q9 ~$ b6 L% K$ w* D
    ,@rowcount INTEGER = 10007 F2 L: g; z' R( X- l5 i8 W
    ,@rc INTEGER;# R! o; u: U1 ?0 m2 c. I+ |+ P9 W0 B
-- Open the cursor and return the first 1,000 rows2 D& c' M! w4 u1 T0 i, `
EXECUTE @rc = sys.sp_cursoropen @cur OUTPUT
$ ?- k) s/ d, O. K  C1 r    ,'SELECT MyColumn FROM MyTable'
- m/ e2 H6 |, `2 h% ?3 m/ Z* E5 t+ I    ,@scrollopt OUTPUT
! X4 E7 x$ Z) b# k& H    ,@ccopt OUTPUT! Q7 l+ s7 _& k0 L  _) f
    ,@rowcount OUTPUT;
; u1 W" c9 P" Z: s( v4 RIF @rc  16 -- FastForward cursor automatically closed
+ M+ `8 c8 w" f, ^- A  g) ]BEGIN
, q8 M3 O% ~0 y& `- e9 M: U    -- Name the cursor so we can use CURSOR_STATUS
2 V+ A& b& p6 x3 U; a# X$ r    EXECUTE sys.sp_cursoroption @cur
# G) M2 r# J0 m" J* E" Z! _        ,2
/ j# A  `4 @) F+ z        ,'MyCursorName';
7 A" e! J' J9 Z$ n  Y, U0 V    -- Until the cursor auto-closes
$ ~- h! _' p. U5 M; K6 w4 V' n    WHILE CURSOR_STATUS('global', 'MyCursorName') = 1- W% B1 B: v4 f. e1 f3 a1 r
    BEGIN
1 I' |; N: F' U% w- R( v& V        EXECUTE sys.sp_cursorfetch @cur
. O% w3 g. [% H            ,2
" Z) `: [* l: W5 c! ^6 `            ,0! Q& a/ T& h' y# L  H% e
            ,1000;
8 |4 @0 S2 q$ K6 Z. M7 t# l    END;$ j9 }1 T4 z6 h
END;+ k7 D6 Z" C0 m: k" T' ?
就像我说过的那样,上面的代码在数据库中创建了一个游标,并要求数据库执行该语句,(内部)跟踪返回的数据,并一次返回一千行。效果很好。
' M  D# B/ {. z. c' iJDBC代码
* l: c1 t8 ?/ T7 ~- K6 l' J; x这就是我遇到的问题。我的Java代码没有编译问题或运行时问题。我遇到的问题是它仅返回前一千行。我不明白如何正确利用数据库游标。我已经尝试了Java基础的各种变化:
7 v" e1 H) f. J) p# i6 s) e: {* q1 \# e// Hoping to get all of the data, but I only get the first thousand.% `* Z6 n& [/ X8 S" g
ResultSet rs = stmt.executeQuery(fq.getQuery());+ O* R7 I# E. z: S* g
while (rs.next()) {- Q* C1 r0 o! o$ h
    System.out.println(rs.getString("MyColumn"));
# `) @) r* _) U5 j, H}
+ L: V6 _9 ?" Y/ D2 N& B我对结果并不感到惊讶,但是我尝试过的所有变体都会产生相同的结果。
% d0 A  c- }( D从我的研究看来,当数据库为Oracle时,JDBC似乎对数据库游标做了一些操作,但是您必须将结果集中返回的数据类型设置为Oracle游标对象。我猜想SQL
" b9 ~9 Z+ O4 s+ ^Server有一些类似的东西,但是我还找不到任何东西。
% O/ H+ n1 r1 M1 W有人知道吗?
% [% w7 j5 i; W' j我完整地包含了示例Java代码(这很丑陋)。4 v: A& I7 U% T, l. P4 V$ Q; @7 R
// FancyQuery.java, h( o5 e$ [# v: I2 j
import java.sql.*;
) Q6 x" d2 |; v3 r0 H1 `5 @8 opublic class FancyQuery {
& h7 G9 t- k; u; \0 p" C4 P. f    // Adapted from http://dba.stackexchange.com/a/82806' _' G" j( x' e5 ~
    String query = "DECLARE @cur INTEGER\n"1 V' X3 F8 U0 L1 N7 {$ h
                 + "    ,\n"
+ M. w; a7 _) U& u. Q                 + "    -- FAST_FORWARD | AUTO_FETCH | AUTO_CLOSE\n"
; @9 F1 Z# `% l% B                 + "    @scrollopt INTEGER = 16 | 8192 | 16384\n"
% f  i; Z. ?' p! n1 e                 + "    ,\n"
" P% C, N6 E) e: N7 I) A                 + "    -- READ_ONLY, CHECK_ACCEPTED_OPTS, READ_ONLY_ACCEPTABLE\n"
7 @& S9 g/ N4 z! [+ J                 + "    @ccopt INTEGER = 1 | 32768 | 65536\n"4 }9 W4 e+ s1 ]6 }
                 + "    ,@rowcount INTEGER = 1000\n"
+ `; D2 M" O2 L2 K$ X% C  |                 + "    ,@rc INTEGER;\n"! w% l+ `! i5 W1 F& o
                 + "\n"
+ w- I9 H6 T( d5 `9 |0 P- ]; G                 + "-- Open the cursor and return the first 1,000 rows\n"
4 q& G  M: Z1 O. N" m8 v                 + "EXECUTE @rc = sys.sp_cursoropen @cur OUTPUT\n"
# j: L" v3 `6 i3 |                 + "    ,'SELECT MyColumn FROM MyTable;'\n"6 s7 D3 x( L  _3 U
                 + "    ,@scrollopt OUTPUT\n". ~  F; ~% E! |" \* ~. B
                 + "    ,@ccopt OUTPUT\n"2 w' O( n) ~0 b: _* |
                 + "    ,@rowcount OUTPUT;\n"
9 H/ z: r4 t+ c( A+ k% g& I                 + "    \n"
9 o# W* c& W- r- ?                 + "IF @rc  16 -- FastForward cursor automatically closed\n"$ v: u2 a4 i: `  l9 C( p, n
                 + "BEGIN\n"
0 O( J/ |8 l* e  ^5 L                 + "    -- Name the cursor so we can use CURSOR_STATUS\n"
: c9 _# T4 x: T7 ~$ y5 n                 + "    EXECUTE sys.sp_cursoroption @cur\n"
" @( U/ o* I, |7 {" u                 + "        ,2\n"
3 A6 Z( L7 f4 Z1 i9 k                 + "        ,'MyCursorName';\n"
; j5 r0 S  L1 C/ m' w6 p0 P0 M                 + "\n"$ T* d& K  U' M+ G6 k
                 + "    -- Until the cursor auto-closes\n"; A2 P3 d: R+ b/ G% f
                 + "    WHILE CURSOR_STATUS('global', 'MyCursorName') = 1\n"
0 T/ c" \5 M8 V7 Y5 j, S                 + "    BEGIN\n"
) e3 K/ V) F3 N: Q5 [% K                 + "        EXECUTE sys.sp_cursorfetch @cur\n"9 t5 U3 N# P8 Y/ h# d9 s
                 + "            ,2\n"
0 ^1 h- M0 g% X# j4 y; R                 + "            ,0\n"$ C3 q  A, x6 M7 j& _9 u
                 + "            ,1000;\n"
- B8 S- @3 U5 T8 g                 + "    END;\n"
5 ?& r! L. ]+ y( q# O/ T/ K                 + "END;\n";  T, }, R( ~' }7 ?# m" S+ n/ e; V4 c
    public String getQuery() {
! B! |  F' V) a' I- c6 u        return this.query;, f2 Q2 F: ~+ Y; H4 }4 T
    }6 H4 q3 V; c! D( h8 h
    public static void main(String[ ] args) throws Exception {
5 i3 g% n# t, ]8 ?% [- S, w        String dbUrl = "jdbc:sqlserver://tc-sqlserver:1433;database=MyBigDatabase";; |8 V/ @' `; l8 P& I! }
        String user = "mario";
/ H/ {# B$ Q7 [$ d$ v$ Z9 U        String password = "p@ssw0rd";
4 T) t4 |* [0 Z( Q8 A* a* M8 Q        String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
4 o: g! Q4 c& ]3 r# y        FancyQuery fq = new FancyQuery();7 A1 u* \# O% C3 ^8 t# G8 [0 K
        Class.forName(driver);2 ?  S# E: `- W" [# F$ {' M
        Connection conn = DriverManager.getConnection(dbUrl, user, password);
" t$ k& q; |1 O- G9 Y- N        Statement stmt = conn.createStatement();
( p: S4 @) ?' a& u1 t0 `        // We expect to get 1,000 rows at a time.* B+ e- \( u: h6 X1 D, R" E
        ResultSet rs = stmt.executeQuery(fq.getQuery());
; v9 w7 }- g4 `! i; @+ B9 N        while (rs.next()) {
0 T9 m; D% y% v- m. _* t. R1 ^            System.out.println(rs.getString("MyColumn"));1 u, E2 I- @& T7 B& e
        }
) o% m1 l. O% Y4 A, m4 g: M6 r        // Alas, we've only gotten 1,000 rows, total.
6 a) H' K5 m8 b' \        rs.close();
% {; ?3 k4 h0 c4 x# m* i        stmt.close();
" m! i; p* Q; H: v        conn.close();4 C7 j8 k( Z& r: N8 ]8 h
    }
4 E( }( Z1 H0 H}
2 I9 T# p* I* H) z. D               
5 }- {" u9 w. k! P$ U+ [9 i0 I+ V解决方案:# k* q) b& `0 p& r% N1 [) [/ Q
                0 G! a' I0 ]- G. |3 |% H9 k  ~; R
9 |  Z2 X" r3 N$ A
! N" t" E7 r/ m9 p1 c. |
                我想到了。' f0 k. o: v* C) u# ~: N
stmt.execute(fq.getQuery());
% s% m" x5 f2 w2 z% y* pResultSet rs = null;
, {* }4 X$ v2 g' F3 T2 s( f# l6 efor (;;) {! U! m0 P+ U4 A% z1 E- L
    rs = stmt.getResultSet();' n4 h4 }# q& D' V' K# z5 J" m; u
    while (rs.next()) {
8 W6 S) I! U3 A2 @        System.out.println(rs.getString("MyColumn"));
' |4 S* n) ~- ?- i    }: }3 B' J5 f# y( X5 Q: q2 ~7 V
    if ((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1)) {
: g- q# ]6 N& d4 ~7 p6 F9 e        break;
" g( E$ V3 y7 y5 _& D7 k/ t    }
( t! \3 H, B3 r4 I}
- b' M, F. {, Vif (rs != null) {" n% f8 d0 [9 k; i+ W
    rs.close();
1 Z1 F) M& d! Y6 o! X) ^% f6 V}8 Y  j4 p5 l" S- n" n4 f
经过更多的谷歌搜索之后,我发现了一些早在2004年发布的代码:3 ?; Z* G3 u! O/ I6 Z
http://www.coderanch.com/t/300865/JDBC/databases/SQL-Server-JDBC-Registering-
$ J9 Y; D. k7 V  b1 Q/ `cursor4 `0 J# t' T% \+ j) i
张贴了我发现有用的代码片段的绅士(朱利安·肯尼迪)建议:“阅读Javadoc的getUpdateCount()和getMoreResults()可以清楚地理解。”8 G# D& M/ p  U, i
这样我就可以将其拼凑起来。
4 t8 S" w4 {( n) V基本上,我认为我从一开始就不太了解我的问题,无法正确地表达它。归结为,我的查询将在多个ResultSet实例中返回数据。我需要的是一种不仅遍历ResultSet中的每一行,而且遍历整个ResultSet集的方法。那就是上面的代码所做的。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则