简体   繁体   English

如何将输出值转换为输出参数?

[英]How can I get output values into output parameters?

I am using C# in VS 2008 to retrieve data from a PostgreSQL stored procedure that has two input parameters and two output parameters. 我在VS 2008中使用C#从具有两个输入参数和两个输出参数的PostgreSQL存储过程中检索数据。 When I created the procedure, PostgreSQL told me that I had to specify that it returns a record. 创建该过程时,PostgreSQL告诉我必须指定它返回一条记录。

In VS2008, my first attempt to use the procedure involved creating an OdbcCommand object of type CommandType.StoredProcedure and giving it four parameters, two with direction of Input and two of direction Output. 在VS2008中,我首次尝试使用该过程涉及创建类型为CommandType.StoredProcedureOdbcCommand对象,并为其提供四个参数,两个参数为Input方向,两个参数为Output方向。 The command executed without error, first using ExecuteNonQuery() and then using ExecuteReader() , but the values of the output parameters were null. 执行该命令没有错误,首先使用ExecuteNonQuery() ,然后使用ExecuteReader() ,但是输出参数的值为null。 I called the reader's GetValues() function, and found that the result was a single object containing the string "{3,4}" . 我调用了读者的GetValues()函数,发现结果是一个包含字符串"{3,4}"的单个对象。

Then, following a suggestion from StackOverflow, I changed the command text to: {call closest_idle_cover(?, ?, ?, ?)} 然后,根据StackOverflow的建议,我将命令文本更改为:{call hidden_​​idle_cover(?,?,?,?)}

This also worked, and GetValues() gave me an array of two objects of type int, one with 3 and the other with 4. This was quite a bit better, since I wouldn't have to parse a string. 这也起作用,并且GetValues()给我一个数组,其中包含两个类型为int的对象,一个对象为3,另一个对象为4。这要好很多,因为我不必解析字符串。 But the output parameters still have null values, and indeed, the command works just as well if I only pass in the two input parameters. 但是输出参数仍然具有空值,并且实际上,如果我仅传递两个输入参数,该命令也可以正常工作。

So, although I have a solution that works, I remain curious: How can I get the values into my output parameters? 因此,尽管我有一个可行的解决方案,但我仍然感到好奇:如何将这些值输入到输出参数中?

Here's the PostgreSQL stored procedure: 这是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;

You are using OUT parameters but then also a RETURNS record clause, without having an explicit RETURN statement in the body of the function. 您正在使用OUT参数,但同时使用RETURNS record子句,而在函数主体中没有显式的RETURN语句。 That combination does not work. 这种组合不起作用。 A more elegant solution than using OUT parameters is to define the output table format - it is more obvious what is going on: 比使用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;

So if you call this function you are returned a record if at least 1 ic_storage_location is not null: 因此,如果调用此函数,并且至少有1个ic_storage_location不为null,则会返回一条记录:

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

You can deal with that in your C# code like you would any other data you pull out of the database. 您可以像使用其他任何从数据库中提取的数据一样,在C#代码中进行处理。

A few observations: 一些观察:

  • Since you are looking for the nearest ic_storage_location you can dispense with the SQRT() function call, which is computationally expensive. 由于您正在寻找最接近的ic_storage_location,因此可以省去SQRT()函数调用,这在计算上是昂贵的。 Just working with the squared sums has the same property of ordering records by distance from the current location. 仅仅使用平方和具有与从当前位置的距离对记录进行排序的相同属性。
  • The function is defined as STRICT because it requires values for both parameters to work properly. 该函数定义为STRICT因为它需要两个参数的值才能正常工作。
  • Don't assign COST values yourself, unless you really know what you are doing. 除非您真的知道自己在做什么,否则不要自己分配COST值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM