Teradata过程中的WHILE循环

时间:2017-04-24 12:19:23

标签: sql while-loop concatenation 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失败。 [3813]位置分配列表的值太多。

我尝试过修改它,但是当我删除一个值时,列表名列表比选择列表长。

1 个答案:

答案 0 :(得分:1)

这被转换为Teradata / Standard 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,只需为表格连接所有行然后执行它。这会在表的每一列中计算NULL:

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');