1

我在 VS 2008 中使用 C# 从具有两个输入参数和两个输出参数的 PostgreSQL 存储过程中检索数据。当我创建过程时,PostgreSQL 告诉我必须指定它返回一条记录。

在 VS2008 中,我第一次尝试使用该过程涉及创建一个OdbcCommand类型的对象CommandType.StoredProcedure并为其提供四个参数,两个带有输入方向,两个带有方向输出。该命令执行没有错误,首先使用ExecuteNonQuery()然后使用ExecuteReader(),但输出参数的值为空。我调用了 reader 的GetValues()函数,发现结果是一个包含字符串的单个对象"{3,4}"

然后,根据 StackOverflow 的建议,我将命令文本更改为:{call most_idle_cover(?, ?, ?, ?)}

这也有效,并GetValues()给了我一个包含两个 int 类型对象的数组,一个为 3,另一个为 4。这要好得多,因为我不必解析字符串。但是输出参数仍然有空值,事实上,如果我只传入两个输入参数,该命令也同样有效。

所以,虽然我有一个可行的解决方案,但我仍然很好奇:如何将这些值放入我的输出参数中?

这是 PostgreSQL 存储过程:

CREATE OR REPLACE FUNCTION plant_genie.closest_idle_cover(IN int, IN int, OUT int, OUT int)
  RETURNS record AS
$BODY$
DECLARE
    current_x ALIAS FOR $1;
    current_y ALIAS FOR $2;
    target_x ALIAS FOR $3;
    target_y ALIAS FOR $4;
    coverLocations ic_storage_locations%rowtype;
BEGIN
    target_x := 3;
    target_y := 4;  

    SELECT INTO coverLocations * 
    FROM ic_storage_locations 
    WHERE inner_cover IS NOT NULL 
    ORDER BY sqrt(pow(current_x - ic_storage_locations.x_coordinate, 2) + 
            pow(current_y - ic_storage_locations.y_coordinate, 2))
    LIMIT 1;

    IF FOUND THEN
        INSERT INTO op_messages (message) VALUES ('Found a cover location record.');
        target_x := coverLocations.x_coordinate;
        target_y := coverLocations.y_coordinate;
    ELSE
        INSERT INTO op_messages (message) VALUES ('Could not find a cover location record.');
    END IF;
END;
$BODY$ LANGUAGE 'plpgsql' VOLATILE COST 100;
4

1 回答 1

1

您使用的是OUT参数,但也使用了RETURNS record子句,而函数主体中没有明确的RETURN语句。这种组合不起作用。比使用OUT参数更优雅的解决方案是定义输出表格式 - 发生了什么更明显:

CREATE OR REPLACE FUNCTION plant_genie.closest_idle_cover(current_x int, current_y int)
RETURNS TABLE (target_x int, target_y int) AS $BODY$
DECLARE
    coverLocations ic_storage_locations%rowtype;
BEGIN
    SELECT INTO coverLocations * 
    FROM ic_storage_locations 
    WHERE inner_cover IS NOT NULL 
    ORDER BY pow(current_x - ic_storage_locations.x_coordinate, 2) + 
             pow(current_y - ic_storage_locations.y_coordinate, 2)
    LIMIT 1;

    IF FOUND THEN
        INSERT INTO op_messages (message) VALUES ('Found a cover location record.');
        RETURN QUERY SELECT coverLocations.x_coordinate, coverLocations.y_coordinate;
    ELSE
        INSERT INTO op_messages (message) VALUES ('Could not find a cover location record.');
    END IF;
END; $BODY$ LANGUAGE 'plpgsql' STRICT;

因此,如果您调用此函数,如果至少 1 ic_storage_location 不为空,则会返回一条记录:

SELECT * FROM plant_genie.closest_idle_cover(1, 2);

您可以在 C# 代码中处理它,就像处理从数据库中提取的任何其他数据一样。

几点观察:

  • 由于您正在寻找最近的 ic_storage_location ,因此您可以省去SQRT()计算量很大的函数调用。仅使用平方和具有相同的属性,即按与当前位置的距离对记录进行排序。
  • 该函数被定义为STRICT因为它需要两个参数的值才能正常工作。
  • 不要COST自己赋值,除非你真的知道自己在做什么。
于 2014-05-11T04:20:03.993 回答