在Java应用程序中执行“sp_msforeachdb”
您好StackOverflow社区:)
我来找你分享我的一个问题……
我必须提取SQL Server实例的每个数据库中的每个表的列表 ,我找到了这个查询:
EXEC sp_msforeachdb 'Use ?; SELECT DB_NAME() AS DB, * FROM sys.tables'
它在Microsoft SQL Server Management Studio上运行良好,但是当我尝试在我的Java程序 (包括SQL Server的JDBC驱动程序)中执行它时,它表示它不会返回任何结果 。
我的Java代码如下:
this.statement = this.connect.createStatement(); // Create the statement this.resultats = this.statement.executeQuery("EXEC sp_msforeachdb 'Use ?; SELECT DB_NAME() AS DB, * FROM sys.tables'"); // Execute the query and store results in a ResultSet this.sortie.ecrireResultats(this.statement.getResultSet()); // Write the ResultSet to a file
感谢任何想要帮助我的人, 祝你有愉快的一天:)
编辑1:
我不确定SQL Server的JDBC驱动程序是否支持我的查询,因此我将尝试以另一种方式实现我的目标。
我想要得到的是SQL Server实例上每个数据库的所有表的列表 ,输出格式如下:
+-----------+--------+ | Databases | Tables | +-----------+--------+
所以现在我问有人可以帮助我通过Java的JDBC for SQL Server驱动程序使用SQL查询来获得该解决方案。
我还要感谢Tim Lehner和Mark Rotteveel提供的非常快速的答案。
如果一个语句不能返回多个结果,那么你不应该使用executeQuery
,而应该使用execute()
,这个方法返回一个boolean
指示第一个结果的类型:
-
true
:result是ResultSet
-
false
:结果是更新计数
如果结果为true
,则使用getResultSet()
来检索ResultSet
,否则使用getUpdateCount()
来检索更新计数。 如果更新计数为-1
则表示没有更多结果。 请注意,当前结果为ResultSet
时,更新计数也将为-1
。 如果没有更多结果或者结果是更新计数,那么知道getResultSet()
应该返回null也是很好的。
现在,如果要检索更多结果,可以调用getMoreResults()
(或其兄弟接受int
参数)。 boolean
的返回值与execute()
的返回值具有相同的含义,因此false
并不意味着没有更多的结果!
如果getMoreResults()
返回false并且getUpdateCount()
返回-1
则只有不再有结果(如Javadoc中所述)
从本质上讲,这意味着如果您想要正确处理所有结果,您需要执行以下操作。 请注意,我实际上没有尝试使用您的语句,也不确定SQL Server JDBC驱动程序是否正确实现了多个结果,因此它可能不起作用:
boolean result = stmt.execute(...); while(true) if (result) { ResultSet rs = stmt.getResultSet(); // Do something with resultset ... } else { int updateCount = stmt.getUpdateCount(); if (updateCount == -1) { // no more results break; } // Do something with update count ... } result = stmt.getMoreResults(); }
注意:这个答案的一部分是基于我对Java SQL的回答:Statement.hasResultSet()?
如果您没有收到错误,可能有一个问题是sp_msforeachdb
将为每个数据库返回一个单独的结果集,而不是一个包含所有记录的结果集。 在这种情况下,您可以尝试使用一些动态SQL来合并所有行:
-- Use sys.tables declare @sql nvarchar(max) select @sql = coalesce(@sql + ' union all ', '') + 'select ''' + quotename(name) + ''' as database_name, * from ' + quotename(name) + '.sys.tables' from sys.databases select @sql = @sql + ' order by database_name, name' exec sp_executesql @sql
我有时也会使用INFORMATION_SCHEMA视图,因为它更容易看到模式名称,其中包括:
-- Use INFORMATION_SCHEMA.TABLES to easily get schema name declare @sql nvarchar(max) select @sql = coalesce(@sql + ' union all ', '') + 'select * from ' + quotename(name) + '.INFORMATION_SCHEMA.TABLES where TABLE_TYPE = ''BASE TABLE''' from sys.databases select @sql = @sql + ' order by TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME' exec sp_executesql @sql
请注意,这种字符串连接方法( select @sql = foo from bar
) 可能无法正常工作,因为您打算通过链接服务器 (它只会获取最后一条记录)。 只是一个小小的警告。
UPDATE
我找到了解决方案!
在阅读了一篇关于sp_spaceused与Java一起使用的文章后,我发现我的情况与此相同。
我的最终代码如下:
this.instances = instances; for(int i = 0 ; i < this.instances.size() ; i++) { try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); this.connect = DriverManager.getConnection("jdbc:sqlserver://" + this.instances.get(i), "tluser", "result"); this.statement = this.connect.prepareCall("{call sp_msforeachdb(?)}"); this.statement.setString(1, "Use ?; SELECT DB_NAME() AS DB, name FROM sys.tables WHERE DB_NAME() NOT IN('master', 'model', 'msdb', 'tempdb')"); this.resultats = this.statement.execute(); while(true) { int rowCount = this.statement.getUpdateCount(); if(rowCount > 0) { this.statement.getMoreResults(); continue; } if(rowCount == 0) { this.statement.getMoreResults(); continue; } ResultSet rs = this.statement.getResultSet(); if(rs != null) { while (rs.next()) { this.sortie.ecrireResultats(rs); // Write the results to a file } rs.close(); this.statement.getMoreResults(); continue; } break; } this.statement.close(); } catch(Exception e) { e.printStackTrace(); } }
它试了一下,我的文件里面有我想要的一切。
感谢大家的帮助 ! 🙂