sqlite 用户定义函数中-deterministic标志的“生存期”?

noj0wjuj  于 2023-10-23  发布在  SQLite
关注(0)|答案(1)|浏览(157)

我试图理解我可能做错了什么,或者不理解在Tcl中使用用户定义函数时不使用-deterministic标志;以及对于相同输入缓存其结果的“生存期”。
例如,我将这个简单的查询改为用户自定义函数;而且,忘记了-deterministic标志的重要性,花了大量的时间试图弄清楚为什么get_key()总是为7时,表doc_pointers中的每一行都被调用。

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()都打印到stdout,并且可以看到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)在这种情况下使用-deterministic标志“安全”吗?3)为什么递归查询的两个部分对待get_key()的方式不同。
我应该补充的是,我尝试使用-deterministic标志进行实验,似乎值的缓存不会持续到当前语句之后,但我想听听知道的人的意见。
谢谢

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

yv5phkfx1#

-deterministic标志缓存一组特定参数值的函数结果。它不缓存每个参数是如何获得其值的。使用固定值、变量或更复杂的表达式调用函数对缓存没有任何影响。
因此,您应该只在每次对相同输入产生相同结果的函数上使用-deterministic标志。如果函数产生的结果仅取决于提供的参数,则为真。如果函数使用可能更改的附加信息,则它不是确定性的。
澄清一下:你不应该担心如何调用函数。这是使用固定参数还是可变参数无关紧要。您也不需要关心结果缓存多长时间。这只是一种优化。如果条目从该高速缓存中被刷新,sqlite将在下次需要该值时再次调用该函数。如果函数是真正确定性的,则新计算的值将与从该高速缓存中丢失的值完全相同。
缓存结果的时间长短可能取决于不可预测的因素,如可用内存和调用之间缓存的其他函数。试图在您的函数中考虑到这一点实际上是不可行的。
具体来说:当$startIdx = 18并且get_key('a', :startIdx)返回7时,缓存的内容是:get_key('a',18)= 7.
如果接下来将$startIdx更改为23并再次调用get_key('a', :startIdx),则该函数将再次被调用,因为参数与缓存的结果不匹配。
但是,如果调用get_key('a', 18),则将使用缓存的结果。即使你这次使用了一个固定的变量,而不是一个变量。

相关问题