Dynamic cursors and WHERE clause Teradata - sql

I'm trying to write a procedure where the databasename is dynamic and is taken from a where clause.
So far I have got this:
CREATE PROCEDURE Test
(IN DBName VARCHAR(100), OUT RowCount DEC(10,2))
BEGIN
DECLARE SqlStr VARCHAR(1000);
DECLARE C1 CURSOR FOR S1;
SET SqlStr = 'SELECT count(*) FROM '|| DBNAME || '.MyTable ';
PREPARE S1 FROM SqlStr;
OPEN C1 USING DBName;
FETCH C1 INTO RowCount;
CLOSE C1;
END;
I would need to add something like this now:
WHERE DBName = (SELECT 'firstpart||EnvName||' FROM EnvTable
WHERE EnvName = (SELECT EnvName FROM EnvTable WHERE Flag = 1 AND Priority = (SELECT MIN(Priority) FROM EnvTable))
Any ideas? Can I add this when I call the procedure?

Sounds like you just need a variable to make this more dynamic:
CREATE PROCEDURE Test
(OUT RowCount DEC(10,2))
BEGIN
DECLARE SqlStr VARCHAR(1000);
DECLARE DBName VARCHAR(100);
DECLARE C1 CURSOR FOR S1;
/*Get your DBName variable loaded using SELECT INTO*/
SELECT 'firstpart' || EnvName INTO DBName
FROM EnvTable
WHERE Flag = 1 AND Priority = (SELECT MIN(Priority) FROM EnvTable);
/*and continue what you were doing*/
SET SqlStr = 'SELECT count(*) FROM '|| DBName || '.MyTable ';
PREPARE S1 FROM SqlStr;
OPEN C1 USING DBName;
FETCH C1 INTO RowCount;
CLOSE C1;
END;
I'm not sure what you were trying to do with firstpart||envname, but this should get you in the ballpark. Essentially you just need to craft a sql statement that makes your dbname variable and then use that in your second query.

Related

Adapting Cursor in Teradata SQL

I have a simple Cursor in SQL Server that I would like to adapt to use in Teradata.
The goal of the cursor is to collect the names of a series of tables and rename them using cursor logic.
I already change most of the cursor code to use in Teradata, but I'm still having some trouble to finish this.
So far I have:
DECLARE varTableOldName VARCHAR(500);
DECLARE varTableNewName VARCHAR(500);
DECLARE vardbName VARCHAR(100);
DECLARE varIDCod VARCHAR(5);
DECLARE varRename VARCHAR(100);
DECLARE varCt INT DEFAULT 0;
DECLARE renameTables CURSOR FOR
SELECT
DBname
,TBname
FROM (
SELECT
DatabaseName AS DBname
,TableName AS TBname
,LastAccessTimeStamp AS LADate
,(CURRENT_DATE - CAST(LastAccessTimeStamp AS DATE)) AS NAccessDate
FROM
DBC.TablesV
WHERE
1=1
AND TableKind = 'T'
AND DatabaseName IN ('PD_BACKUP')
GROUP BY
DatabaseName
,TableName
,LastAccessTimeStamp
,LastAlterTimeStamp
) tbHig
WHERE NAccessDate IS NULL OR NAccessDate >= 180
ORDER BY DBname, TBname
FOR READ ONLY;
OPEN renameTables;
FETCH NEXT FROM renameTables
INTO vardbName, varTableOldName;
WHILE (SQLCODE = 0)
DO
SET varTableNewName = vardbName || '_V' || CAST(EXTRACT(YEAR FROM CURRENT_TIMESTAMP) AS VARCHAR(20)) || '_' || CAST(varCt AS VARCHAR(20));
SET varTableOldName = vardbName || '.' || varTableOldName;
SET varRename = 'RENAME TABLE ' || varTableOldName || ' TO ' || varTableNewName;
EXECUTE IMMEDIATE varRename;
SET varCt = varCt + 1;
FETCH NEXT FROM renameTables INTO vardbName, varTableOldName;
END WHILE;
CLOSE renameTables;
I think the problem is with some syntax details or something.
Can anyone guide me with this please?

Can I create a table with columns named similar to field value of another table, return by SELECT statement

Question Image
When I execute this script, there is a syntax error. What is wrong with this?
use TEST
go
CREATE TABLE newTagsTable
(
(SELECT TEST.dbo.dynamicTags.Alais
FROM TEST.dbo.dynamicTags
WHERE ID = 2) varchar(200)
);
Is this what you need ?
SELECT Alais
into newTagsTable
FROM dynamicTags
WHERE ID = 2;
DEMO
Ok, so something like this then:
create procedure test_proc
as
declare #p_sql varchar(2000);
declare #p_sql_2 varchar(2000);
declare #getid CURSOR;
SET #getid = CURSOR FOR
SELECT Alais
FROM dynamicTags;
begin
set #p_sql = 'create table newTagsTable (';
OPEN #getid
FETCH NEXT
FROM #getid INTO #p_sql_2
WHILE ##FETCH_STATUS = 0
begin
set #p_sql = #p_sql + #p_sql_2 + ' varchar(20),'
FETCH NEXT FROM #getid INTO #p_sql_2
end;
set #p_sql = left(#p_sql, len(#p_sql)-1) + ')';
EXEC (#p_sql);
end;
And then just execute the procedure:
exec test_proc
You can add parameters to this to make it more usable for orher situations...
Here is the demo for this second option:
DEMO

How get information from multiple tables using cursor?

I have a query, that returns multiple tables, something like that:
SELECT TableName, DatabaseName +'.'+ TableName, ColumnName
FROM DBC.Columns
WHERE ColumnName = 'id'
And I need to loop through these tables by looking to the information stored in these tables, in order to get only specific tables.
I tried something like code below, using 'LOOP' and cursor, but it says that Query is invalid (code have been taken from here):
DECLARE cursor_Tables CURSOR FOR
SELECT DatabaseName || '.' || TableName
FROM DBC.Columns
WHERE ColumnName ='id';
OPEN cursor_Tables;
label1:
LOOP
FETCH cursor_Tables into tbName;
IF (SQLSTATE ='02000') THEN
LEAVE label1;
END IF;
CASE WHEN (
SELECT COUNT(*)
FROM prd3_db_tmd.K_PTY_NK01
WHERE id = 0 ) > 0
THEN tbName
END
END LOOP label1;
CLOSE cursor_Tables;
END;
How can I actually deal with this problem? Do I need to use procedure in addition? DBMS is Teradata
You need a Stored Procedure because this is the only place where you can use a cursor in Teradata.
REPLACE PROCEDURE testproc()
DYNAMIC RESULT SETS 1
BEGIN
DECLARE tbName VARCHAR(257);
DECLARE SqlStr VARCHAR(500);
-- temporary table to store the result set
CREATE VOLATILE TABLE _vt_(tbName VARCHAR(257)) ON COMMIT PRESERVE ROWS;
-- your existing query to return the table name
-- Better use ColumnsV instead of Columns
FOR cursor_Tables AS
SELECT DatabaseName || '.' || TABLENAME AS tbName
FROM DBC.ColumnsV
WHERE ColumnName ='id'
DO -- prepare the dynamic SQL ...
SET SqlStr =
'insert into _vt_
select ''' || cursor_tables.tbName || '''
from ' || cursor_tables.tbName || '
where id = 0
having count(*) > 0;
';
-- ... and run it
EXECUTE IMMEDIATE SqlStr;
END FOR;
BEGIN -- return the result set
DECLARE resultset CURSOR WITH RETURN ONLY FOR S1;
SET SqlStr = 'SELECT * FROM _vt_;';
PREPARE S1 FROM SqlStr;
OPEN resultset;
END;
DROP TABLE vt;
END;
If this is SQL Server you can check following SQL cursor, I edited the cursor declaration and the code within
Although they may differ from your requirement, I think you can modify easily
declare #sql nvarchar(max)
declare #tablename nvarchar(100)
DECLARE cursor_Tables CURSOR FOR
SELECT s.name + '.' + o.name
--s.name [schema], o.name [table]
FROM sys.Columns c
inner join sys.objects o on c.object_id = o.object_id
inner join sys.schemas s on s.schema_id = o.schema_id
WHERE c.Name ='id' and o.type = 'U'
/*
SELECT TableName, DatabaseName +'.'+ TableName, ColumnName
FROM DBC.Columns
WHERE ColumnName = 'id'
*/
OPEN cursor_Tables;
FETCH NEXT FROM cursor_Tables INTO #tablename
WHILE ##FETCH_STATUS = 0
BEGIN
-- print #tablename
set #sql = 'select case when count(*) > 0 then ''' + #tablename + ''' else '''' end from ' + #tablename
exec sp_executesql #sql
FETCH NEXT FROM cursor_Tables INTO #tablename
END
CLOSE cursor_Tables;
DEALLOCATE cursor_Tables;
On SQL Server, sp_MsForEachTable undocumented stored procedure can be used instead of a loop structure like a cursor
Please check the below SQL command
EXEC sp_MSForEachTable 'IF EXISTS(select * from sys.columns where name = ''Id'' and object_id = object_id(''?''))SELECT ''?'', COUNT(*) FROM ?'
The syntax may be difficult if you are using the sp_msforeachtable or sp_msforeachdb, but you can find samples on the web
You could create a variable to hold the number of rows and set it equal to the count:
DECLARE #count INT
SELECT #count = COUNT(*)
FROM prd3_db_tmd.K_PTY_NK01
WHERE id = 0
Then use an if statement to select the table if it has rows that meet your criteria:
IF #count > 0
BEGIN
SELECT tbName
END
Also as a side note without having SELECT in front of your CASE statement the syntax is invalid, you may want to try it with just adding SELECT in front of CASE if you don't like the way mentioned above
You need to use dynamic SQL. If you need to see the info on the table, you can create a synonym.
CURSOR cursor_Tables is
SELECT DatabaseName || '.' || TableName AS tbName
FROM DBC.Columns
WHERE ColumnName ='id';
begin
FOR R IN cursor_Tables
LOOP
execute immediate 'CREATE OR REPLACE SYNONYM your_synonym FOR '|| R.tbName ;
select *
from your_synonym;
END LOOP;
END;
Or if you want you can create a view.

Select from table, name is stored in the field

How can I join some data from some table whose name is a field of the dataset?
Like this:
SELECT *
FROM dataset
INNER JOIN dataset.table_name
ON dataset.param_id = (dataset.table_name).id_(dataset.table_name)
You will have to construct the select statement as a string using T-SQL. Then, use the execute command to run it. For example:
DECLARE #sql VARCHAR(MAX);
DECLARE #table_name VARCHAR(100);
SET #table_name = (SELECT TOP 1 table_name FROM dataset) ' TODO set criteria correctly here
SELECT #sql = 'SELECT * FROM dataset INNER JOIN ' & #table_name & ' ON dataset.param_id = ' & #table_name & '.id_' & #table_name & ';'
EXEC (#sql)
Update
This is the syntax for Oracle (quoted from Andrewst's answer here):
DECLARE
TYPE rc_type REF CURSOR;
rc rc_type;
table_rec table%ROWTYPE;
BEGIN
OPEN rc FOR 'select * from table';
LOOP
FETCH rc INTO table_rec;
EXIT WHEN rc%NOTFOUND;
-- Process this row, e.g.
DBMS_OUTPUT.PUT_LINE( 'Name: '||table_rec.name );
END LOOP;
END;
http://www.dbforums.com/oracle/718534-ms-sql-exec-equivalent.html

MySql syntax question

Why does this work perfectly:
SET #columnQuery = 'INSERT INTO ColumnNames SELECT ORDINAL_POSITION, COLUMN_NAME FROM information_schema.columns WHERE TABLE_NAME=''residents'' ORDER BY ORDINAL_POSITION;';
PREPARE STMT FROM #columnQuery;
EXECUTE STMT;
but this does not:
DECLARE TableName VARCHAR(50);
SET #TableName = 'residents';
SET #columnQuery = 'INSERT INTO ColumnNames SELECT ORDINAL_POSITION, COLUMN_NAME FROM information_schema.columns WHERE TABLE_NAME=''' + #TableName + ''' ORDER BY ORDINAL_POSITION;';
PREPARE STMT FROM #columnQuery;
EXECUTE STMT;
and neither does this:
DECLARE TableName VARCHAR(50);
SET #TableName = 'residents';
SET #columnQuery = CONCAT('INSERT INTO ColumnNames SELECT ORDINAL_POSITION, COLUMN_NAME FROM information_schema.columns WHERE TABLE_NAME=''', #TableName, ''' ORDER BY ORDINAL_POSITION;');
PREPARE STMT FROM #columnQuery;
EXECUTE STMT;
Neither does this:
SET #columnQuery = 'INSERT INTO ColumnNames SELECT ORDINAL_POSITION, COLUMN_NAME FROM information_schema.columns WHERE TABLE_NAME=? ORDER BY ORDINAL_POSITION;';
PREPARE STMT FROM #columnQuery;
EXECUTE STMT USING #TableName;
Here is the part of the sproc that is giving me issues:
CREATE PROCEDURE sp_ReturnSingleRowAsXml
(
IN TableName VARCHAR(50)
)
BEGIN
DECLARE columnQuery VARCHAR(1000);
DROP TABLE IF EXISTS ColumnNames;
CREATE TEMPORARY TABLE ColumnNames (
ID INT, ColumnName VARCHAR(50)
);
SET #columnQuery = 'INSERT INTO ColumnNames SELECT ORDINAL_POSITION, COLUMN_NAME FROM information_schema.columns WHERE TABLE_NAME=''' + #TableName + ''' ORDER BY ORDINAL_POSITION;';
PREPARE STMT FROM #columnQuery;
EXECUTE STMT;
select * from ColumnNames;
END;
CALL sp_ReturnSingleRowAsXml('residents');
The select returns nothing when it should return about 40 rows. BUT, if I hard code the word 'residents' IN the #columnQuery, I get results! And yes, there is definitely a table named 'residents'.
Sometimes I get this error, sometimes I don't, depending on whether I'm using CONCAT or just manually concatenating strings:
MySQL Database Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NULL' at line 1
I was able to get it to work with the following:
CREATE PROCEDURE `sp_ReturnSingleRowAsXml`(
IN TableName VARCHAR(50)
)
BEGIN
DROP TABLE IF EXISTS ColumnNames;
CREATE TEMPORARY TABLE ColumnNames (
ID INT, ColumnName VARCHAR(50)
);
SET #tblName = TableName;
SET #columnQuery = 'INSERT INTO ColumnNames SELECT ORDINAL_POSITION, COLUMN_NAME FROM information_schema.columns WHERE TABLE_NAME=? ORDER BY ORDINAL_POSITION;';
PREPARE STMT FROM #columnQuery;
EXECUTE STMT USING #tblName;
select * from ColumnNames;
END
try this:
DECLARE TableName VARCHAR(50);
SET #TableName = 'residents';
SET #columnQuery = 'INSERT INTO ColumnNames SELECT ORDINAL_POSITION, COLUMN_NAME FROM information_schema.columns WHERE TABLE_NAME=? ORDER BY ORDINAL_POSITION;';
PREPARE STMT FROM #columnQuery;
EXECUTE STMT USING #TableName;