Ефективний тестовий запит SQL або запит на перевірку, який буде працювати в усіх (або більшості) баз даних


148

Багато бібліотек об’єднання підключень до баз даних надають можливість перевірити свої SQL-з'єднання на непрацюючість. Наприклад, бібліотека пулу JDBC c3p0 має властивість під назвою preferredTestQuery, яка виконується під час з'єднання з налаштованими інтервалами. Аналогічно є і DBCP Apache Commons validationQuery.

Я бачив багато прикладів запитів для MySQL і рекомендую використовувати SELECT 1;як значення для тестового запиту. Однак цей запит не працює в деяких базах даних (наприклад, HSQLDB, для яких SELECT 1очікує FROMпункт).

Чи є аггностичний запит до бази даних, який настільки ж ефективний, але буде працювати для всіх баз даних SQL?

Редагувати:

Якщо цього немає (що, здається, так), може хтось запропонувати набір SQL запитів, які працюватимуть для різних постачальників баз даних? Моїм наміром було б програмне визначення твердження, яке я можу використовувати, на основі конфігурації мого постачальника баз даних.



1
Примітка: налаштування тестового запиту більше не потрібна, дивіться мою відповідь нижче
Tim Büthe

Відповіді:


274

Після невеликих досліджень, а також довідки з деяких відповідей тут:

SELECT 1

  • Н2
  • MySQL
  • Microsoft SQL Server (за словами NimChimpsky )
  • PostgreSQL
  • SQLite

SELECT 1 FROM DUAL

  • Oracle

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

SELECT 1 FROM SYSIBM.SYSDUMMY1

  • DB2

select count(*) from systables

  • Інформукс

Це повинно бути "SELECT 1 FROM any_existing_table WHERE 1 = 0" - інакше дзвінок може бути дуже повільним. До речі, і SELECT 1, і SELECT 1 OF DUAL також працюють з H2.
Томас Мюллер

2
Я знаю, що це пару років, але ви можете додати і те, VALUES 1і SELECT 1 FROM SYSIBM.SYSDUMMY1для Apache Derby
daiscog

Якщо припустити, що ОП хоче відповідь Java: я вважаю, що в Java 6 ця відповідь застаріла. Дивіться мою відповідь в іншому місці на цій сторінці.
peterh

Ви можете додати ці два у свою відповідь, DB2: "ВИБІРТЕ поточну дату Із sysibm.sysdummy1" Informix: "виберіть кількість (*) з systables"
Майкл

@Michael Якщо ви хочете запропонувати редагування, я би схвалив його. Крім того, ви отримаєте пару реп-балів за це.
Роб Грушка

22

Якщо ваш драйвер сумісний з JDBC 4, для тестування з'єднань не потрібен спеціальний запит. Натомість існує Connection.isValid для перевірки з'єднання.

JDBC 4 є частиною Java 6 з 2006 року, і ви, водій, повинні підтримувати це вже зараз!

У відомих пулах підключень, як-от HikariCP, як і раніше є конфігураційний параметр для визначення тестового запиту, але сильно не рекомендується використовувати його:

.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"? Вибрати з подвійного працює лише oracle, чи не так? Не sql-сервер, або mysql щонайменше
NimChimpsky

+1 Я відмовився намагатися думати про незалежний спосіб RDBMS!
Мартін Сміт

2

Я використовую цей:

select max(table_catalog) as x from information_schema.tables

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



2

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


1

select 1 працював би на сервері sql, не впевнений у інших.

Використовуйте стандартний ansi sql для створення таблиці, а потім запит із цієї таблиці.


Чи охоплює ansi SQL create table?
Мартін Сміт

так. Якщо ви використовуєте типи даних ansi. Я був би здивований, якби "вибрати 1" не працював.
NimChimpsky

1

Припустимо, що ОП хоче відповідь Java:

Станом на JDBC3 / Java 6 є метод isValid (), який слід використовувати, а не вигадувати власний метод.

Реалізатору драйвера потрібно виконати якийсь запит до бази даних, коли цей метод ідентифікується. Ви - як звичайний користувач JDBC - не повинні знати чи розуміти, що це за запит. Все, що вам потрібно зробити, - це довіритися, що творець драйвера JDBC виконав свою роботу належним чином.


2
Я вважаю, що ОП говорить про запит перевірки для конфігурації пулу підключення контейнера, а не програмно. Наприклад, у kontekst.xml Tomcat, де ви налаштовуєте Resources, потрібний валідаційний запит, який Tomcat використовує для перевірки з'єднання. Сам Tomcat доведеться змінити, щоб скористатися isValid (). Це не те, що може контролювати ОП.
Майкл

Варто також зазначити, що "творець драйвера JDBC правильно виконав свою роботу" насправді не гарантується. Щойно я виявив, що ні Postgres, HSQLDB, ні H2 не намагаються реалізувати метод, тому він завжди створюватиме виняток.
akroy

1

Як щодо

SELECT user()

Я використовую це раніше.MySQL, H2 добре, я не знаю інших.


1

Щойно з’ясував важкий шлях, який це є

SELECT 1 FROM DUAL

і для MaxDB.


Це не дає відповіді на запитання. Коли у вас буде достатня репутація, ви зможете коментувати будь-яку публікацію ; натомість надайте відповіді, які не потребують уточнення від запитувача . - З огляду
Пітер Брітайн

Я не розумію, це додає значення прийнятій відповіді, тож де проблема?
Ларс Декер

І як ви вже згадували: як я не можу коментувати прийняту відповідь, так я ставлю це як відповідь тут. Тож краще не писати повідомлення, хоча це може бути корисним лише через відсутність репутації?
Ларс Декер

TBH, це близький дзвінок ... Замість того, щоб дублювати відповідь, це повинен був бути коментарем до оригінальної відповіді. Якщо цього не вдалося, ви могли зробити запропоновану редакцію оригіналу.
Пітер Брітайн

1

Для Oracle високоефективним запитом буде

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

Це з точки зору продуктивності.



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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.