有条件地选择要在 FOR LOOP 中迭代的集合

Posted

技术标签:

【中文标题】有条件地选择要在 FOR LOOP 中迭代的集合【英文标题】:Conditionally chose the set to iterate over in a FOR LOOP 【发布时间】:2019-01-04 23:22:10 【问题描述】:

我想知道是否可以在循环中执行 if-elsif 或 case-when。在 for 循环之间没有,但作为 for 循环的一个状态,可以在 select 或另一个之间进行选择。 像这样:

if-elsif 和 case-when 两种方法我都试过了....但它们都不起作用,我一直在网上潜伏着寻找一些东西,但没有。

CREATE OR REPLACE FUNCTION myfunct(op integer, -vars-)
RETURNS table(-vars-)
LANGUAGE plpgsql
AS $function$
DECLARE 
  selectop record;
  -vars-
BEGIN
  FOR selectop in (IF (op=1) THEN
                             SELECT * FROM mytab WHERE somevar=true;
                   ELSIF (op=2) THEN
                             SELECT * from mytab WHERE somevar=false;
                   END IF;)
      -things-
  RETURN NEXT;
  LOOP
    ---THINGS---
 END;
$function$

【问题讨论】:

【参考方案1】:

我想知道为什么您不只是设置一个变量并在查询中使用它。这似乎容易得多。但从学术的角度来看,这是一个有趣的问题。

问题在于 IFCASE 作为控制结构不会返回某些内容或评估某些内容。所以我们不能以这种方式使用它们。

如果我们考虑返回一些东西,那么我们可能会想到函数。所以你可以在那里放置一个函数调用,它根据参数返回不同的集合。但似乎您想要的功能应该完全实现,所以这只会将问题转移到另一个层面并最终无休止。

所以让我们考虑评估一些东西。我们想到了表达式,确实有 CASE 作为表达式,我们可以用它来切换。唯一的问题是,至少据我所知,它无法处理集合。

但它可以处理数组。所以我们的想法是使用CASE 表达式,计算结果为(两个)不同的数组。

我们将使用这样一个事实,即每个表还在 Postgres 中定义了一个类型,即其行的类型。所以我们将遍历该类型的数组。

Postgres 的下一个巧妙之处是,我们可以使用array_agg() 将完整的表或其子集聚合到一个数组中。这就是我们将如何创建我们迭代的数组。

要遍历数组,我们将使用FOREACH 循环。 (是的,这不是光标上的 FOR 循环,但在语义上我猜这已经足够接近了。)

这样的函数可能如下所示。 elbat 是我们的表,nmuloc4 是我们想要比较值的那一列,这取决于函数参数 switch 的值。该函数返回一个SETOF elbat,即来自elbat的一组记录。

CREATE FUNCTION noitcnuf
                (switch integer)
RETURNS SETOF elbat
AS
$$
DECLARE
  elbat_array elbat[];
  elbat_element elbat;
BEGIN
  FOREACH elbat_element IN ARRAY(CASE
                                   WHEN switch = 1 THEN
                                     (SELECT array_agg(elbat)
                                             FROM elbat
                                             WHERE nmuloc4 = true)
                                   WHEN switch = 2 THEN
                                     (SELECT array_agg(elbat)
                                             FROM elbat
                                             WHERE nmuloc4 = false)
                                   ELSE
                                     ARRAY[]::elbat[]
                                 END) LOOP
    RETURN NEXT elbat_element;
  END LOOP;

  RETURN;
END;
$$
LANGUAGE plpgsql;

db<>fiddle

CASEELSE 分支中,我们只有一个正确类型的空数组。否则,如果我们将参数传递给CASE 中没有分支退出的函数,我们将收到错误消息。像这样,我们只是在这种情况下得到空集。

请注意,这也适用于视图甚至可能是集合返回函数,而不是表(我没有明确测试后者)。

但我也想警告一下,我怀疑这种方法的性能可能比仅仅根据变量构建查询并在游标上执行经典循环或最多将其减少为基于集合的方法完全没有循环。

【讨论】:

以上是关于有条件地选择要在 FOR LOOP 中迭代的集合的主要内容,如果未能解决你的问题,请参考以下文章

迭代,循环,遍历,递归的区别

VBA - 如何有条件地跳过for循环迭代

如何有条件地跳过python中for循环中的迭代步骤数?

迭代,循环,遍历,递归的区别

C++中,do while, for loop和while loop之间有啥不同

在每次迭代中有条件地使用不同数量的项目