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
Related
I have two tables in SQL and want to create a new column from the table name.
In the database, there are many many tables and name are following similar pattern e.g. total_abc_001, total_abc_0002etc... I want only tables starting with total_abc to be selected..
And create a new column as num which contains the values of table name last characters like 001, 002.
I have tried like this:
DROP TABLE IF EXISTS TABLE_NEW_XY
CREATE TABLE TABLE_NEW_XY
(
email VARCHAR(MAX),
Profile VARCHAR(MAX)
)
DECLARE #Sql NVARCHAR(MAX) = '',
#TableName VARCHAR(MAX),
#Id INT
DECLARE Table_Cursor CURSOR FOR
SELECT
ROW_NUMBER() OVER (ORDER BY TABLE_NAME ASC) Id,
TABLE_NAME
FROM
INFORMATION_SCHEMA.TABLES
WHERE
TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME LIKE 'total_abc_%'
OPEN Table_Cursor
FETCH NEXT FROM Table_Cursor INTO #Id, #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#Id = 1)
BEGIN
SET #Sql = #Sql + 'SELECT email, Profile FROM '+#TableName
SELECT #SQL
END
ELSE
BEGIN
SET #Sql = #Sql + ' UNION ALL SELECT email, Profile FROM '+#TableName --Modify the columns based on your column names
END
FETCH NEXT FROM Table_Cursor INTO #Id,#TableName
END
CLOSE Table_Cursor
DEALLOCATE Table_Cursor
INSERT INTO TABLE_NEW_XY
EXEC (#Sql)
Now i wanted to add a new column "num in the dynamic sql itself to get the values of the filename in the column as 001,002,003 and so on.
Can you please suggest how to achieve this?
If the number is always 3 digits on the end of the #TableName you can let your dynamic sql hardcode it:
IF (#Id = 1)
BEGIN
SET #Sql = #Sql + 'SELECT email, Profile, ''' + RIGHT(#TableName,3) + ''' tname FROM '+#TableName
SELECT #SQL
END
ELSE
BEGIN
SET #Sql = #Sql + ' UNION ALL SELECT email, Profile, ''' + RIGHT(#TableName,3) + ''' tname FROM '+#TableName --Modify the columns based on your column names
END
This should output:
SELECT email, Profile, '001' tname FROM total_abc_001
UNION ALL SELECT email, Profile, '002' tname FROM total_abc_002
If the n last characters you want to capture are variable you can use the same principle with some more complex find/replace functions
I've written some SQL queries of queries that I later use in Cursors to run each line. They take advantage of Information_Schema,Table_Name,Column_name, which I recently just picked up how to do from forums. Below is an example of one Query I use to delete rows from multiple tables with a criteria. I've left out the cursor code as I'm only focused on trying to build the select query.
DECLARE #devCodeDELETE varchar(20);
SET #devCodeDELETE = '001e';
SELECT 'DELETE FROM ' + TABLE_NAME + ' WHERE devCode = '''+ #devCodeDELETE +''';'
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME like 'DEV_%';
The above Outputs which is then run through cursor code:
DELETE FROM DEV_assembly WHERE devCode = '001e';
DELETE FROM DEV_comments WHERE devCode = '001e';
DELETE FROM DEV_costhistory WHERE devCode = '001e';
DELETE FROM DEV_dates WHERE devCode = '001e';
DELETE FROM DEV_master WHERE devCode = '001e';
etc...
This has been effective for building batch update queries against multiple tables, since all tables join with column "devCode".
I can't seem to figure out how to write a select query that will pivot (is it a pivot?) column names with it's source table, using Table_Name, Column_Name and Information_Schema. Not even sure if it is possible.
The output I'm trying to achieve for now would be :
table1 table1.c1 table1.c2 table1.c3, etc
table2 table2.c1 table2.c2 table2.c3, table2.c4, table2.c5,etc
table3 table3.c1 table3.c3 table2.c3, table2.c4,etc
etc....
I displayed each line with different numbers of columns to illustrate each tables having different number of columns.
If I can get this far, I make the necessary changes to make each line into a query.
Any help would be much appreciated. Thanks in advance.
-UPDATE----
Thanks to the Brian from below I made edits pertaining to my schema:
declare #TableName as varchar(256)
declare #ColumnName as varchar(256)
declare #str as varchar(max)
declare #outstring as varchar(max)
select TABLE_NAME, COLUMN_NAME
into #temp
from INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME LIKE 'DEV_%' AND COLUMN_NAME <> 'devCode' AND COLUMN_NAME NOT LIKE 'ID%'
select distinct TABLE_NAME, CONVERT(varchar(max),'') outstring
into #tempout
from #temp
Declare TableNames cursor FORWARD_ONLY FOR
select * from #tempout
for update of outstring
open TableNames
fetch Next From TableNames into #TableName, #outstring
WHILE ##FETCH_STATUS = 0
BEGIN
SET #outstring = ''
SELECT #outstring = #outstring + ', ' + COLUMN_NAME
FROM #temp
WHERE TABLE_NAME = #TableName
UPDATE #tempout
SET outstring = #outstring
WHERE CURRENT OF TableNames
fetch Next From TableNames into #TableName, #ColumnName
END
close TableNames
deallocate TableNames
select 'INSERT INTO ' + TABLE_NAME + '(devCode' + outstring + ') SELECT ''newCode'',' + SUBSTRING(outstring,2,99999) +' FROM ' + TABLE_NAME + ' WHERE devCode = ''oldCode'';' from #tempout
And the output was as desired! That'll go into a another cursor to run each line.
INSERT INTO DEV_assembly(devCode, asmNote, asmUser) SELECT 'newCode', asmNote, asmUser FROM DEV_assembly WHERE devCode = 'oldCode';
INSERT INTO DEV_comments(devCode, comComment, comDate, comStatus, comExternal) SELECT 'newCode', comComment, comDate, comStatus, comExternal FROM DEV_comments WHERE devCode = 'oldCode';
INSERT INTO DEV_costhistory(devCode, costDate, costQuote, costFactory, costNote, costTimeStamp, costSelect) SELECT 'newCode', costDate, costQuote, costFactory, costNote, costTimeStamp, costSelect FROM DEV_costhistory WHERE devCode = 'oldCode';
INSERT INTO DEV_dates(devCode, datesRecord, datesNote, datesDue, datesStatus, datesComplete) SELECT 'newCode', datesRecord, datesNote, datesDue, datesStatus, datesComplete FROM DEV_dates WHERE devCode = 'oldCode';
INSERT INTO DEV_dimensions(devCode, dimNote, dimDimension) SELECT 'newCode', dimNote, dimDimension FROM DEV_dimensions WHERE devCode = 'oldCode';
etc....
You can't do a SQL pivot without knowing the columns in advance... unless you do some dynamic SQL. Even then the number of columns is fixed so you'd have an enormous list of columns, one for every column title in the entire database.
What you can do is run a cursor though all the tables to build up a string with all the columns you need... such as:
declare #TableName as varchar(256)
declare #ColumnName as varchar(256)
declare #str as varchar(max)
declare #outstring as varchar(max)
select TABLE_NAME, COLUMN_NAME
into #temp
from INFORMATION_SCHEMA.COLUMNS
select distinct TABLE_NAME, CONVERT(varchar(max),'') outstring
into #tempout
from #temp
Declare TableNames cursor FORWARD_ONLY FOR
select * from #tempout
for update of outstring
open TableNames
fetch Next From TableNames into #TableName, #outstring
WHILE ##FETCH_STATUS = 0
BEGIN
SET #outstring = ''
SELECT #outstring = #outstring + ' ' + COLUMN_NAME
FROM #temp
WHERE TABLE_NAME = #TableName
UPDATE #tempout
SET outstring = #outstring
WHERE CURRENT OF TableNames
fetch Next From TableNames into #TableName, #ColumnName
END
close TableNames
deallocate TableNames
select * from #tempout
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.
I have fairly new to using SQL, currently I have a table that has a column that contains the names of all the tables I want to use for one query, so what I want to do is to loop through that column and go to every single one of these tables and then search one of their columns for a value (there could be multiple values), so whenever a table contains the value, I will list the name of the table. Could someone give me a hint of how this is done? Is cursor needed for this?
I don't have enough reputation to comment but is the table with the column that contain the table names all in one column, meaning that all the table names are comma separated or marked with some sort of separator? This would cause the query to be a little more complicated as you would have to take care of that before you start looping through your table.
However, this would require a cursor, as well as some dynamic sql.
I will give a basic example of how you can go about this.
declare #value varchar(50)
declare #tableName varchar(50)
declare #sqlstring nvarchar(100)
set #value = 'whateveryouwant'
declare #getTableName = cursor for
select tableName from TablewithTableNames
OPEN #getTableName
fetch NEXT
from #getTableName into #tableName
while ##FETCH_STATUS = 0
BEGIN
set #sqlstring = 'Select Count(*) from ' + #tableName + 'where ColumnNameYouwant = ' + #value
exec #sqlstring
If ##ROWcount > 0
insert into #temptable values (#tableName)
fetch next
from #getTableName into #tableName
END
select * from #temptable
drop table #temptable
close #getTableName
deallocate #getTableName
I'm currently not able to test this out as for time constraint reasons, but this is how I would go about doing this.
You could try something like this:
--Generate dynamic SQL
DECLARE #TablesToSearch TABLE (
TableName VARCHAR(50));
INSERT INTO #TablesToSearch VALUES ('invoiceTbl');
DECLARE #SQL TABLE (
RowNum INT,
SQLText VARCHAR(500));
INSERT INTO
#SQL
SELECT
ROW_NUMBER() OVER (ORDER BY ts.TableName) AS RowNum,
'SELECT * FROM ' + ts.TableName + ' WHERE ' + c.name + ' = 1;'
FROM
#TablesToSearch ts
INNER JOIN sys.tables t ON t.name = ts.TableName
INNER JOIN sys.columns c ON c.object_id = t.object_id;
--Now run the queries
DECLARE #Count INT;
SELECT #Count = COUNT(*) FROM #SQL;
WHILE #Count > 0
BEGIN
DECLARE #RowNum INT;
DECLARE #SQLText VARCHAR(500);
SELECT TOP 1 #RowNum = RowNum, #SQLText = SQLText FROM #SQL;
EXEC (#SQLText);
DELETE FROM #SQL WHERE RowNum = #RowNum;
SELECT #Count = COUNT(*) FROM #SQL;
END;
You would need to change the "1" I am using as an example to the value you are looking for and probably add a CONVERT/ CAST to make sure the column is the right data type?
You actually said that you wanted the name of the table, so you would need to change the SQL to:
'SELECT ''' + ts.TableName + ''' FROM ' + ts.TableName + ' WHERE ' + c.name + ' = 1;'
Another thought, it would probably be best to insert the results from this into a temporary table so you can dump out the results in one go at the end?
I have one outer cursor and one inner cursor also have two tables to work with. Now with the outer cursor i'm making new columns in the table 1 and naming them by the values from the table two, and that works just fine. Problem is with the inner cursor witch i used to insert the values into those new columns from one specific column from another table. This seams not to work, but what confusing me is that i do not get any error messages. Now i hope you understand what i'm trying to do, here is the code so comment for more description about the problem :
DECLARE #rbr_param nvarchar(255)
DECLARE #vrednost nvarchar(255)
DECLARE #cName nvarchar(255)
DECLARE #sql nvarchar (255)
DECLARE curs CURSOR FOR SELECT DISTINCT rbr_param FROM dbo.parametri_pomocna ORDER BY rbr_param
OPEN curs
FETCH NEXT FROM curs
INTO #rbr_param
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cName = 'P_'+#rbr_param+'_P'
EXEC('ALTER TABLE dbo.Parametri ADD ' + #cName + ' nvarchar(255)')
DECLARE vrd CURSOR FOR SELECT DISTINCT vrednost FROM dbo.parametri_pomocna
OPEN vrd
FETCH NEXT FROM vrd
INTO #vrednost
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'INSERT INTO dbo.Parametri'+(#cName)+ ' SELECT vrednost FROM dbo.parametri_pomocna WHERE vrednost = '+#vrednost+ ' AND rbr_param = '+#rbr_param
if exists (select * from INFORMATION_SCHEMA.COLUMNS where table_name = 'dbo.Parametri' and column_name = '#cName')
begin
exec(#sql)
end
FETCH NEXT FROM vrd
INTO #vrednost
END --end vrd
CLOSE vrd
DEALLOCATE vrd
FETCH NEXT FROM curs
INTO #rbr_param
END
CLOSE curs
DEALLOCATE curs
You have two problems here:
if exists ( select * from INFORMATION_SCHEMA.COLUMNS
where table_name = 'dbo.Parametri'
and column_name = '#cName'
)
(1) This view will never have table_name = schema name and table name.
(2) You have enclosed your variable name in single quotes for some reason.
For both of these reasons, your IF condition will never return true.
Try:
IF EXISTS
(
SELECT 1 FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.Parametri')
AND name = #cName
)
(And here is why I prefer catalog views over INFORMATION_SCHEMA.)
Also this double-nested cursor thing seems quite inefficient and a lot more code than necessary to achieve what I think you're trying to do. How about something like this instead:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'ALTER TABLE dbo.Parametri ADD '
+ QUOTENAME('P_' + rbr_param + '_P') + ' NVARCHAR(255);'
FROM dbo.parametri_pomocna GROUP BY rbr_param;
EXEC sp_executesql #sql;
SET #sql = N'';
SELECT #sql = #sql + N'INSERT dbo.Parametri('+QUOTENAME('P_' + rbr_param + '_P')+ ')
SELECT vrednost
FROM dbo.parametri_pomocna WHERE rbr_param = ''' + rbr_param + '''
GROUP BY vrednost;'
FROM dbo.parametri_pomocna
GROUP BY rbr_param;
EXEC sp_executesql #sql;