Эффективный тестовый запрос SQL или запрос проверки, который будет работать во всех (или в большинстве) базах данных

148

Многие библиотеки пулов соединений с базами данных предоставляют возможность проверить свои соединения SQL на бездействие. Например, у библиотеки пулов JDBC c3p0 есть свойство под названием preferredTestQuery, которое выполняется в соединении через заданные интервалы. Аналогично, у Apache Commons DBCP есть validationQuery.

Многие примеры запросов, которые я видел, предназначены для MySQL и рекомендуют использовать SELECT 1;в качестве значения для тестового запроса. Однако этот запрос не работает в некоторых базах данных (например, HSQLDB, для которого SELECT 1ожидается FROMпредложение).

Существует ли независимый от базы данных запрос, который эквивалентно эффективен, но будет работать для всех баз данных SQL?

Редактировать:

Если нет (что, кажется, имеет место), может ли кто-нибудь предложить набор SQL-запросов, которые будут работать для различных поставщиков баз данных? Мое намерение состояло бы в том, чтобы программно определить оператор, который я могу использовать, основываясь на конфигурации моего поставщика базы данных.

Роб Хруска
источник
1
Примечание: настройка тестового запроса больше не нужна, см. Мой ответ ниже
Тим Бют

Ответы:

274

После небольшого исследования вместе с помощью некоторых ответов здесь:

SELECT 1


SELECT 1 FROM DUAL

  • оракул

SELECT 1 FROM any_existing_table WHERE 1=0

или

SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS

  • HSQLDB (протестировано с версией 1.8.0.10)

    Примечание: я попытался использовать WHERE 1=0предложение для второго запроса, но он не работал как значение для DBCP Apache Commons validationQuery, так как запрос не возвращает никаких строк


VALUES 1 или SELECT 1 FROM SYSIBM.SYSDUMMY1

  • Apache Derby (через daiscog )

SELECT 1 FROM SYSIBM.SYSDUMMY1

  • DB2

select count(*) from systables

  • Informix
Роб Хруска
источник
Это должно быть «SELECT 1 FROM any_existing_table WHERE 1 = 0» - иначе вызов может быть очень медленным. Кстати, и SELECT 1, и SELECT 1 FRU DUAL также работают с H2.
Томас Мюллер
2
Я знаю, что это пара лет, но вы можете добавить оба VALUES 1и SELECT 1 FROM SYSIBM.SYSDUMMY1для Apache Derby
daiscog
Предполагая, что OP хочет получить ответ Java: я считаю, что в Java 6 этот ответ устарел. Смотрите мой ответ в другом месте на этой странице.
Петер
Вы можете добавить эти два к своему ответу, DB2: «ВЫБЕРИТЕ текущую дату ОТ sysibm.sysdummy1» Informix: «выберите количество (*) из systables»
Майкл
@ Майкл Если вы хотите предложить редактирование, я бы одобрил это. Плюс, вы получите пару очков за это.
Роб Хруска,
22

Если ваш драйвер совместим с JDBC 4, то для проверки соединений нет необходимости в специальном запросе. Вместо этого есть Connection.isValid для проверки соединения.

JDBC 4 является частью Java 6 с 2006 года, и ваш драйвер должен уже поддерживать это!

Известные пулы соединений, такие как HikariCP, все еще имеют параметр config для указания тестового запроса, но настоятельно не рекомендуют его использовать:

🔠connectionTestQuery

Если ваш драйвер поддерживает JDBC4, мы настоятельно рекомендуем не устанавливать это свойство. Это для "устаревших" баз данных, которые не поддерживают API JDBC4 Connection.isValid (). Это запрос, который будет выполнен непосредственно перед тем, как вам будет дано соединение из пула, чтобы проверить, что соединение с базой данных еще живо. Снова, попробуйте запустить пул без этого свойства, HikariCP будет регистрировать ошибку, если ваш драйвер не совместим с JDBC4, чтобы сообщить вам. По умолчанию: нет

Тим Бюте
источник
9

К сожалению, нет оператора SELECT, который всегда будет работать независимо от базы данных.

Большинство баз данных поддерживают:

SELECT 1

Некоторые базы данных не поддерживают это, но имеют таблицу с именем DUAL, которую вы можете использовать, когда вам не нужна таблица:

SELECT 1 FROM DUAL

MySQL также поддерживает это по причинам совместимости, но не все базы данных поддерживают. Обходной путь для баз данных, которые не поддерживают ни один из вышеперечисленных, заключается в создании таблицы с именем DUAL, содержащей одну строку, тогда вышеприведенное будет работать.

HSQLDB не поддерживает ничего из вышеперечисленного, поэтому вы можете либо создать таблицу DUAL, либо использовать:

SELECT 1 FROM any_table_that_you_know_exists_in_your_database
Марк Байерс
источник
Спасибо за ответ. Я немного обновил свой вопрос из-за вашего заявления "нет оператора SELECT, который всегда будет работать". SELECT 1 FROM DUALтакже не работает с HSQLDB.
Роб Хруска
1
+1, это то, куда я пришел с моими исследованиями, особенно для случая HSQLDB.
Роб Хруска
какие из них не поддерживают "выберите 1"? Выбрать из двойного работает только оракул, не так ли? Не SQL Server или MySQL по крайней мере
NimChimpsky
+1 Я перестал пытаться придумать независимый способ СУРБД!
Мартин Смит,
2

Я использую это:

select max(table_catalog) as x from information_schema.tables

проверить соединение и возможность выполнения запросов (в результате 1 строка) для postgreSQL, MySQL и MSSQL.

Wojciechk
источник
2

я использую

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

для hsqldb 1.8.0

thinkbase
источник
2

Для использования тестов select count(*), он должен быть более эффективным, select count(1)поскольку *может заставить его читать все данные столбца.

Натан Нисен
источник
1

select 1 будет работать в SQL Server, не уверен в других.

Используйте стандартный ANSI SQL, чтобы создать таблицу, а затем запросить из этой таблицы.

ним чимпский
источник
Покрывает ли ANSI SQL create table?
Мартин Смит
Да, это так. Если вы используете типы данных ANSI. Я был бы удивлен, если бы "выбор 1" не работал все же.
НимЧимпский
1

Предполагая, что ОП хочет ответ Java:

Начиная с JDBC3 / Java 6 есть метод isValid (), который следует использовать, а не изобретать собственный метод.

Разработчик драйвера должен выполнить какой-то запрос к базе данных при вызове этого метода с идентификатором. Вы - как простой пользователь JDBC - не должны знать или понимать, что это за запрос. Все, что вам нужно сделать, это поверить, что создатель драйвера JDBC выполнил свою работу должным образом.

peterh
источник
2
Я полагаю, что OP говорит о проверочном запросе для конфигурации пула соединений Контейнера, а не программно. Например, в файле context.xml Tomcat, где вы настраиваете ресурсы, он принимает запрос validationQuery, который Tomcat использует для проверки соединения. Сам Tomcat должен быть изменен, чтобы использовать isValid (). Это не то, что ОП может контролировать.
Майкл
Также стоит отметить, что «создатель драйвера JDBC выполнил свою работу должным образом» на самом деле не гарантируется. Я только что обнаружил, что ни Postgres, ни HSQLDB, ни H2 не потрудились реализовать метод, поэтому он всегда будет вызывать исключение.
akroy
1

Как насчет

SELECT user()

Я использую это раньше. MySQL, H2 в порядке, я не знаю других.

Wener
источник
1

Просто выяснил трудный путь, что это

SELECT 1 FROM DUAL

и для MaxDB.

Ларс Декер
источник
Это не дает ответа на вопрос. Как только у вас будет достаточно репутации, вы сможете комментировать любой пост ; вместо этого предоставьте ответы, которые не требуют разъяснений от автора . - Из обзора
Питер Бриттен
Я не понимаю, это добавляет ценность принятому ответу, так в чем же проблема?
Ларс Декер
И как вы упомянули: поскольку я не могу прокомментировать принятый ответ, поэтому я поставил его здесь как ответ. Так что лучше не писать пост, хотя это может быть полезно только из-за отсутствия репутации?
Ларс Декер
TBH, это близкий звонок ... Вместо того, чтобы дублировать ответ, это должен был быть комментарий к оригинальному ответу. Если это не удастся, вы могли бы предложить изменить оригинал.
Питер Бриттен
1

Для Oracle высокопроизводительный запрос будет

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

Это с точки зрения производительности.

Джоби Курьян
источник
0

Я использую это для Firebird

select 1 from RDB$RELATION_FIELDS rows 1
claudsan
источник
0

Для MSSQL .

Это помогло мне определить, живы ли связанные серверы. Использование соединения Open Query и TRY CATCH, чтобы поместить результаты ошибки в нечто полезное.

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
источник