2017-04-24 75 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