保证表值函数结果的顺序

时间:2011-08-19 01:21:12

标签: sql sql-server function user-defined-functions

PREMISE :无法更改应用程序代码。条件非常具体。我正在寻找书籍上的东西,如果可能的话,这是最后的解决方法。

我有一个表值函数(内联),可生成2到7条记录。有时它可能只有1或最多15(但很少)。

该函数仅以这种方式由应用程序使用,没有任何ORDER BY。

select * from dbo.myfunction(...)

根据您的经验,是否有任何方式保证确保(根据您使用特定技术观察到的)第二列按顺序返回结果?列是:varchar(3),datetime,varchar(50)。 不要让我开始选择* ,它是 INTENTIONAL ,这样前端会显示很多列,我将来会显示该功能。

根据经验,使用单个索引(群集PK)来遍历数据,任何当前版本的SQL Server和SP级别都应始终对< 20条记录执行简单的INDEX SCAN而不进行并行操作,从而让我订购导致应用程序选择。

你的想法?我更愿意将理论排除在讨论之外。如果你能坚持实践经验并在家里继续讲述最佳实践,我也会很感激。


已更新 这就是现在的样子

create function dbo.myfunction(....)
returns @RES table
    (
    [#] int identity primary key clustered,
    [Varchar3Col] varchar(3),
    [DateTimeCol] datetime,
    [Varchar50Col] varchar(50)
    ) as
BEGIN
declare @RES2 table
    (
    rn int,
    [Varchar3Col] varchar(3),
    [DateTimeCol] datetime,
    [Varchar50Col] varchar(50)
    )

insert @RES2
select rn=row_number() over (order by action_time),
    [Varchar3Col]
    [DateTimeCol]
    [Varchar50Col]
from (.....)
inner join (.....) ON (.....)

declare @i int
set @i = 0
while @@rowcount > 0 begin
    set @i=@i+1
    insert @RES
    select [Varchar3Col], [DateTimeCol], [Varchar50Col]
    from @RES2
    where rn=@i
end
return
END
GO
  • 如果你看一下上面的内容,@RES的数量是按照所需的顺序依次完成的。
  • @RES有一个表示插入订单的聚类PK。
  • 列足够小,20行应始终适合单个8K页

这是否有效(使用来自应用程序层的直接SELECT)?

4 个答案:

答案 0 :(得分:7)

对于内联TVF,没有什么能真正起作用。不仅如此,内联TVF甚至可以返回更多行,而不是你认为应该这样做的行,并且在执行TVF之后将修剪行(基本上是TVF定义中的谓词)可以从TVF中拉出并移动到查询树中的其他位置)。有关此情况的示例,请参阅T-SQL functions do no imply a certain order of execution

将内联TVF转换为多语句将引入一些程序顺序,因为语句不能无序执行,但TVF结果可能会被重新排序,排序,拆分,假脱机,基本上由生成的优化器损坏计划,最终打破你对输出订单的假设。我担心如果你必须有一定的执行顺序,那么游标就是你最好的朋友。

答案 1 :(得分:2)

只是你不喜欢你听到的答案吗?事实是,订单只能通过order by子句得到保证。这不是意见,而是事实。如果它是您正在寻找的保证,则别无选择。

答案 2 :(得分:-1)

如果只有一个应用程序正在使用该函数,那么就像你提到的那样(因此没有加入/应用于另一个对象),这应该是一个特色! 将oder by子句添加到数据返回查询以按特定顺序接收结果集。

总是有风险你的fn将被另一个开发者使用,但不会像你想要的那样被使用,造成各种问题。表演是我会担心的。

答案 3 :(得分:-3)

如果您使用单一语句TVF而不是多语句TVF,您可以更好地了解可预测的查询计划。 ROW_NUMBER OVER应该在RES2查询中提供您想要的顺序,如果没有,只需将其放入CTE并按行号列排序。见下文。

CREATE FUNCTION [dbo].[MyFunction]
(
/*
Parameters
*/
)
RETURNS TABLE
RETURN
WITH res2
(
    rn,
    Varchar3Col,
    DateTimeCol,
    Varchar50Col
)
AS
(
    SELECT
        ROW_NUMBER() OVER (ORDER BY action_time) AS rn,
        Varchar3Col,
        action_time AS DateTimeCol,
        Varchar50Col
    FROM
/*
        [...from statement...]
*/      
)
SELECT
    rn,
    Varchar3Col,
    DateTimeCol,
    Varchar50Col
FROM
    res2
ORDER BY
    rn;