用户定义函数中-确定性标志的“生命周期”?

问题描述 投票:0回答:1

我试图了解在不使用 -确定性标志时 时我可能做错了什么或不理解在 Tcl 中使用用户定义的函数;以及相同输入的结果缓存的“生命周期”。

比如我原来用的是这个简单的查询,改成了用户自定义函数;并且,忘记了 -确定性标志的重要性,花了很多时间试图弄清楚为什么表 doc_pointers 中的每一行总是为 7 时调用 get_key() 。

 select *
 from
    doc_pointers
  --where key = order_keys->>:startIdx -- 7
  where key = get_key('a',:startIdx); -- 7

我的意思是“始终”仅在

select
语句的“生命周期”内,因为作为其一部分的 CTE 中的另一个查询可能会更改键的顺序,并且 :startIdx 可能不再返回 7。当然, json_array方法也是如此;并且它似乎不会为 doc_pointers 中的每一行重新调用。

此外,在递归查询的这一部分中,非递归部分为表 doc_pointers 的每一行调用

get_key('a',18)
;但是,在递归部分,不会为每一行调用
get_key('b', p.order_idx + :loopDir)

我知道这一点,因为每次调用 get_key() 时我都会打印到标准输出,并且可以看到 doc_pointers 中的每一行都有一个“a”行,而只有少量的“b”行。我期待一个“a”行,如果

where
子句是
r.key = 7
,就会出现这种情况。

我可以将

get_key('a',18)
设置为变量,例如 :startKey 因为它只使用一次,但不能设置
get_key('b', p.order_idx + :loopDir)
,因为它取决于循环索引。

因此,问题是,1)-确定性标志将 :startIdx 无限期地缓存为 7 多长时间? 2)在这种情况下使用-确定性标志是否“安全”? 3) 为什么递归查询的两部分对待 get_key() 的方式不同。

我应该补充一点,我尝试使用 -确定性标志进行实验,并且值的缓存似乎并没有 最后超出了当前的声明,但我要求听取知情者的意见。

谢谢你。

pieces( order_idx, key, buffer_id, char_start, char_length, char_begin ) as
(
  select
     18,
     r.key,
     r.buffer_id,
     r.char_start,
     r.char_length,
     598
  from
     doc_pointers r
  where
     r.key = get_key('a',18)

  UNION ALL

  select
     order_idx + :loopDir,
     r.key,
     r.buffer_id,
     r.char_start,
     r.char_length,
     p.char_begin +
       iif(:loopDir > 0, p.char_length, -r.char_length)
  from
     doc_pointers r,
     pieces p
  where
         r.key = get_key('b', p.order_idx + :loopDir)
     and (
           (
                 :loopDir > 0
             and p.char_length + p.char_begin <= cast(:breakPt as integer)
           )
        or
           (     :loopDir < 0
             and p.char_begin >= cast(:breakPt as integer)
           )
         )
)
sqlite tcl
1个回答
0
投票

-确定性标志缓存特定参数值集的函数结果。它不会缓存每个参数如何获取其值。使用固定值、变量或更复杂的表达式调用函数不会对缓存产生任何影响。

因此,您应该只在每次为相同输入生成相同结果的函数上使用 -确定性标志。如果函数产生的结果仅取决于所提供的参数,则这是正确的。如果函数使用可能更改的附加信息,则它不是确定性的。

澄清一下:您不应该担心该函数将如何被调用。无论是使用固定参数还是可变参数都无关紧要。您也不需要关心结果被缓存多长时间。这只是一种优化。如果条目从缓存中被刷新,sqlite 将在下次需要该值时再次调用该函数。如果该函数确实是确定性的,则新计算的值将与从缓存中丢失的值完全相同。

结果缓存多长时间可能取决于不可预测的因素,例如可用内存以及在调用之间缓存哪些其他函数。尝试在你的函数中考虑到这一点实际上是不可行的。

具体:当$startIdx = 18且

get_key('a', :startIdx)
返回7时,缓存的是:get_key('a', 18) = 7。
如果您接下来将 $startIdx 更改为 23 并再次调用
get_key('a', :startIdx)
,该函数将再次被调用,因为参数与缓存的结果不匹配。
但是,如果您调用
get_key('a', 18)
,将使用缓存的结果。即使这次你使用的是固定变量,而不是变量。

© www.soinside.com 2019 - 2024. All rights reserved.