sql >> Databáze >  >> RDS >> Sqlserver

Jak získat *všechno* zpět z uložené procedury pomocí JDBC

Když provedeme uloženou proceduru v JDBC, dostaneme zpět sérii nula nebo více „výsledků“. Tyto "výsledky" pak můžeme zpracovat postupně voláním CallableStatement#getMoreResults() . Každý "výsledek" může obsahovat

  • nula nebo více řádků dat, které můžeme načíst pomocí ResultSet objekt,
  • počet aktualizací pro příkaz DML (INSERT, UPDATE, DELETE), který můžeme načíst pomocí CallableStatement#getUpdateCount() nebo
  • Chyba, která vyvolá výjimku SQLServerException.

U "Issue 1" je problém často v tom, že uložená procedura nezačíná SET NOCOUNT ON; a provede příkaz DML před provedením SELECT pro vytvoření sady výsledků. Počet aktualizací pro DML je vrácen jako první "výsledek" a datové řádky jsou "zaseknuté za ním", dokud nezavoláme getMoreResults .

"Vydání 2" je v podstatě stejný problém. Uložená procedura vytvoří "výsledek" (obvykle SELECT nebo případně počet aktualizací), než dojde k chybě. Chyba je vrácena v následném „výsledku“ a nezpůsobí výjimku, dokud ji „nezískáme“ pomocí getMoreResults .

V mnoha případech lze problému předejít jednoduše přidáním SET NOCOUNT ON; jako první spustitelný příkaz v uložené proceduře. Změna uložené procedury však není vždy možná a faktem zůstává, že pro získání všeho zpět z uložené procedury musíme volat getMoreResults dokud, jak říká Javadoc:

There are no more results when the following is true: 

     // stmt is a Statement object
     ((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))

Zní to jednoduše, ale jako obvykle „ďábel je v detailech“, jak ukazuje následující příklad. Pro uloženou proceduru SQL Server ...

ALTER PROCEDURE dbo.TroublesomeSP AS
BEGIN
    -- note: no `SET NOCOUNT ON;`
    DECLARE @tbl TABLE (id VARCHAR(3) PRIMARY KEY);

    DROP TABLE NonExistent;
    INSERT INTO @tbl (id) VALUES ('001');
    SELECT id FROM @tbl;
    INSERT INTO @tbl (id) VALUES ('001');  -- duplicate key error
    SELECT 1/0;  -- error _inside_ ResultSet
    INSERT INTO @tbl (id) VALUES ('101');
    INSERT INTO @tbl (id) VALUES ('201'),('202');
    SELECT id FROM @tbl;
END

... následující kód Java vrátí vše ...

try (CallableStatement cs = conn.prepareCall("{call dbo.TroublesomeSP}")) {
    boolean resultSetAvailable = false;
    int numberOfResultsProcessed = 0;
    try {
        resultSetAvailable = cs.execute();
    } catch (SQLServerException sse) {
        System.out.printf("Exception thrown on execute: %s%n%n", sse.getMessage());
        numberOfResultsProcessed++;
    }
    int updateCount = -2;  // initialize to impossible(?) value
    while (true) {
        boolean exceptionOccurred = true; 
        do {
            try {
                if (numberOfResultsProcessed > 0) {
                    resultSetAvailable = cs.getMoreResults();
                }
                exceptionOccurred = false;
                updateCount = cs.getUpdateCount();
            } catch (SQLServerException sse) {
                System.out.printf("Current result is an exception: %s%n%n", sse.getMessage());
            }
            numberOfResultsProcessed++;
        } while (exceptionOccurred);

        if ((!resultSetAvailable) && (updateCount == -1)) {
            break;  // we're done
        }

        if (resultSetAvailable) {
            System.out.println("Current result is a ResultSet:");
            try (ResultSet rs = cs.getResultSet()) {
                try {
                    while (rs.next()) {
                        System.out.println(rs.getString(1));
                    }
                } catch (SQLServerException sse) {
                    System.out.printf("Exception while processing ResultSet: %s%n", sse.getMessage());
                }
            }
        } else {
            System.out.printf("Current result is an update count: %d %s affected%n",
                    updateCount,
                    updateCount == 1 ? "row was" : "rows were");
        }
        System.out.println();
    }
    System.out.println("[end of results]");
}

... vytvoří následující výstup konzoly:

Exception thrown on execute: Cannot drop the table 'NonExistent', because it does not exist or you do not have permission.

Current result is an update count: 1 row was affected

Current result is a ResultSet:
001

Current result is an exception: Violation of PRIMARY KEY constraint 'PK__#314D4EA__3213E83F3335971A'. Cannot insert duplicate key in object '[email protected]'. The duplicate key value is (001).

Current result is a ResultSet:
Exception while processing ResultSet: Divide by zero error encountered.

Current result is an update count: 1 row was affected

Current result is an update count: 2 rows were affected

Current result is a ResultSet:
001
101
201
202

[end of results]



  1. Chyba PostgreSQL:Závažná:uživatelské jméno role neexistuje

  2. Oracle:existuje nějaký logický důvod, proč nepoužívat paralelní spouštění s poddotazy v seznamu SELECT?

  3. SQL Server Azure / 2022 Database Ledger Tables z Linuxu.

  4. jak přidat sekundu do časového razítka oracle