我想在前言中说我对SQL非常陌生,但我现在的工作要求我在其中工作。
我有一个包含地形点数据(x,y,z)的数据集。我正在尝试基于这些数据构建KNN模型。对于每个点'P',我搜索最接近P的数据集中的100个点(最近的意思是地理上最接近的)。然后我平均这些点的值(这个平均值称为残差),并将此值添加到'resid'列中的表中。
作为一个概念证明,我试图简单地遍历表格,并在每一行中将'resid'列的值设置为1.0。
我的疑问是:
CREATE OR REPLACE FUNCTION LoopThroughTable() RETURNS VOID AS '
DECLARE row table%rowtype;
BEGIN
FOR row in SELECT * FROM table LOOP
SET row.resid = 1.0;
END LOOP;
END
' LANGUAGE 'plpgsql';
SELECT LoopThroughTable() as output;
此代码执行并成功返回,但是当我检查表时,没有进行任何更改。我的错误是什么?
答案 0 :(得分:8)
在循环中逐行进行更新几乎总是一个坏主意,将非常慢并且不会扩展。你应该找到一种方法来避免这种情况。
说完之后:
您所做的所有功能都是更改内存中列值的值 - 您只是修改变量的内容。如果要更新数据,则需要update
语句:
你需要在循环中使用UPDATE
:
CREATE OR REPLACE FUNCTION LoopThroughTable()
RETURNS VOID
AS
$$
DECLARE
t_row the_table%rowtype;
BEGIN
FOR t_row in SELECT * FROM the_table LOOP
update the_table
set resid = 1.0
where pk_column = t_row.pk_column; --<<< !!! important !!!
END LOOP;
END;
$$
LANGUAGE plpgsql;
请注意,您 要在where
语句的主键上添加update
条件,否则您将更新所有行< strong>每次循环迭代。
稍微更有效的解决方案是使用游标,然后使用where current of
进行更新
CREATE OR REPLACE FUNCTION LoopThroughTable()
RETURNS VOID
AS $$
DECLARE
t_curs cursor for
select * from the_table;
t_row the_table%rowtype;
BEGIN
FOR t_row in t_curs LOOP
update the_table
set resid = 1.0
where current of t_curs;
END LOOP;
END;
$$
LANGUAGE plpgsql;
因此,如果我在循环结束后执行UPDATE查询,是否会将更改提交给表?
没有。对函数的调用在调用事务的上下文中运行。因此,如果在SQL客户端中禁用了自动提交,则在运行commit
后需要SELECT LoopThroughTable()
。
请注意,语言名称是标识符,不要在其周围使用单引号。您还应避免使用row
之类的关键字作为变量名称。
使用dollar quoting(正如我所做的那样)也使得更容易编写函数体
答案 1 :(得分:1)
我不确定概念验证示例是否符合您的要求。通常,使用SQL,您几乎永远不会需要FOR循环。虽然您可以使用函数,但如果您使用PostgreSQL 9.3或更高版本,则可以使用LATERAL
subquery为每行执行子查询。
例如,使用随机value
列创建10,000个随机3D点:
CREATE TABLE points(
gid serial primary key,
geom geometry(PointZ),
value numeric
);
CREATE INDEX points_geom_gist ON points USING gist (geom);
INSERT INTO points(geom, value)
SELECT ST_SetSRID(ST_MakePoint(random()*1000, random()*1000, random()*100), 0), random()
FROM generate_series(1, 10000);
对于每个点,搜索100个最近点(相关点除外),并找到点之间的残差。 value
和最近的100的平均值:
SELECT p.gid, p.value - avg(l.value) residual
FROM points p,
LATERAL (
SELECT value
FROM points j
WHERE j.gid <> p.gid
ORDER BY p.geom <-> j.geom
LIMIT 100
) l
GROUP BY p.gid
ORDER BY p.gid;
答案 2 :(得分:0)
以下是更新表中行的简单示例:
假定行ID字段为id
更新所有行:
UPDATE my_table SET field1='some value'
WHERE id IN (SELECT id FROM staff)
选择性行更新
UPDATE my_table SET field1='some value'
WHERE id IN (SELECT id FROM staff WHERE field2='same value')
答案 3 :(得分:-1)
你不需要一个功能。 您只需运行此查询:
UPDATE table SET resid = 1.0;
如果您想使用函数执行此操作,可以使用SQL
函数:
CREATE OR REPLACE FUNCTION LoopThroughTable()
RETURNS VOID AS
$BODY$
UPDATE table SET resid = 1.0;
$BODY$
LANGUAGE sql VOLATILE
如果你想使用plpgsql
那么函数将是:
CREATE OR REPLACE FUNCTION LoopThroughTable()
RETURNS void AS
$BODY$
begin
UPDATE table SET resid = 1.0;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
请注意,建议不要将plpgsql
函数用于可以使用Sql
函数执行的任务。