Wydajne zapytanie testowe SQL lub zapytanie walidacyjne, które będzie działać we wszystkich (lub większości) baz danych

148

Wiele bibliotek puli połączeń bazy danych umożliwia testowanie połączeń SQL pod kątem bezczynności. Na przykład biblioteka c3p0 buforująca JDBC ma właściwość o nazwie preferredTestQuery, która jest wykonywana w połączeniu w skonfigurowanych odstępach czasu. Podobnie Apache Commons DBCP ma validationQuery.

Wiele przykładowych zapytań , które widziałem, dotyczy MySQL i zalecamy użycie ich SELECT 1;jako wartości dla zapytania testowego. Jednak to zapytanie nie działa na niektórych bazach danych (np. HSQLDB, dla których SELECT 1oczekuje FROMklauzuli).

Czy istnieje zapytanie niezależne od bazy danych, które jest równie wydajne, ale będzie działać dla wszystkich baz danych SQL?

Edytować:

Jeśli nie ma (co wydaje się mieć miejsce), czy ktoś może zasugerować zestaw zapytań SQL, które będą działać dla różnych dostawców baz danych? Moim zamiarem byłoby programowe określenie instrukcji, której mogę użyć, na podstawie konfiguracji mojego dostawcy bazy danych.

Rob Hruska
źródło
1
Uwaga: konfigurowanie zapytania testowego nie jest już potrzebne, zobacz moją odpowiedź poniżej
Tim Büthe

Odpowiedzi:

274

Po krótkich badaniach i pomocy z niektórymi odpowiedziami tutaj:

SELECT 1

  • H2
  • MySQL
  • Microsoft SQL Server (według NimChimpsky )
  • PostgreSQL
  • SQLite

SELECT 1 FROM DUAL

  • Wyrocznia

SELECT 1 FROM any_existing_table WHERE 1=0

lub

SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS

  • HSQLDB (testowane z wersją 1.8.0.10)

    Uwaga: próbowałem użyć WHERE 1=0klauzuli w drugim zapytaniu, ale nie działało to jako wartość dla DBCP Apache Commons validationQuery, ponieważ zapytanie nie zwraca żadnych wierszy


VALUES 1 lub SELECT 1 FROM SYSIBM.SYSDUMMY1

SELECT 1 FROM SYSIBM.SYSDUMMY1

  • DB2

select count(*) from systables

  • Informix
Rob Hruska
źródło
Powinno to być „SELECT 1 FROM any_existing_table WHERE 1 = 0” - w przeciwnym razie wywołanie może być bardzo wolne. Nawiasem mówiąc, zarówno SELECT 1, jak i SELECT 1 FROM DUAL również współpracują z H2.
Thomas Mueller
2
Wiem, że to kilka lat, ale możesz chcieć dodać oba VALUES 1i SELECT 1 FROM SYSIBM.SYSDUMMY1dla Apache Derby
daiscog
Zakładając, że OP chce odpowiedzi Java: uważam, że w przypadku Java 6 ta odpowiedź jest teraz nieaktualna. Zobacz moją odpowiedź w innym miejscu na tej stronie.
peterh
Możesz dodać te dwa elementy do swojej odpowiedzi, DB2: "WYBIERZ bieżącą datę Z sysibm.sysdummy1" Informix: "wybierz liczbę (*) z systables"
Michael
@Michael Jeśli chcesz zasugerować zmianę, zaakceptuję ją. Dodatkowo dostaniesz za to kilka punktów rep.
Rob Hruska,
22

Jeśli Twój sterownik jest zgodny z JDBC 4, nie ma potrzeby wysyłania dedykowanego zapytania w celu przetestowania połączeń. Zamiast tego istnieje Connection.isValid do testowania połączenia.

JDBC 4 jest częścią Java 6 od 2006 roku i Twój sterownik powinien już to obsługiwać!

Słynne pule połączeń, takie jak HikariCP, nadal mają parametr konfiguracyjny do określania zapytania testowego, ale zdecydowanie odradzamy jego używanie:

🔠connectionTestQuery

Jeśli Twój sterownik obsługuje JDBC4, zdecydowanie odradzamy ustawianie tej właściwości. Dotyczy to „starszych” baz danych, które nie obsługują funkcji API JDBC4 Connection.isValid (). Jest to zapytanie, które zostanie wykonane tuż przed uzyskaniem połączenia z puli w celu sprawdzenia, czy połączenie z bazą danych nadal działa. Ponownie spróbuj uruchomić pulę bez tej właściwości, HikariCP zarejestruje błąd, jeśli sterownik nie jest zgodny z JDBC4, aby Cię o tym powiadomić. Domyślnie: brak

Tim Büthe
źródło
9

Niestety nie ma instrukcji SELECT, która zawsze będzie działać niezależnie od bazy danych.

Większość baz danych obsługuje:

SELECT 1

Niektóre bazy danych tego nie obsługują, ale mają tabelę o nazwie DUAL, której możesz użyć, gdy nie potrzebujesz tabeli:

SELECT 1 FROM DUAL

MySQL również to obsługuje ze względu na kompatybilność, ale nie wszystkie bazy danych tak. Obejściem dla baz danych, które nie obsługują żadnego z powyższych, jest utworzenie tabeli o nazwie DUAL, która zawiera pojedynczy wiersz, wtedy powyższe będzie działać.

HSQLDB nie obsługuje żadnego z powyższych, więc możesz utworzyć tabelę DUAL lub użyć:

SELECT 1 FROM any_table_that_you_know_exists_in_your_database
Mark Byers
źródło
Dziękuję za odpowiedź. Zaktualizowałem nieznacznie moje pytanie ze względu na Twoje stwierdzenie „nie ma instrukcji SELECT, która zawsze będzie działać”. SELECT 1 FROM DUALrównież nie działa z HSQLDB.
Rob Hruska
1
+1, to jest o tym, gdzie przyszedłem z moimi badaniami, szczególnie w przypadku HSQLDB.
Rob Hruska
które z nich nie obsługują „wybierz 1”? Wybierz z dwóch tylko działa Oracle, prawda? Nie serwer sql, a przynajmniej mysql
NimChimpsky
+1 Zrezygnowałem z myślenia o niezależnym sposobie RDBMS!
Martin Smith
2

Używam tego:

select max(table_catalog) as x from information_schema.tables

aby sprawdzić połączenie i możliwość uruchamiania zapytań (z wynikiem 1 wiersza) dla postgreSQL, MySQL i MSSQL.

Wojciechk
źródło
2

używam

Select COUNT(*) As X From INFORMATION_SCHEMA.SYSTEM_USERS Where 1=0

dla hsqldb 1.8.0

thinkbase
źródło
2

W przypadku testów używających select count(*)powinno być bardziej wydajne, select count(1)ponieważ *może spowodować odczytanie wszystkich danych z kolumny.

Nathan Niesen
źródło
1

select 1 działałby na serwerze sql, nie mając pewności co do innych.

Użyj standardowego ansi sql, aby utworzyć tabelę, a następnie wykonać zapytanie z tej tabeli.

NimChimpsky
źródło
Czy ansi SQL obejmuje create table?
Martin Smith
tak. Jeśli używasz typów danych ANSI. Byłbym zdziwiony, gdyby „wybierz 1” nie zadziałało.
NimChimpsky
1

Zakładając, że OP chce odpowiedzi Java:

Od JDBC3 / Java 6 istnieje metoda isValid () , której należy używać zamiast wymyślać własną metodę.

Implementator sterownika jest zobowiązany do wykonania pewnego rodzaju zapytania w bazie danych, gdy wywoływana jest ta metoda. Ty - jako zwykły użytkownik JDBC - nie musisz wiedzieć ani rozumieć, czym jest to zapytanie. Wszystko, co musisz zrobić, to zaufać, że twórca sterownika JDBC wykonał swoją pracę poprawnie.

peterh
źródło
2
Myślę, że OP mówi o zapytaniu walidacyjnym dla konfiguracji puli połączeń kontenera, a nie programowo. Na przykład w pliku context.xml serwera Tomcat, w którym konfigurujesz zasoby, pobiera on zapytanie validationQuery, którego Tomcat używa do sprawdzenia połączenia. Sam Tomcat musiałby zostać zmieniony, aby korzystać z isValid (). To nie jest coś, co OP może kontrolować.
Michael,
Warto również zauważyć, że "twórca sterownika JDBC wykonał swoją pracę poprawnie" nie jest tak naprawdę gwarantowany. Właśnie odkryłem, że ani Postgres, HSQLDB, ani H2 nie zadały sobie trudu, aby zaimplementować tę metodę, więc zawsze zgłosi tam wyjątek.
akroy
1

Co powiesz na

SELECT user()

Używam tego wcześniej, MySQL, H2 jest OK, innych nie znam.

wener
źródło
1

Właśnie się o tym przekonałem

SELECT 1 FROM DUAL

również dla MaxDB.

Lars Decker
źródło
To nie daje odpowiedzi na pytanie. Gdy zdobędziesz wystarczającą reputację , będziesz mógł komentować każdy post ; zamiast tego udziel odpowiedzi, które nie wymagają wyjaśnień od pytającego . - Z recenzji
Peter Brittain
Nie rozumiem, to dodaje wartości do zaakceptowanej odpowiedzi, więc gdzie jest problem?
Lars Decker
I jak wspomniałeś: ponieważ nie mogę skomentować zaakceptowanej odpowiedzi, więc umieszczam ją tutaj jako odpowiedź. Więc lepiej nie pisać posta, chociaż może to być pomocne tylko z powodu utraty reputacji?
Lars Decker
TBH, to mało prawdopodobne ... Zamiast powielać odpowiedź, powinien to być komentarz do oryginalnej odpowiedzi. W przeciwnym razie mógłbyś dokonać sugerowanej zmiany oryginału.
Peter Brittain
1

W przypadku Oracle wysokowydajnym zapytaniem będzie

select 'X' from <your_small_table> where <primay_key_coulmn> = <some_value>

To jest z perspektywy wydajności.

Joby Kurian
źródło
0

Używam tego dla Firebird

select 1 from RDB$RELATION_FIELDS rows 1
Claudsan
źródło
0

W przypadku MSSQL .

Pomogło mi to ustalić, czy połączone serwery działają. Używanie połączenia Open Query i TRY CATCH, aby przypisać wyniki błędu do czegoś użytecznego.

IF OBJECT_ID('TEMPDB..#TEST_CONNECTION') IS NOT NULL DROP TABLE #TEST_CONNECTION
IF OBJECT_ID('TEMPDB..#RESULTSERROR') IS NOT NULL DROP TABLE #RESULTSERROR
IF OBJECT_ID('TEMPDB..#RESULTSGOOD') IS NOT NULL DROP TABLE #RESULTSGOOD

DECLARE @LINKEDSERVER AS VARCHAR(25)    SET @LINKEDSERVER = 'SERVER NAME GOES HERE'
DECLARE @SQL AS VARCHAR(MAX)
DECLARE @OPENQUERY AS VARCHAR(MAX)

--IF OBJECT_ID ('dbo.usp_GetErrorInfo', 'P' ) IS NOT NULL DROP PROCEDURE usp_GetErrorInfo;  
--GO  

---- Create procedure to retrieve error information.  
--CREATE PROCEDURE dbo.usp_GetErrorInfo  
--AS  
--SELECT     
--    ERROR_NUMBER() AS ErrorNumber  
--    ,ERROR_SEVERITY() AS ErrorSeverity  
--    ,ERROR_STATE() AS ErrorState  
--    ,ERROR_PROCEDURE() AS ErrorProcedure  
--    ,ERROR_LINE() AS ErrorLine  
--    ,ERROR_MESSAGE() AS Message;  
--GO  


BEGIN TRY
SET @SQL='
SELECT 1 
'''
--SELECT @SQL
SET @OPENQUERY = 'SELECT * INTO ##TEST_CONNECTION FROM OPENQUERY(['+ @LINKEDSERVER +'],''' + @SQL + ')'
--SELECT @OPENQUERY
EXEC(@OPENQUERY)
SELECT * INTO #TEST_CONNECTION FROM ##TEST_CONNECTION
DROP TABLE ##TEST_CONNECTION
--SELECT * FROM #TEST_CONNECTION
END TRY

BEGIN CATCH
-- Execute error retrieval routine.
IF OBJECT_ID('dbo.usp_GetErrorInfo') IS NOT NULL -- IT WILL ALWAYS HAVE SOMTHING... 
    BEGIN
        CREATE TABLE #RESULTSERROR (
        [ErrorNumber]       INT
        ,[ErrorSeverity]    INT
        ,[ErrorState]       INT
        ,[ErrorProcedure]   INT
        ,[ErrorLine]        INT
        ,[Message]          NVARCHAR(MAX) 
        )
        INSERT INTO #RESULTSERROR
        EXECUTE dbo.usp_GetErrorInfo
    END
END CATCH

BEGIN 
    IF (Select ERRORNUMBER FROM #RESULTSERROR WHERE ERRORNUMBER = '1038') IS NOT NULL --'1038' FOR ME SHOWED A CONNECTION ATLEAST. 
        SELECT
        '0' AS [ErrorNumber]        
        ,'0'AS [ErrorSeverity]  
        ,'0'AS [ErrorState]     
        ,'0'AS [ErrorProcedure] 
        ,'0'AS [ErrorLine]      
        , CONCAT('CONNECTION IS UP ON ', @LINKEDSERVER) AS [Message]            
    ELSE 
        SELECT * FROM #RESULTSERROR
END

docs.microsoft.com

DeFlanko
źródło