Effiziente SQL-Testabfrage oder Validierungsabfrage, die in allen (oder den meisten) Datenbanken funktioniert

148

Viele Pooling-Bibliotheken für Datenbankverbindungen bieten die Möglichkeit, ihre SQL-Verbindungen auf Leerlauf zu testen. Beispielsweise verfügt die JDBC- Poolbibliothek c3p0 über eine Eigenschaft namens preferredTestQuery, die in konfigurierten Intervallen für die Verbindung ausgeführt wird. Ebenso hat Apache Commons DBCP validationQuery.

Viele Beispiel Abfragen Ich habe gesehen , sind für MySQL und empfehlen die Verwendung SELECT 1;als Wert für die Testabfrage. Diese Abfrage funktioniert jedoch nicht in einigen Datenbanken (z. B. HSQLDB, für die SELECT 1eine FROMKlausel erwartet wird ).

Gibt es eine datenbankunabhängige Abfrage, die gleich effizient ist, aber für alle SQL-Datenbanken funktioniert?

Bearbeiten:

Wenn dies nicht der Fall ist (was anscheinend der Fall ist), kann jemand eine Reihe von SQL-Abfragen vorschlagen, die für verschiedene Datenbankanbieter funktionieren? Meine Absicht wäre es, programmgesteuert eine Anweisung zu bestimmen, die ich basierend auf der Konfiguration meines Datenbankanbieters verwenden kann.

Rob Hruska
quelle
1
Hinweis: Die Konfiguration einer Testabfrage ist nicht mehr erforderlich, siehe meine Antwort unten
Tim Büthe

Antworten:

274

Nach ein wenig Recherche und Hilfe von einigen der Antworten hier:

SELECT 1

  • H2
  • MySQL
  • Microsoft SQL Server (laut NimChimpsky )
  • PostgreSQL
  • SQLite

SELECT 1 FROM DUAL

  • Orakel

SELECT 1 FROM any_existing_table WHERE 1=0

oder

SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS

  • HSQLDB (getestet mit Version 1.8.0.10)

    Hinweis: Ich habe versucht, eine WHERE 1=0Klausel für die zweite Abfrage zu verwenden, diese hat jedoch nicht als Wert für Apache Commons-DBCPs validationQueryfunktioniert, da die Abfrage keine Zeilen zurückgibt


VALUES 1 oder SELECT 1 FROM SYSIBM.SYSDUMMY1

SELECT 1 FROM SYSIBM.SYSDUMMY1

  • DB2

select count(*) from systables

  • Informix
Rob Hruska
quelle
Das sollte "SELECT 1 FROM any_existing_table WHERE 1 = 0" sein - andernfalls kann der Aufruf sehr langsam sein. Übrigens funktionieren sowohl SELECT 1 als auch SELECT 1 FROM DUAL auch mit H2.
Thomas Mueller
2
Ich weiß, dass dies ein paar Jahre alt ist, aber Sie können beide VALUES 1und SELECT 1 FROM SYSIBM.SYSDUMMY1für Apache Derby
Daiscog
Angenommen, OP möchte eine Java-Antwort: Ich glaube, dass diese Antwort mit Java 6 jetzt veraltet ist. Siehe meine Antwort an anderer Stelle auf dieser Seite.
Peterh
Sie können diese beiden zu Ihrer Antwort hinzufügen, DB2: "Aktuelles Datum AUS Sysibm.sysdummy1 AUSWÄHLEN" Informix: "Anzahl (*) aus Systables auswählen"
Michael
@Michael Wenn Sie eine Bearbeitung vorschlagen möchten, würde ich sie genehmigen. Außerdem erhalten Sie ein paar Wiederholungspunkte dafür.
Rob Hruska
22

Wenn Ihr Treiber JDBC 4-kompatibel ist, ist keine dedizierte Abfrage zum Testen von Verbindungen erforderlich. Stattdessen gibt es Connection.isValid , um die Verbindung zu testen.

JDBC 4 ist Teil von Java 6 aus dem Jahr 2006 und Ihr Treiber sollte dies jetzt unterstützen!

Berühmte Verbindungspools wie HikariCP verfügen weiterhin über einen Konfigurationsparameter zum Angeben einer Testabfrage, raten jedoch dringend davon ab, diese zu verwenden:

🔠connectionTestQuery

Wenn Ihr Treiber JDBC4 unterstützt, empfehlen wir dringend, diese Eigenschaft nicht festzulegen. Dies gilt für "Legacy" -Datenbanken, die die JDBC4 Connection.isValid () -API nicht unterstützen. Dies ist die Abfrage, die ausgeführt wird, kurz bevor Sie eine Verbindung aus dem Pool erhalten, um zu überprüfen, ob die Verbindung zur Datenbank noch besteht. Versuchen Sie erneut, den Pool ohne diese Eigenschaft auszuführen. HikariCP protokolliert einen Fehler, wenn Ihr Treiber nicht JDBC4-kompatibel ist, um Sie darüber zu informieren. Standard: keine

Tim Büthe
quelle
9

Leider gibt es keine SELECT-Anweisung, die unabhängig von der Datenbank immer funktioniert.

Die meisten Datenbanken unterstützen:

SELECT 1

Einige Datenbanken unterstützen dies nicht, haben jedoch eine Tabelle namens DUAL, die Sie verwenden können, wenn Sie keine Tabelle benötigen:

SELECT 1 FROM DUAL

MySQL unterstützt dies auch aus Kompatibilitätsgründen, jedoch nicht alle Datenbanken. Eine Problemumgehung für Datenbanken, die keines der oben genannten Verfahren unterstützen, besteht darin, eine Tabelle mit dem Namen DUAL zu erstellen, die eine einzelne Zeile enthält. Dann funktioniert das oben Gesagte.

HSQLDB unterstützt keines der oben genannten Verfahren, sodass Sie entweder die DUAL-Tabelle erstellen oder Folgendes verwenden können:

SELECT 1 FROM any_table_that_you_know_exists_in_your_database
Mark Byers
quelle
Danke für die Antwort. Ich habe meine Frage aufgrund Ihrer Anweisung "Es gibt keine SELECT-Anweisung, die immer funktioniert" leicht aktualisiert. SELECT 1 FROM DUALfunktioniert auch nicht mit HSQLDB.
Rob Hruska
1
+1, hier bin ich auch mit meiner Forschung angekommen, insbesondere für den HSQLDB-Fall.
Rob Hruska
Welche unterstützen "select 1" nicht? Wählen Sie aus Dual funktioniert nur Orakel, nicht wahr? Nicht SQL Server oder zumindest
MySQL
+1 Ich habe es aufgegeben, über einen RDBMS-unabhängigen Weg nachzudenken!
Martin Smith
2

Ich benutze dieses:

select max(table_catalog) as x from information_schema.tables

um die Verbindung und die Fähigkeit zu überprüfen, Abfragen (mit 1 Zeile als Ergebnis) für postgreSQL, MySQL und MSSQL auszuführen.

Wojciechk
quelle
2

ich benutze

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

für hsqldb 1.8.0

thinkbase
quelle
2

Bei Tests mit select count(*)sollte die Verwendung effizienter sein, select count(1)da *dadurch alle Spaltendaten gelesen werden können.

Nathan Niesen
quelle
1

select 1 würde in SQL Server funktionieren, nicht sicher über die anderen.

Verwenden Sie Standard-Ansi-SQL, um eine Tabelle zu erstellen und dann von dieser Tabelle abzufragen.

NimChimpsky
quelle
Deckt ansi SQL ab create table?
Martin Smith
ja tut es. Wenn Sie Ansi-Datentypen verwenden. Ich wäre überrascht, wenn "select 1" nicht funktioniert hätte.
NimChimpsky
1

Angenommen, das OP möchte eine Java-Antwort:

Ab JDBC3 / Java 6 gibt es die Methode isValid () , die verwendet werden sollte, anstatt die eigene Methode zu erfinden.

Der Implementierer des Treibers muss eine Art Abfrage für die Datenbank ausführen, wenn diese Methoden-ID aufgerufen wird. Sie als reiner JDBC-Benutzer müssen nicht wissen oder verstehen, was diese Abfrage ist. Sie müssen lediglich darauf vertrauen, dass der Ersteller des JDBC-Treibers seine Arbeit ordnungsgemäß ausgeführt hat.

Peterh
quelle
2
Ich glaube, das OP spricht von einer Validierungsabfrage für die Verbindungspoolkonfiguration eines Containers, nicht programmgesteuert. In der Datei context.xml von Tomcat, in der Sie Ressourcen einrichten, ist beispielsweise eine validierungsabfrage erforderlich, mit der Tomcat eine Verbindung überprüft. Tomcat selbst müsste geändert werden, um isValid () nutzen zu können. Das kann das OP nicht kontrollieren.
Michael
Es ist auch erwähnenswert, dass "der Schöpfer des JDBC-Treibers seine Arbeit ordnungsgemäß ausgeführt hat" nicht wirklich garantiert ist. Ich habe gerade festgestellt, dass weder Postgres, HSQLDB noch H2 sich die Mühe gemacht haben, die Methode zu implementieren, sodass dort immer eine Ausnahme ausgelöst wird.
Akroy
1

Wie wäre es mit

SELECT user()

Ich benutze dies vorher. MySQL, H2 ist in Ordnung, ich kenne andere nicht.

wener
quelle
1

Ich habe gerade herausgefunden, wie schwierig es ist

SELECT 1 FROM DUAL

auch für MaxDB.

Lars Decker
quelle
Dies gibt keine Antwort auf die Frage. Sobald Sie einen ausreichenden Ruf haben, können Sie jeden Beitrag kommentieren . Geben Sie stattdessen Antworten, die nicht vom Fragesteller geklärt werden müssen . - Aus dem Rückblick
Peter Brittain
Ich verstehe es nicht, es erhöht den Wert der akzeptierten Antwort. Wo liegt also das Problem?
Lars Decker
Und wie Sie bereits erwähnt haben: Da ich die akzeptierte Antwort nicht kommentieren kann, habe ich sie hier als Antwort angegeben. Also lieber keinen Beitrag schreiben, obwohl er nur wegen des fehlenden Rufs hilfreich sein könnte?
Lars Decker
TBH, es ist ein enger Anruf ... Anstatt eine Antwort zu duplizieren, sollte dies ein Kommentar zur ursprünglichen Antwort sein. Andernfalls hätten Sie möglicherweise eine vorgeschlagene Änderung am Original vornehmen können.
Peter Brittain
1

Für Oracle ist die leistungsstarke Abfrage

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

Dies ist aus Sicht der Leistung.

Joby Kurian
quelle
0

Ich benutze dies für Firebird

select 1 from RDB$RELATION_FIELDS rows 1
claudsan
quelle
0

Für MSSQL .

Dies half mir festzustellen, ob Verbindungsserver am Leben waren. Verwenden einer Open Query-Verbindung und eines TRY CATCH, um die Ergebnisse des Fehlers auf etwas Nützliches zu übertragen.

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
quelle