Requête de test SQL ou requête de validation efficace qui fonctionnera sur toutes (ou la plupart) des bases de données

148

De nombreuses bibliothèques de regroupement de connexions de base de données offrent la possibilité de tester leurs connexions SQL pour l'inactivité. Par exemple, la bibliothèque de regroupement JDBC c3p0 a une propriété appelée preferredTestQuery, qui est exécutée sur la connexion à des intervalles configurés. De même, Apache Commons DBCP a validationQuery.

De nombreux exemples de requêtes que j'ai vus concernent MySQL et recommandent de l'utiliser SELECT 1;comme valeur pour la requête de test. Cependant, cette requête ne fonctionne pas sur certaines bases de données (par exemple HSQLDB, pour qui SELECT 1attend une FROMclause).

Existe-t-il une requête indépendante de la base de données qui est tout aussi efficace mais fonctionnera pour toutes les bases de données SQL?

Éditer:

S'il n'y en a pas (ce qui semble être le cas), quelqu'un peut-il suggérer un ensemble de requêtes SQL qui fonctionneront pour divers fournisseurs de bases de données? Mon intention serait de déterminer par programme une instruction que je peux utiliser en fonction de la configuration de mon fournisseur de base de données.

Rob Hruska
la source
1
Remarque: la configuration d'une requête de test n'est plus nécessaire, voir ma réponse ci
Tim Büthe

Réponses:

274

Après un peu de recherche avec l'aide de certaines des réponses ici:

SELECT 1

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

SELECT 1 FROM DUAL

  • Oracle

SELECT 1 FROM any_existing_table WHERE 1=0

ou

SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS

  • HSQLDB (testé avec la version 1.8.0.10)

    Remarque: j'ai essayé d'utiliser une WHERE 1=0clause sur la deuxième requête, mais cela ne fonctionnait pas comme valeur pour Apache Commons DBCP validationQuery, car la requête ne renvoie aucune ligne


VALUES 1 ou SELECT 1 FROM SYSIBM.SYSDUMMY1

SELECT 1 FROM SYSIBM.SYSDUMMY1

  • DB2

select count(*) from systables

  • Informix
Rob Hruska
la source
Cela devrait être "SELECT 1 FROM any_existing_table WHERE 1 = 0" - sinon l'appel pourrait être très lent. À propos, SELECT 1 et SELECT 1 FROM DUAL fonctionnent également avec H2.
Thomas Mueller
2
Je sais que cela date de quelques années, mais vous voudrez peut-être ajouter les deux VALUES 1et SELECT 1 FROM SYSIBM.SYSDUMMY1pour Apache Derby
daiscog
En supposant qu'OP veut une réponse Java: je pense qu'avec Java 6, cette réponse est désormais obsolète. Voir ma réponse ailleurs sur cette page.
peterh
Vous pouvez ajouter ces deux à votre réponse, DB2: "SELECT date actuelle FROM sysibm.sysdummy1" Informix: "select count (*) from systables"
Michael
@Michael Si vous voulez suggérer une modification, je l'approuverais. De plus, vous obtiendrez quelques points de rep.
Rob Hruska
22

Si votre pilote est compatible JDBC 4, aucune requête dédiée n'est nécessaire pour tester les connexions. Au lieu de cela, il y a Connection.isValid pour tester la connexion.

JDBC 4 fait partie de Java 6 à partir de 2006 et votre pilote devrait le supporter maintenant!

Les pools de connexions célèbres, comme HikariCP, ont toujours un paramètre de configuration pour spécifier une requête de test, mais déconseillent fortement de l'utiliser:

🔠connexionTestQuery

Si votre pilote prend en charge JDBC4, nous vous recommandons vivement de ne pas définir cette propriété. Cela concerne les bases de données "héritées" qui ne prennent pas en charge l'API JDBC4 Connection.isValid (). Il s'agit de la requête qui sera exécutée juste avant qu'une connexion ne vous soit donnée depuis le pool pour valider que la connexion à la base de données est toujours active. Encore une fois, essayez d'exécuter le pool sans cette propriété, HikariCP enregistrera une erreur si votre pilote n'est pas compatible JDBC4 pour vous en informer. Par défaut: aucun

Tim Büthe
la source
9

Malheureusement, aucune instruction SELECT ne fonctionnera toujours quelle que soit la base de données.

La plupart des bases de données prennent en charge:

SELECT 1

Certaines bases de données ne prennent pas en charge cela mais ont une table appelée DUAL que vous pouvez utiliser lorsque vous n'avez pas besoin d'une table:

SELECT 1 FROM DUAL

MySQL prend également en charge cela pour des raisons de compatibilité, mais toutes les bases de données ne le font pas. Une solution de contournement pour les bases de données qui ne prennent en charge aucun des éléments ci-dessus consiste à créer une table appelée DUAL qui contient une seule ligne, puis ce qui précède fonctionnera.

HSQLDB ne prend en charge ni l'un ni l'autre des éléments ci-dessus, vous pouvez donc soit créer la table DUAL, soit utiliser:

SELECT 1 FROM any_table_that_you_know_exists_in_your_database
Mark Byers
la source
Merci d'avoir répondu. J'ai légèrement mis à jour ma question en raison de votre déclaration "il n'y a pas d'instruction SELECT qui fonctionnera toujours". SELECT 1 FROM DUALne fonctionne pas non plus avec HSQLDB.
Rob Hruska
1
+1, c'est à peu près là où je suis venu avec mes recherches, en particulier pour le cas HSQLDB.
Rob Hruska
lesquels ne prennent pas en charge "select 1"? Sélectionnez parmi les doubles ne fonctionne que Oracle, n'est-ce pas? Pas de serveur SQL, ou au moins mysql
NimChimpsky
+1 J'ai renoncé à essayer de penser à une méthode indépendante du SGBDR!
Martin Smith
2

J'utilise celui-ci:

select max(table_catalog) as x from information_schema.tables

pour vérifier la connexion et la capacité à exécuter des requêtes (avec 1 ligne comme résultat) pour postgreSQL, MySQL et MSSQL.

Wojciechk
la source
2

j'utilise

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

pour hsqldb 1.8.0

thinkbase
la source
2

Pour les tests utilisant select count(*), il devrait être plus efficace à utiliser select count(1)car il *peut le faire lire toutes les données de la colonne.

Nathan Niesen
la source
1

select 1 fonctionnerait dans le serveur SQL, pas sûr des autres.

Utilisez la norme ansi sql pour créer une table, puis interrogez cette table.

NimChimpsky
la source
Est-ce que ansi SQL couvre create table?
Martin Smith
Oui. Si vous utilisez des types de données ansi. Je serais surpris si "select 1" ne fonctionnait pas.
NimChimpsky
1

En supposant que l'OP souhaite une réponse Java:

Depuis JDBC3 / Java 6, il existe la méthode isValid () qui devrait être utilisée plutôt que d'inventer sa propre méthode.

L'implémenteur du pilote doit exécuter une sorte de requête sur la base de données lorsque cette méthode est appelée. En tant que simple utilisateur JDBC, vous n'avez pas besoin de savoir ou de comprendre ce qu'est cette requête. Tout ce que vous avez à faire est de vous assurer que le créateur du pilote JDBC a fait son travail correctement.

Peterh
la source
2
Je crois que l'OP parle d'une requête de validation pour la configuration du pool de connexions d'un conteneur, pas par programme. Par exemple, dans le fichier context.xml de Tomcat, où vous configurez les ressources, il faut une requête de validation que Tomcat utilise pour valider une connexion. Tomcat lui-même devrait être changé pour profiter de isValid (). Ce n'est pas quelque chose que l'OP peut contrôler.
Michael
Il est également intéressant de noter que "le créateur du pilote JDBC a fait son travail correctement" n'est pas vraiment garanti. Je viens de constater que ni Postgres, HSQLDB, ni H2 ne se sont donné la peine d'implémenter la méthode, donc cela soulèvera toujours une exception.
akroy
1

Que diriez-vous

SELECT user()

J'utilise ça avant. MySQL, H2 est OK, je ne connais pas les autres.

Wener
la source
1

Je viens de découvrir à la dure que c'est

SELECT 1 FROM DUAL

pour MaxDB également.

Lars Decker
la source
Cela ne répond pas à la question. Une fois que vous aurez une réputation suffisante, vous pourrez commenter n'importe quel message ; à la place, fournissez des réponses qui ne nécessitent pas de clarification de la part du demandeur . - De l'avis
Peter Brittain
Je ne comprends pas, cela ajoute de la valeur à la réponse acceptée, alors où est le problème?
Lars Decker
Et comme vous l'avez mentionné: comme je ne peux pas commenter la réponse acceptée, je la mets ici comme réponse. Alors mieux vaut ne pas écrire un article, même si cela pourrait être utile simplement en raison d'une réputation manquante?
Lars Decker
TBH, c'est un appel rapproché ... Plutôt que de dupliquer une réponse, cela aurait dû être un commentaire sur la réponse originale. À défaut, vous auriez pu apporter une suggestion de modification à l'original.
Peter Brittain
1

Pour Oracle, la requête la plus performante sera

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

C'est du point de vue de la performance.

Joby Kurian
la source
0

J'utilise ceci pour Firebird

select 1 from RDB$RELATION_FIELDS rows 1
Claudsan
la source
0

Pour MSSQL .

Cela m'a aidé à déterminer si les serveurs liés étaient actifs. Utilisation d'une connexion Open Query et d'un TRY CATCH pour mettre les résultats de l'erreur en quelque chose d'utile.

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
la source