在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(); } } 

它试了一下,我的文件里面有我想要的一切。

感谢大家的帮助 ! 🙂