Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Jak odzyskać *wszystko* z procedury składowanej za pomocą JDBC?

Kiedy wykonujemy procedurę składowaną w JDBC, otrzymujemy serię zero lub więcej „wyników”. Następnie możemy przetwarzać te „wyniki” sekwencyjnie, wywołując CallableStatement#getMoreResults() . Każdy „wynik” może zawierać

  • zero lub więcej wierszy danych, które możemy pobrać za pomocą ResultSet obiekt,
  • liczba aktualizacji dla instrukcji DML (INSERT, UPDATE, DELETE), którą możemy pobrać za pomocą CallableStatement#getUpdateCount() lub
  • błąd, który generuje wyjątek SQLServerException.

W przypadku „problemu 1” problem często polega na tym, że procedura składowana nie zaczyna się od SET NOCOUNT ON; i wykonuje instrukcję DML przed wykonaniem SELECT w celu wygenerowania zestawu wyników. Licznik aktualizacji dla DML jest zwracany jako pierwszy „wynik”, a wiersze danych „utykają za nim”, dopóki nie wywołamy getMoreResults .

„Problem 2” to w zasadzie ten sam problem. Procedura składowana generuje „wynik” (zwykle SELECT lub ewentualnie licznik aktualizacji) przed wystąpieniem błędu. Błąd jest zwracany w kolejnym „wyniku” i nie powoduje wyjątku, dopóki go „nie pobierzemy” za pomocą getMoreResults .

W wielu przypadkach problemu można uniknąć, po prostu dodając SET NOCOUNT ON; jako pierwsza instrukcja wykonywalna w procedurze składowanej. Jednak zmiana procedury składowanej nie zawsze jest możliwa i pozostaje faktem, że aby uzyskać wszystko z powrotem z procedury składowanej, musimy nadal wywoływać getMoreResults dopóki, jak mówi dokument Javadoc:

There are no more results when the following is true: 

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

Brzmi to dość prosto, ale jak zwykle „diabeł tkwi w szczegółach”, co ilustruje poniższy przykład. Dla procedury składowanej 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

... poniższy kod Java zwróci wszystko ...

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]");
}

... tworząc następujące wyjście konsoli:

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak obracać się dynamicznie z datą jako kolumną

  2. Przewodnik:konfigurowanie wysokiej dostępności programu SQL Server

  3. Czy możemy przekazać parametry do widoku w SQL?

  4. Indeksy oparte na funkcjach w SQL Server

  5. Jak usunąć lub usunąć wszystkie wyzwalacze z bazy danych w SQL Server