2017-04-24 79 views
0

我正试图编写一个过程,在行数未知的情况下连接表中的所有行。WHILE循环在Teradata程序

我有这个代码,但它不工作。

CREATE PROCEDURE Test (OUT r VARCHAR(3000)) 

BEGIN 
DECLARE RowCnt INT; 
DECLARE CurrRow INT ; 
SET CurrRow = 1, 
     r = 'SELECT ', 
     RowCnt = (SELECT COUNT(*) 
        FROM tableWithSQLStmnts 
        )  
WHILE CurrRow <= RowCnt DO 
BEGIN 
      SET r = r + 
      CASE WHEN CurrRow = 1 
       THEN 'MAX(CASE Seq WHEN ' + CAST(CurrRow AS VARCHAR) + ' 
           THEN SqlStmnt 
              ELSE SPACE(0) END) + ' + CHAR(13) 
      WHEN i = RowCnt 
      THEN 'MAX(CASE Seq WHEN ' + CAST(CurrRow AS VARCHAR) + ' 
           THEN '' '' + SqlStmnt 
           ELSE SPACE(0) END) ' + CHAR(13) 
      ELSE 'MAX(CASE Seq WHEN ' + CAST(CurrRow AS VARCHAR) + ' 
           THEN '' '' + SqlStmnt 
           ELSE SPACE(0) END) + ' + CHAR(13) 
      END 
      SET CurrRow = CurrRow + 1 ; 
END ; 
SET r = r + ' 
    FROM (SELECT SqlStmnt, 
        ROW_NUMBER() OVER (PARTITION BY TabName ORDER BY SQlStmnt) 
      FROM tableWithSQLStmnts t) D (SqlStmnt, Seq) 
      GROUP BY TabName;' 

END WHILE; 
END 

; 

,我发现了以下错误:

  • 语法错误,期望像 ';'在一个整数和','。'之间。
  • 意外的文字'SET'。

dnoeth建议的新代码。

REPLACE PROCEDURE Test3 (IN TbName VARCHAR(256)) --, OUT r2 VARCHAR(3000)) 

BEGIN 
DECLARE RowCnt INT; 
DECLARE i INT; 
DECLARE CurrRow INT; 
DECLARE r VARCHAR(3000); 
DECLARE r2 VARCHAR(3000); 
SET CurrRow = 1; 
SET r = 'SELECT '; 
SET RowCnt = (SELECT COUNT(*) 
       FROM tableWithSQLStmnts 
       WHERE tabname = :TbName 
      ); 

WHILE CurrRow <= RowCnt DO 
    BEGIN 
     SET r = r || 
    'MAX(CASE Seq WHEN ' || CAST(CurrRow AS VARCHAR(10)) || ' 
      THEN '' , '' || SqlStmnt 
      ELSE '''' END) 
    ' 
     || CASE WHEN CurrRow = RowCnt 
       THEN '' 
       ELSE ' || ' 
     END; 
     SET CurrRow = CurrRow + 1 ; 
    END; 
END WHILE; 

SET r = r || ' 
    FROM (SELECT SqlStmnt, 
        ROW_NUMBER() OVER (PARTITION BY TbName ORDER BY SQlStmnt) 
      FROM tableWithSQLStmnts t) D (SqlStmnt) 
      GROUP BY TbName 
      ;'; 

SET r2 = r; 
CALL dbc.sysexecsql(:r); 
END; 

现在我得到这个错误:

[3706] Syntax error: Column name list shorter than select list. 

编辑2:

我现在已经改写这样的:

REPLACE PROCEDURE Test3 (IN TabName VARCHAR(256)) 
DYNAMIC RESULT SETS 1 
BEGIN 
DECLARE RowCnt INT; 
DECLARE Seq INT; 
DECLARE QRY VARCHAR(3000); 
DECLARE CurrRow INT; 
SET QRY= 'INSERT INTO vt21 SELECT '; 
SET CurrRow = 1; 

CREATE VOLATILE TABLE vt21(QRY VARCHAR(3000)) ON COMMIT PRESERVE ROWS; 
SET RowCnt = (SELECT COUNT(*) 
       FROM TestTable 
       WHERE tabname = :TabName 
      ); 
FOR CurrentRefRow AS SourceCursor CURSOR FOR 
      SELECT SqlStmnt 
      FROM TestTable 
      DO 
WHILE CurrRow <= RowCnt 
DO 
    BEGIN 
     SET QRY = QRY || 
     CASE WHEN CurrRow=1 
     THEN 'MAX(CASE Seq WHEN ' || CAST(CurrRow AS VARCHAR(10)) || ' 
      THEN '' , '' || SqlStmnt 
      ELSE '''' END)  ' 

     WHEN CurrRow < RowCnt 
     THEN ', MAX(CASE Seq WHEN ' || CAST(CurrRow AS VARCHAR(10)) || ' 
      THEN '' , '' || SqlStmnt 
      ELSE '''' END) ' 
     WHEN CurrRow=RowCnt 
     THEN ', MAX(CASE Seq WHEN ' || CAST(CurrRow AS VARCHAR(10)) || ' 
      THEN '' , '' || SqlStmnt 
      ELSE '''' END) ' 
       ELSE ' || ' 
     END; 
        SET CurrRow = CurrRow + 1 ; 
     END; 
     END WHILE; 

SET QRY = QRY || ' 
    FROM (SELECT SqlStmnt, Tabname, 
        ROW_NUMBER() OVER (PARTITION BY TabName ORDER BY SQlStmnt) 
      FROM TestTable t) D (Seq, Tabname, SqlStmnt) 
      GROUP BY TabName 
      ;'; 

EXECUTE IMMEDIATE QRY; 
END FOR; 


BEGIN -- return the result set 
     DECLARE resultset CURSOR WITH RETURN ONLY FOR S1; 
     SET QRY = 'SELECT * FROM vt21;'; 
     PREPARE S1 FROM QRY; 
     OPEN resultset; 
    END; 

DROP TABLE vt21; 
END; 

但我发现了以下错误:

CALL失败。位置分配列表具有太多的值。

我试着修改它,但是当我删除一个值比它说列名称列表更长,然后选择列表。

+0

看起来像MS SQL Server的SP,没有'在Teradata的,没有'SPACE/CHAR'的'OUT'参数的用法+'到Concat的字符串是错误的,很多分号丢失。 – dnoeth

+0

@dnoeth,谢谢你的帮助。你能否澄清为什么OUT参数的使用是错误的,以及我在哪里丢失分号?我是程序新手,我很难找到文档。 – Barbara

+0

根据标准SQL,只能设置OUT参数,但不能读取(您需要声明一个最终分配给out变量的中间变量)。 – dnoeth

回答

1

这被转换为有效的语法为Teradata /标准SQL(以及一些简化):

REPLACE PROCEDURE Test (OUT r2 VARCHAR(3000)) 

BEGIN 
DECLARE RowCnt INT; 
DECLARE i INT; 

DECLARE CurrRow INT; 
DECLARE r VARCHAR(3000); 

SET CurrRow = 1; 
SET r = 'SELECT '; 
SET RowCnt = (SELECT Count(*) 
       FROM tableWithSQLStmnts 
      ); 

WHILE CurrRow <= RowCnt DO 
    BEGIN 
     SET r = r || 
    'MAX(CASE Seq WHEN ' || Cast(CurrRow AS VARCHAR(10)) || ' 
      THEN '' '' || SqlStmnt 
      ELSE '''' END) 
    ' 
     || CASE WHEN CurrRow = RowCnt 
       THEN '' 
       ELSE ' || ' 
     END; 
     SET CurrRow = CurrRow + 1 ; 
    END; 
END WHILE; 

SET r = r || ' 
    FROM (SELECT department_name--SqlStmnt, 
        ROW_NUMBER() OVER (PARTITION BY TabName ORDER BY SQlStmnt) 
      FROM tableWithSQLStmnts t) D (SqlStmnt, Seq) 
      GROUP BY TabName 
      ;'; 

SET r2 = r; 
END 
; 

什么的tableWithSQLStmnts的内容?

为什么你想要一条线?有更简单的方法来获得一种LISTAGG

编辑:

根据您的意见(这里和Teradata的开发者交流),它看起来像你想要某种计数适用于每一列。但是,您不需要MAX/CASE/ROW_NUMBER,只需将所有行连接为一个表,然后执行它。这种计算空值在表中的每一列:

REPLACE PROCEDURE Test3 (IN DBName VARCHAR(128),IN TabName VARCHAR(128)) 
DYNAMIC RESULT SETS 1 
BEGIN 

    DECLARE QRY VARCHAR(3000); 

    CREATE VOLATILE TABLE vt21(col VARCHAR(128) CHARACTER SET Unicode, NullCnt BIGINT) ON COMMIT PRESERVE ROWS; 

    SET QRY = 'INSERT INTO vt21 '; 

    FOR c AS 
     SELECT DatabaseName, TableName, ColumnName, 
     Row_Number() 
     Over (PARTITION BY tablename 
       ORDER BY columnname) AS rn, 
     Count(*) 
     Over (PARTITION BY tablename) AS Cnt 
     FROM dbc.ColumnsV 
     WHERE DatabaseName = :DBName 
     AND TableName = :TabName 
    DO 
     SET QRY = QRY 
     || 'SELECT ''' || c.ColumnName 
     || ''', COUNT(CASE WHEN ' || c.columnname 
     || ' IS NULL THEN 1 END) FROM ' 
     || c.DatabaseName || '.' || c.TableName 
     || CASE WHEN c.rn = c.Cnt -- last row 
       THEN ';' 
       ELSE ' UNION ALL ' 
      END; 

    END FOR; 

    EXECUTE IMMEDIATE QRY; 

    BEGIN -- return the result set 
     DECLARE resultset CURSOR WITH RETURN ONLY FOR S1; 
     SET QRY = 'SELECT * FROM vt21;'; 
     PREPARE S1 FROM QRY; 
     OPEN resultset; 
    END; 

    DROP TABLE vt21; 

END; 

CALL Test3('dbc', 'dbcinfoV'); 
+0

非常感谢您的答案@dnoeth。当我尝试打电话给它时,虽然出现此错误: CALL失败。 [5531]命名列表不支持过程的参数。 tableWithSQLStmnts包含从前一个查询中获得并保存在表中的SQL语句。他们基本上是选择'columnName',从表中统计(*)与联盟。所以在该表中,我有一个可变数量的行,这取决于我作为参数传递的表上有多少列。 我需要一行代码,因为我需要将这些语句的所有行放入一行,然后运行该代码。 – Barbara

+0

编辑到上一篇文章。我解决了这个问题,我仍然得到了另一个错误 - 我将编辑主要帖子相应 – Barbara

+0

非常感谢,它完美的作品! – Barbara