Java 对于空的SELECT结果,T-SQL存储过程不会按计数(*)返回零
我使用Spring框架的Java 对于空的SELECT结果,T-SQL存储过程不会按计数(*)返回零,java,sql-server,spring,tsql,stored-procedures,Java,Sql Server,Spring,Tsql,Stored Procedures,我使用Spring框架的StoredProcedure(当然,我正在扩展它)来获得一个结果集和一个输出参数(@totalRowsReturned)。问题是,当返回的resultset应该是一个空列表时,当我尝试检索输出参数(totalRows,我天真地认为它是零)时,会得到一个NullPointerException 我想提到的是,当找到的结果集不是空的时候,代码可以正常工作 我的问题是: List<Book> books = null; int totalRows = 0; Ma
StoredProcedure
(当然,我正在扩展它)来获得一个结果集和一个输出参数(@totalRowsReturned
)。问题是,当返回的resultset应该是一个空列表时,当我尝试检索输出参数(totalRows,我天真地认为它是零)时,会得到一个NullPointerException
我想提到的是,当找到的结果集不是空的时候,代码可以正常工作
我的问题是:
List<Book> books = null;
int totalRows = 0;
Map<String, Object> results = storedProcedure.execute(parameters);
books = (List<Book>) results.get("rs");
totalRows = (Integer) results.get("totalRowsReturned"); // NullPointerException on this line if total rows are supposed to be zero!!
CREATE PROCEDURE Find_Books
@authorName Varchar(250),
@totalRowsReturned INTEGER OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @SelectQuery NVARCHAR(2000)
SET @SelectQuery = 'SELECT @totalRows=COUNT(*) OVER() FROM book b WHERE b.author_name = @authorName'
Execute sp_Executesql @SelectQuery, N'@authorName VARCHAR(250), @totalRows int OUTPUT', @authorName, @totalRows=@totalRowsReturned OUTPUT
-- Select resultset goes here...
END
@totalrowsreturn
设置为零?(如果是,为什么我不能通过Java代码检索它?)@totalRowsReturned
设置为零,并通过Java代码检索它List<Book> books = null;
int totalRows = 0;
Map<String, Object> results = storedProcedure.execute(parameters);
books = (List<Book>) results.get("rs");
totalRows = (Integer) results.get("totalRowsReturned"); // NullPointerException on this line if total rows are supposed to be zero!!
CREATE PROCEDURE Find_Books
@authorName Varchar(250),
@totalRowsReturned INTEGER OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @SelectQuery NVARCHAR(2000)
SET @SelectQuery = 'SELECT @totalRows=COUNT(*) OVER() FROM book b WHERE b.author_name = @authorName'
Execute sp_Executesql @SelectQuery, N'@authorName VARCHAR(250), @totalRows int OUTPUT', @authorName, @totalRows=@totalRowsReturned OUTPUT
-- Select resultset goes here...
END
更新:
List<Book> books = null;
int totalRows = 0;
Map<String, Object> results = storedProcedure.execute(parameters);
books = (List<Book>) results.get("rs");
totalRows = (Integer) results.get("totalRowsReturned"); // NullPointerException on this line if total rows are supposed to be zero!!
CREATE PROCEDURE Find_Books
@authorName Varchar(250),
@totalRowsReturned INTEGER OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @SelectQuery NVARCHAR(2000)
SET @SelectQuery = 'SELECT @totalRows=COUNT(*) OVER() FROM book b WHERE b.author_name = @authorName'
Execute sp_Executesql @SelectQuery, N'@authorName VARCHAR(250), @totalRows int OUTPUT', @authorName, @totalRows=@totalRowsReturned OUTPUT
-- Select resultset goes here...
END
实际上,我的存储过程看起来更像这样(更改是SELECT中的附加@first\u id=b.book\u id
):
问题是,当SELECT没有返回行时,没有定义b.book_id,因此我得到一个org.springframework.dao.TransientDataAccessResourceException。。。“book.book\u id”列在选择列表中无效,因为它未包含在聚合函数或GROUP BY子句中。
因此,似乎如果我保持OVER()
,那么@totalRows=COUNT(*)OVER()
在返回零行时失败,如果我删除OVER()
,那么@first\u id=b.book\u id
失败
你知道我是如何克服这个问题的吗?COUNT(*)OVER()
在这里使用不正确。只需使用COUNT(*)
COUNT(*)OVER()
返回一个结果集,其行数与计数的行数相同。e、 g如果结果为3,则结果集为
3
3
3
然后,查询的效果是重复将值3
重新分配给@totalRows
变量,分配的次数与完全没有意义的行的次数相同
相反,如果COUNT(*)=0
,则()上的COUNT(*)结果集为空,因此您的变量根本不会被赋值
COUNT(*)
将始终在此处为您提供一个单行标量结果集,您可以将该结果集分配给变量,并且将有一个更高效的执行计划,而无需使用不必要的公共子表达式假脱机
编辑
回应您在评论中提出的问题。这与链接文章的作用相同。它可以使用一个更窄的索引来查找(比如)第10000名员工,然后仅为随后的1页记录加入部门。这种分页方法只能正常工作,因为每个员工只有一个部门
WITH E1(RN, EmployeeID)
AS (SELECT ROW_NUMBER() OVER (ORDER BY EmployeeID),
EmployeeID
FROM Employees)
SELECT TOP (@maximumRows) e.*,
d.Name AS DepartmentName
FROM Employees e
INNER JOIN Departments d
ON e.DepartmentID = d.DepartmentID
WHERE EmployeeID >= (SELECT EmployeeID
FROM E1
WHERE RN = @startRowIndex)
ORDER BY e.EmployeeID
COUNT(*)OVER()
在这里使用不正确。只需使用COUNT(*)
COUNT(*)OVER()
返回一个结果集,其行数与计数的行数相同。e、 g如果结果为3,则结果集为
3
3
3
然后,查询的效果是重复将值3
重新分配给@totalRows
变量,分配的次数与完全没有意义的行的次数相同
相反,如果COUNT(*)=0
,则()上的COUNT(*)结果集为空,因此您的变量根本不会被赋值
COUNT(*)
将始终在此处为您提供一个单行标量结果集,您可以将该结果集分配给变量,并且将有一个更高效的执行计划,而无需使用不必要的公共子表达式假脱机
编辑
回应您在评论中提出的问题。这与链接文章的作用相同。它可以使用一个更窄的索引来查找(比如)第10000名员工,然后仅为随后的1页记录加入部门。这种分页方法只能正常工作,因为每个员工只有一个部门
WITH E1(RN, EmployeeID)
AS (SELECT ROW_NUMBER() OVER (ORDER BY EmployeeID),
EmployeeID
FROM Employees)
SELECT TOP (@maximumRows) e.*,
d.Name AS DepartmentName
FROM Employees e
INNER JOIN Departments d
ON e.DepartmentID = d.DepartmentID
WHERE EmployeeID >= (SELECT EmployeeID
FROM E1
WHERE RN = @startRowIndex)
ORDER BY e.EmployeeID
您需要在b.book\u id
上使用聚合函数
您可以使用min()
或者max()
SELECT @first_id = max(b.book_id), @totalRows=COUNT(*) FROM book ...
您需要在b.book\u id
上使用聚合函数
您可以使用min()
或者max()
SELECT @first_id = max(b.book_id), @totalRows=COUNT(*) FROM book ...
我不能100%确定您想要做什么,但我认为是这样的(在这种情况下,您确实不需要动态SQL):
我不能100%确定您想要做什么,但我认为是这样的(在这种情况下,您确实不需要动态SQL):
您的WHERE
和FROM
似乎走错了方向。还有,为什么您要使用COUNT(*)OVER()
而不仅仅是COUNT(*)
?请尝试删除OVER()
。当您使用OVER()
并且没有点击时,您将不会得到任何行。如果没有OVER()
您将始终得到1行。@Martin Smith-对不起-这是我的打字错误。我刚刚纠正了我的问题。正如我上面所说的,该代码对于零行以外的情况工作良好。@rapt。它适用于这些情况,但效率低下。它重复地将相同的值重新分配给变量。您应该只使用COUNT(*)
来获得单行结果。@Mikael Eriksson-我想我没有得到您。当没有点击时,我不想得到任何行:没有点击,这意味着零结果,零行。如果没有OVER()
-当有零行时,我为什么要得到一行那一行是什么?你的WHERE
和FROM
似乎走错了方向。还有,为什么您要使用COUNT(*)OVER()
而不仅仅是COUNT(*)
?请尝试删除OVER()
。当您在上面使用时