SQL select 语句产生与 PL/pgSQL FOR 循环相同的结果
SQL select statement yielding same results as PL/pgSQL FOR loop
我很难理解何时使用 PL/pgSQL FOR
循环与常规 SQL。
这个问题肯定是暴露了我的天真,但我的困惑是:
鉴于此 table:
CREATE TABLE bins AS
SELECT * FROM GENERATE_SERIES(1, 10) AS id;
如果我想在每一行的 id
字段中加 1,我会这样做:
select id+1 from bins;
哪个会正确 return
2
3
4
5
6
7
8
9
10
11
现在,如果我为这个操作使用 PL/pgSQL 函数,它将看起来像这样:
CREATE OR REPLACE FUNCTION add_one(
n integer
)
RETURNS VOID AS $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN SELECT id
FROM bins
LOOP
RAISE NOTICE '%', rec.id+n;
END LOOP;
END;
$$ LANGUAGE plpgsql;
那么
select add_one(1)
会产生
-- Executing query:
select add_one(1)
NOTICE: 2
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 3
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 4
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 5
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 6
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 7
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 8
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 9
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 10
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 11
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
所以我的问题是,SQL select 语句在某种程度上就像一个 for 循环,因为在这个例子中它向每一行添加一个 1...发生在 PL/pgSQL 函数中。这是一个准确的描述吗?
您的描述是准确的。
此时您最大的问题可能是理解 SQL 的非过程性质。在这种语言中,您无需指定达到结果所需的步骤,只需描述您想要的结果。
所以你上面使用的 SQL 语句你想要 id
,增加一个,对于 table 中的每一行。如果数据库通过 运行 内部循环或不可抗力来执行此操作,则不是您的主要关注点。
一开始这对许多过程程序员来说很奇怪,但一旦你习惯了它会让生活变得更好:你不必了解所有的数据库内部知识来找出处理查询的最佳方法。 优化器 会为您做这些,而且通常比您自己做的更好。
当然,想知道查询是如何在内部处理的是健康的好奇心。为此,您使用 EXPLAIN
语句。这对于分析和修复性能问题尤为必要。
经验法则是,在一个更大的 SQL 语句中执行尽可能多的工作通常比在许多简单的小语句中与应用程序代码捆绑在一起更好。这是因为这种方式的开销较少,而且还因为更大的语句通常会为优化器打开更多途径。例如,嵌套循环是最简单的,但并不总是解决问题的最佳方法。
我很难理解何时使用 PL/pgSQL FOR
循环与常规 SQL。
这个问题肯定是暴露了我的天真,但我的困惑是:
鉴于此 table:
CREATE TABLE bins AS
SELECT * FROM GENERATE_SERIES(1, 10) AS id;
如果我想在每一行的 id
字段中加 1,我会这样做:
select id+1 from bins;
哪个会正确 return
2
3
4
5
6
7
8
9
10
11
现在,如果我为这个操作使用 PL/pgSQL 函数,它将看起来像这样:
CREATE OR REPLACE FUNCTION add_one(
n integer
)
RETURNS VOID AS $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN SELECT id
FROM bins
LOOP
RAISE NOTICE '%', rec.id+n;
END LOOP;
END;
$$ LANGUAGE plpgsql;
那么
select add_one(1)
会产生
-- Executing query:
select add_one(1)
NOTICE: 2
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 3
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 4
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 5
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 6
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 7
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 8
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 9
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 10
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
NOTICE: 11
CONTEXT: PL/pgSQL function add_one(integer) line 9 at RAISE
所以我的问题是,SQL select 语句在某种程度上就像一个 for 循环,因为在这个例子中它向每一行添加一个 1...发生在 PL/pgSQL 函数中。这是一个准确的描述吗?
您的描述是准确的。
此时您最大的问题可能是理解 SQL 的非过程性质。在这种语言中,您无需指定达到结果所需的步骤,只需描述您想要的结果。
所以你上面使用的 SQL 语句你想要 id
,增加一个,对于 table 中的每一行。如果数据库通过 运行 内部循环或不可抗力来执行此操作,则不是您的主要关注点。
一开始这对许多过程程序员来说很奇怪,但一旦你习惯了它会让生活变得更好:你不必了解所有的数据库内部知识来找出处理查询的最佳方法。 优化器 会为您做这些,而且通常比您自己做的更好。
当然,想知道查询是如何在内部处理的是健康的好奇心。为此,您使用 EXPLAIN
语句。这对于分析和修复性能问题尤为必要。
经验法则是,在一个更大的 SQL 语句中执行尽可能多的工作通常比在许多简单的小语句中与应用程序代码捆绑在一起更好。这是因为这种方式的开销较少,而且还因为更大的语句通常会为优化器打开更多途径。例如,嵌套循环是最简单的,但并不总是解决问题的最佳方法。