【问题标题】:guarantee order of table valued function results表值函数结果的保证顺序
【发布时间】:2011-08-19 01:21:11
【问题描述】:

前提:应用程序代码不能更改。条件非常具体。我正在寻找一些书外的东西,如果可以的话,这是最后的解决方法。

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

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

select * from dbo.myfunction(...)

根据您的经验,有什么方法可以保证确保(就您使用特定技术观察到的情况而言)返回的结果按第二列排序?列是:varchar(3)、datetime、varchar(50)。 不要让我开始select *,它是INTENTIONAL,因此无论我将来显示多少列,前端都会显示该功能。

根据经验,使用单个索引(集群 PK)来遍历数据,任何当前版本的 SQL Server 和 SP 级别都应该始终对

你的想法?我宁愿把理论排除在讨论之外。如果您能坚持实践经验,并在家中继续讲道最佳实践,我也将不胜感激。


更新 这就是现在的样子

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)?

【问题讨论】:

  • 不,不能保证。如果您需要保证,请在应用程序中添加 ORDER BY。这就是保证的执行方式。这不是关于最佳实践的布道,这只是事实......查询优化器可以选择以认为最有效的任何方式对您的结果进行排序。是的,这可能会发生在 20 行中,并且可能会随着基表上的统计信息更改、累积更新、服务包、升级等而发生变化。
  • 优化器不是我写的,所以我不能告诉你它到底做了什么。但我可以告诉你,这是由地球上一些最聪明的人编写的一段极其复杂的代码。我怀疑他们会告诉你它是如何工作的,因为这也是 SQL Server 表现如此出色的原因之一,但我敢打赌,他们会告诉你我告诉你的同样的事情。 不要依赖观察到的订单行为来保持不变。如果您需要保证,请添加ORDER BY
  • 人们一直在使用无证的东西,但通常他们了解风险,并且不会对他们的行为做出任何保证(除非他们不知道更好,或者不在乎)。我只是不明白将 ORDER BY 2 添加到您的应用程序代码有什么困难。如果它是我的应用程序,我会在有明确保证的情况下晚上睡得更好……而在你花时间制作这个问题并贬低任何质疑你的人的时候,你本可以做到这一点。尽情享受你的问题吧。
  • @Aaron。假设乔得了癌症。医生(专家)在 1 个月内宣布终末期,没有追索权。乔应该寻求替代医学/祈祷/巫毒吗?好的,我没有提到APP不能更改。但这就是我所在的地方,我有一个问题,正在寻找一种解决方法,无证,无保证或其他。这不是一个包罗万象的问题,只是一个非常具体的案例和条件。感谢您在这个问题上的时间,但如果您觉得您需要向我扔 MSSQL 圣经,我有一个暗示,我可以在任何其他日子反对您。
  • 好吧,好吧,如果你想依赖你理解不能保证的行为,你为什么还要麻烦问这个问题?你似乎事先就知道会是什么反应。您需要决定后果是否值得......当这个应用程序在没有明确说明的情况下对订单进行假设,突然开始收到意外订单的结果时,究竟会发生什么?

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


【解决方案1】:

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

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

【讨论】:

  • 我已经添加了函数的主体。我已将其转换为多语句,并使用循环来保证插入顺序(沿集群 PK)。这有帮助吗?
  • 您能想出 QO 对少于 20 条记录的 TVF 结果重新排序、(重新)排序或拆分的原因吗?
【解决方案2】:

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

【讨论】:

  • 我已编辑问题以更好地反映问题的原因。 SQLkiwi 没看错。
【解决方案3】:

如果只有一个应用程序正在使用该功能,那么按照您提到的这种特殊方式(因此没有加入/应用于另一个对象),这应该是一个 sproc ! 在数据返回查询中添加 oder by 子句,以特定顺序接收结果集。

您的 fn 始终存在被其他开发人员使用的风险,但并非按照您的预期使用方式,从而导致各种问题。性能是我最担心的。

【讨论】:

    【解决方案4】:

    如果您使用单语句 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;
    

    【讨论】:

    • 为什么你认为这比多语句 tvf 更能保证在没有命令的情况下调用时保持某个命令?在函数结果被具体化之后,排序仍然会影响结果,这就是为什么无论函数内部是什么都不能保证的原因。
    • CTE 末尾的 order by 子句将保证从函数返回行的顺序。它作为原子语句运行,而不是问题中提出的多语句 TVF 中的迭代插入。这将更有效地运行。
    • 如果您只说 SELECT * FROM dbo.MyFunction() 而没有 ORDER BY,则无法保证。不知道如何解释这一点。请注意,运行 1000 次并每次观察相同的顺序与保证相同。
    • ROW_NUMBER() 根据传入的排序顺序 (action_time) 应用行号排名。内部语句将该列别名为 rn。外部语句按 rn 排序。订单不保怎么办?
    • 这是怎么回事?函数内部的 ORDER BY 告诉函数如何对行进行排序; row_number 表示如何应用行号值,但不一定表示订单将如何返回; top...order by 类似地规定了如何选择行,但同样不能保证它们将以相同的顺序返回。还有另一层:当您从函数中选择时,that 是我正在谈论的外部查询,如果您想要保证,这正是 ORDER BY 需要去的地方。 排在最后,或者不要依赖它。
    猜你喜欢
    • 2011-11-06
    • 1970-01-01
    • 2014-08-05
    • 1970-01-01
    • 2012-10-18
    • 2016-09-06
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    相关资源
    最近更新 更多