在PL/SQL FOR循环中,如何根据列的位置而不是列名来引用列。

huangapple go评论58阅读模式
英文:

Oracle how to address column in PL/SQL FOR LOOP by it's position instead of column name

问题

有没有一种方法可以在PL/SQL中根据列的位置来引用列,就像这样:

BEGIN
FOR r IN (SELECT * FROM table)
LOOP
    DBMS_OUTPUT.PUT_LINE(r.(1));
END LOOP;
END;

我尝试了两种括号类型,但总是得到以下错误:

PLS-00103: 遇到符号“(”时,期望以下之一

我还尝试了使用VARRAY的表和BULK COLLECT INTO,但然后我得到了以下错误:

PLS-00642: 不允许在SQL语句中使用本地集合类型

如果我使用%ROWTYPE声明表并进行批量操作,它似乎表现得与常规循环相同。

我尝试查阅文档,但找不到这种情况的示例。

英文:

Is there a way to address column by it's position in PL/SQL, something like this

BEGIN
FOR r IN (SELECT * FROM table)
LOOP
    DBMS_OUTPUT.PUT_LINE(r.(1));
END LOOP;
END;

I tried both bracket types but always get

PLS-00103: Encountered the symbol "(" when expecting one of the following

I also tried BULK COLECT INTO with table of varray but then I get

PLS-00642: local collection types not allowed in SQL statements

And if I declare table with %ROWTYPE and bulk it then it seems to behave the same way as the normal loop.

I tried to look into documentation but couldn't find any example of this scenario.

答案1

得分: 2

使用DBMS_SQL包和COLUMN_VALUE过程:但这需要很多工作... 可能对于您的实际需求来说有些过于复杂:您需要为查询打开一个sys_refcursor,使用DBMS_SQL.TO_CURSOR_NUMBER将其转换为游标号,使用DBMS_SQL.DESCRIBE_COLUMNS收集列的描述信息,然后循环处理列数并根据其类型使用DBMS_SQL.COLUMN_VALUE将值提取到特定变量中...

英文:

Using the DBMS_SQL package and the COLUMN_VALUE procedure: but that's a lot of work... probably overkilling for your actual needs: you have to open a sys_refcursor for the query, convert it into a cursor number with DBMS_SQL.TO_CURSOR_NUMBER, collect the descriptions of the columns with DBMS_SQL.DESCRIBE_COLUMNS and loop on the column count and fetch the value with DBMS_SQL.COLUMN_VALUE into a specific variable according to its type...

答案2

得分: 0

一般情况下,不支持通过位置访问记录,只支持通过标识符。

你可以略微不同的方法来解决这个问题,而不是使用 SELECT *,你可以明确列出要选择的列并为它们指定数字列别名:

BEGIN
  FOR r IN (SELECT dummy AS "1" FROM DUAL)
  LOOP
    DBMS_OUTPUT.PUT_LINE(r."1");
  END LOOP;
END;
/

你仍然通过列名来引用列,只是现在它们的名称是“数字”别名。

注意:个人建议仍然使用列名,而不要尝试寻找使用位置的解决方法,因为如果从表中删除列,位置可能会发生变化。

英文:

In general, no. Oracle does not support accessing records by position; only by identifier.


You can approach the problem slightly differently and, instead of using SELECT *, you can explicitly list the columns to select and give them numeric column aliases:

BEGIN
  FOR r IN (SELECT dummy AS "1" FROM DUAL)
  LOOP
    DBMS_OUTPUT.PUT_LINE(r."1");
  END LOOP;
END;
/

You are still referring to the columns by name; only now their names are the "numeric" aliases.

Note: Personally, I would stick to using the column names and not try to find work-arounds to use positions as positions can change if columns are deleted from the table.

答案3

得分: 0

如果您想在PL/SQL中使用矩阵,可以将其定义为索引表的索引表,数据类型与您正在处理的数据类型相同,类似于以下方式:

type t_row is table of varchar2(1) index by pls_integer;
type t_tab is table of t_row index by pls_integer;
l_matrix t_tab;

复杂的部分是如何填充它。DBMS_SQL当然是一种方法,但出于趣味,您可以使用XML。DBMS_XMLGEN包 允许您运行查询并将结果返回为具有ROWSET根节点的XML文档。然后,您可以计算ROW节点的数量,假设有任何ROW节点,然后计算第一个ROW内的节点 - 这给出了矩阵的维度。

然后,您可以使用嵌套循环来填充矩阵,完成后,您可以引用一个元素,例如 l_matrix(3)(2)

这是一个工作示例,其中查询的表具有所有单个字符的值,以简化为例;您需要更改矩阵定义以处理您期望的数据。

DECLARE
  -- 矩阵元素需要适合您的数据的数据类型和大小
  type t_row is table of varchar2(1) index by pls_integer;
  type t_tab is table of t_row index by pls_integer;
  l_matrix t_tab;
  l_xml xmltype;
  l_rows pls_integer;
  l_cols pls_integer;
BEGIN
  l_xml := dbms_xmlgen.getxmltype('SELECT * FROM your_table');

  SELECT XMLQuery('count(/ROWSET/ROW)'
    PASSING l_xml
    RETURNING CONTENT).getnumberval()
  INTO l_rows
  FROM DUAL;

  IF l_rows = 0 THEN
    dbms_output.put_line('No data');
    RETURN;
  END IF;

  SELECT XMLQuery('count(/ROWSET/ROW[1]/*)'
    PASSING l_xml
    RETURNING CONTENT).getnumberval()
  INTO l_cols
  FROM DUAL;

  dbms_output.put_line('Rows: ' || l_rows || ' Cols: ' || l_cols);

  -- 填充矩阵
  FOR i IN 1..l_rows LOOP
    FOR j in 1..l_cols LOOP
      l_matrix(i)(j) := l_xml.extract('/ROWSET/ROW[' || i || ']/*[' || j || ']/text()').getstringval();
    END LOOP;
  END LOOP;

  -- 引用特定元素
  dbms_output.put_line('Element 3,2 should be H; is actually: ' || l_matrix(3)(2));

  -- 显示所有元素的列表
  FOR i IN 1..l_matrix.COUNT LOOP
    FOR j IN 1..l_matrix(i).COUNT LOOP
      dbms_output.put_line('Row ' || i || ' col ' || j || ': ' || l_matrix(i)(j));
    END LOOP;
  END LOOP;

  -- 显示所有元素的网格
  FOR i IN 1..l_matrix.COUNT LOOP
    FOR j IN 1..l_matrix(i).COUNT LOOP
      IF j > 0 THEN
        dbms_output.put(' ');
      END IF;
      -- 格式化/填充真实数据以对齐它; 在此处使用单个字符值很简单
      dbms_output.put(l_matrix(i)(j));
    END LOOP;
    dbms_output.new_line;
  END LOOP;
END;
/

使用我的示例表,输出如下:

Rows: 4 Cols: 3
Element 3,2 should be H; is actually: H
Row 1 col 1: A
Row 1 col 2: B
Row 1 col 3: C
Row 2 col 1: D
Row 2 col 2: E
Row 2 col 3: F
Row 3 col 1: G
Row 3 col 2: H
Row 3 col 3: I
Row 4 col 1: J
Row 4 col 2: K
Row 4 col 3: L
A B C
D E F
G H I
J K L

fiddle


正如 @astenx 在评论中提到的,您可以使用JSON 而不是XML;这将允许您像这样填充矩阵like this

这引发了我忽略的另一点。我的示例表没有ID列或明显的结果排序方法。对于JSON版本,我使用了rownum,但在原始查询和矩阵中,行的顺序(在结果集中,因此在矩阵中)目前是不确定的。您需要在查询中拥有一个可以order by的列,或其他确定行序的方法。

英文:

If you want to have a matrix in PL/SQL you could define one as an indexed table of an indexed table of whatever data type you're dealing with, something like:

type t_row is table of varchar2(1) index by pls_integer;
type t_tab is table of t_row index by pls_integer;
l_matrix t_tab;

The tricky part is populating it. DBMS_SQL is certainly one approach, but partly for fun, you could use XML. The DBMS_XMLGEN package lets you run a query and get the results back as an XML document with a ROWSET root node. You can then count the ROW nodes under that, and assuming there are any, count the nodes within the first ROW - which gives the matrix dimensions.

You can then use nested loops to populate the matrix, and once done, you can refer to an element as for example l_matrix(3)(2).

This is a working example, where the table being queried has values that are all a single character, for simplicity; you would need to change the matrix definition to handle the data you expect.

DECLARE
-- elements of matrix need to be data type and size suitable for your data
type t_row is table of varchar2(1) index by pls_integer;
type t_tab is table of t_row index by pls_integer;
l_matrix t_tab;
l_xml xmltype;
l_rows pls_integer;
l_cols pls_integer;
BEGIN
l_xml := dbms_xmlgen.getxmltype('SELECT * FROM your_table');
SELECT XMLQuery('count(/ROWSET/ROW)'
PASSING l_xml
RETURNING CONTENT).getnumberval()
INTO l_rows
FROM DUAL;
IF l_rows = 0 THEN
dbms_output.put_line('No data');
RETURN;
END IF;
SELECT XMLQuery('count(/ROWSET/ROW[1]/*)'
PASSING l_xml
RETURNING CONTENT).getnumberval()
INTO l_cols
FROM DUAL;
dbms_output.put_line('Rows: ' || l_rows || ' Cols: ' || l_cols);
-- populate matrix
FOR i IN 1..l_rows LOOP
FOR j in 1..l_cols LOOP
l_matrix(i)(j) := l_xml.extract('/ROWSET/ROW[' || i || ']/*[' || j || ']/text()').getstringval();
END LOOP;
END LOOP;
-- refer to a specific element
dbms_output.put_line('Element 3,2 should be H; is actually: ' || l_matrix(3)(2));
-- display all elements as list
FOR i IN 1..l_matrix.COUNT LOOP
FOR j IN 1..l_matrix(i).COUNT LOOP
dbms_output.put_line('Row ' || i || ' col ' || j || ': ' || l_matrix(i)(j));
END LOOP;
END LOOP;
-- display all elements as grid
FOR i IN 1..l_matrix.COUNT LOOP
FOR j IN 1..l_matrix(i).COUNT LOOP
IF j > 0 THEN
dbms_output.put(' ');
END IF;
-- format/pad real data to align it; simple here with single-char values
dbms_output.put(l_matrix(i)(j));
END LOOP;
dbms_output.new_line;
END LOOP;
END;
/

With my sample table that outputs:

Rows: 4 Cols: 3
Element 3,2 should be H; is actually: H
Row 1 col 1: A
Row 1 col 2: B
Row 1 col 3: C
Row 2 col 1: D
Row 2 col 2: E
Row 2 col 3: F
Row 3 col 1: G
Row 3 col 2: H
Row 3 col 3: I
Row 4 col 1: J
Row 4 col 2: K
Row 4 col 3: L
A B C
D E F
G H I
J K L

fiddle


As @astenx said in a comment, you can use JSON instead of XML; which would allow you to populate the matrix like this.

That has raised something else I'd overlooked. My dummy table doesn't have an ID column or an obvious way of ordering the results. For the JSON version I used rownum, but in both that and the original query the order of the rows (in the result set, and thus in the matrix) is currently indeterminate. You need to have a column you can order by in the query, or some other way to determine the sequence of rows.

huangapple
  • 本文由 发表于 2023年2月9日 00:28:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75388836.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定