适用于所有(或大多数)数据库的高效 SQL 测试查询或验证查询

Posted

技术标签:

【中文标题】适用于所有(或大多数)数据库的高效 SQL 测试查询或验证查询【英文标题】:Efficient SQL test query or validation query that will work across all (or most) databases 【发布时间】:2011-04-09 18:29:48 【问题描述】:

许多数据库连接池库都提供了测试其 SQL 连接是否空闲的能力。例如,JDBC 池库c3p0 有一个名为preferredTestQuery 的属性,它以配置的时间间隔在连接上执行。同样,Apache Commons DBCP 有validationQuery

我看到的许多example queries 都是针对mysql 的,建议使用SELECT 1; 作为测试查询的值。但是,此查询不适用于某些数据库(例如 HSQLDB,SELECT 1 需要 FROM 子句)。

是否有一个与数据库无关的查询同样有效但适用于所有 SQL 数据库?

编辑:

如果没有(似乎是这样),有人可以建议一组适用于各种数据库提供商的 SQL 查询吗?我的意图是根据我的数据库提供程序配置以编程方式确定我可以使用的语句。

【问题讨论】:

另见Simple DB2 Query for connection validation。 注意:不再需要配置测试查询,请参阅下面的my answer 【参考方案1】:

经过一些研究以及这里的一些答案的帮助:

SELECT 1

H2 MySQL Microsoft SQL Server(根据NimChimpsky) PostgreSQL SQLite 蜂巢

SELECT 1 FROM DUAL

甲骨文

SELECT 1 FROM any_existing_table WHERE 1=0

SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS

CALL NOW()

HSQLDB(使用版本 1.8.0.10 测试)

注意:我尝试在第二个查询中使用 WHERE 1=0 子句,但它不能作为 Apache Commons DBCP 的 validationQuery 的值,因为查询不返回任何行


VALUES 1SELECT 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 FROM DUAL 也适用于 H2。 假设 OP 想要一个 Java 答案:我相信对于 Java 6,这个答案现在已经过时了。在此页面的其他地方查看我的答案。 @RobHruska,您的回答也帮助解决了我的问题“无法验证新建立的连接”。 ***.com/questions/33100423/… 对于oracle,使用SELECT 1 from DUAL; @RobHruska 我不得不为 DB2 使用 SELECT 1 FROM SYSIBM/SYSDUMMY1。 source(抱歉是德语)。我在使用点表示法时收到的错误消息类似于:[SQL5016] Qualified Objectname SYSDUMMY1 not valid【参考方案2】:

如果您的驱动程序符合 JDBC 4,则无需专门的查询来测试连接。相反,有Connection.isValid 来测试连接。

JDBC 4 是 2006 年 Java 6 的一部分,您的驱动程序现在应该支持它!

著名的连接池,例如 HikariCP,仍然有一个用于指定测试查询的配置参数,但强烈建议不要使用它:

?connectionTestQuery

如果您的驱动程序支持 JDBC4,我们强烈 建议不要设置此属性。这适用于“遗留”数据库 不支持 JDBC4 Connection.isValid() API。这是 将在为您提供连接之前执行的查询 从池中验证与数据库的连接是否仍然存在 活。再次尝试在没有此属性的情况下运行池 HikariCP 如果您的驱动程序不兼容 JDBC4 将记录一个错误以让您 知道。默认值:无

【讨论】:

【参考方案3】:

不幸的是,没有任何 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,这也是我的研究成果,尤其是 HSQLDB 案例。 哪些不支持“select 1”?从双重中选择仅适用于 oracle 不是吗?不是 sql server,或者至少是 mysql +1 我已经放弃尝试独立于 RDBMS 的方式了!【参考方案4】:

jOOQ manual's section about the DUAL table 为 jOOQ 的 select(inline(1)) 查询列出以下内容:

-- Access
SELECT 1 FROM (SELECT count(*) dual FROM MSysResources) AS dual

-- BigQuery, CockroachDB, Exasol, H2, Ignite, MariaDB, MySQL, PostgreSQL, 
-- Redshift, Snowflake, SQLite, SQL Server, Sybase ASE, Vertica
SELECT 1

-- MemSQL, Oracle
SELECT 1 FROM DUAL

-- CUBRID
SELECT 1 FROM db_root

-- Db2
SELECT 1 FROM SYSIBM.DUAL

-- Derby
SELECT 1 FROM SYSIBM.SYSDUMMY1

-- Firebird
SELECT 1 FROM RDB$DATABASE

-- HANA, Sybase SQL Anywhere
SELECT 1 FROM SYS.DUMMY

-- HSQLDB
SELECT 1 FROM (VALUES(1)) AS dual(dual)

-- Informix
SELECT 1 FROM (SELECT 1 AS dual FROM systables WHERE (tabid = 1)) AS dual

-- Ingres, Teradata
SELECT 1 FROM (SELECT 1 AS "dual") AS "dual"

【讨论】:

【参考方案5】:

我用这个:

select max(table_catalog) as x from information_schema.tables

检查连接和运行 postgreSQL、MySQL 和 MSSQL 查询的能力(结果为 1 行)。

【讨论】:

【参考方案6】:

我用

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

对于 hsqldb 1.8.0

【讨论】:

【参考方案7】:

对于使用select count(*) 的测试,使用select count(1) 应该更有效,因为* 会导致它读取所有列数据。

【讨论】:

COUNT(*) 中的星号没有这种作用。如果优化器不是太奇怪的话,两者的行为实际上应该完全相同,see this blog post【参考方案8】:

select 1 可以在 sql server 中工作,其他的就不清楚了。

使用标准 ansi sql 创建表,然后从该表中查询。

【讨论】:

ansi SQL 是否覆盖create table 是的。如果您使用 ansi 数据类型。如果“选择 1”不起作用,我会感到惊讶。【参考方案9】:

假设 OP 想要一个 Java 答案:

从 JDBC3 / Java 6 开始,应该使用 isValid() 方法,而不是发明自己的方法。

当调用此方法 id 时,驱动程序的实现者需要对数据库执行某种查询。您——作为一个单纯的 JDBC 用户——不必知道或理解这个查询是什么。您所要做的就是相信 JDBC 驱动程序的创建者已经正确地完成了他/她的工作。

【讨论】:

我相信 OP 是在讨论容器连接池配置的验证查询,而不是以编程方式。例如,在设置资源的 Tomcat 的 context.xml 中,它需要一个验证查询,Tomcat 使用该验证查询来验证连接。必须更改 Tomcat 本身才能利用 isValid()。这不是 OP 可以控制的。 还值得注意的是,“JDBC 驱动程序的创建者已正确完成他/她的工作”并不能真正得到保证。我刚刚发现 Postgres、HSQLDB 和 H2 都没有费心去实现这个方法,所以它总是会在那里引发异常。【参考方案10】:

怎么样

SELECT user()

我以前用过这个。MySQL,H2还可以,其他的不知道。

【讨论】:

【参考方案11】:

刚刚发现它是困难的方式

SELECT 1 FROM DUAL

对于 MaxDB 也是如此。

【讨论】:

这并没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review 我不明白,它为接受的答案增加了价值,那么问题出在哪里? 正如你所提到的:因为我无法评论接受的答案,所以我把它作为答案放在这里。所以最好不要写帖子,尽管它可能会因为失去声誉而有所帮助? TBH,这是一个近距离的电话......而不是重复一个答案,这应该是对原始答案的评论。如果做不到这一点,您可以对原件进行建议的编辑。【参考方案12】:

对于 Oracle,高性能查询将是

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

这是从性能的角度来看的。

【讨论】:

【参考方案13】:

我将它用于 Firebird

select 1 from RDB$RELATION_FIELDS rows 1

【讨论】:

【参考方案14】:

对于 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

【讨论】:

以上是关于适用于所有(或大多数)数据库的高效 SQL 测试查询或验证查询的主要内容,如果未能解决你的问题,请参考以下文章

Python教程

使用 SQL Server 后端访问前端,适用于某些机器,而不适用于其他机器

SQL简介

sql 以下SQL脚本将检索给定数据库的所有“唯一”约束的列表。这适用于SQL Server。

sql 2008中大表的高效分页

高效的 SQL 查询或索引来查找所有列是不是只有 1 个值