Looping through a column in SQL table that contains names of other tables - sql

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?

Related

Verify all columns can convert from varchar to float

I have tried a bunch of different ways like using cursors and dynamic SQL, but is there a fast way to verify that all columns in a given table can convert from varchar to float (without altering the table)?
I want to get a print out of which columns fail and which columns pass.
I am trying this method now but it is slow and cannot get the list of columns that pass or error out.
drop table users;
select *
into users_1
from users
declare #cols table (i int identity, colname varchar(100))
insert into #cols
select column_name
from information_schema.COLUMNS
where TABLE_NAME = 'users'
and COLUMN_NAME not in ('ID')
declare #i int, #maxi int
select #i = 1, #maxi = MAX(i) from #cols
declare #sql nvarchar(max)
while(#i <= #maxi)
begin
select #sql = 'alter table users_1 alter column ' + colname + ' float NULL'
from #cols
where i = #i
exec sp_executesql #sql
select #i = #i + 1
end
I found this code on one of the SQL tutorials sites.
Why all the drop/create/alter nonsense? If you just want to know if a column could be altered, why leave your table in a wacky state, where the columns that can be altered are altered, and the ones that can't just raise errors?
Here's one way to accomplish this with dynamic SQL (and with some protections):
DECLARE #tablename nvarchar(513) = N'dbo.YourTableName';
IF OBJECT_ID(#tablename) IS NOT NULL
BEGIN
DECLARE #sql nvarchar(max) = N'SELECT ',
#tmpl nvarchar(max) = N'[Can $colP$ be converted?]
= CASE WHEN EXISTS
(
SELECT 1 FROM ' + #tablename + N'
WHERE TRY_CONVERT(float, COALESCE($colQ$,N''0'')) IS NULL
)
THEN ''No, $colP$ cannot be coverted''
ELSE ''Yes, $colP$ CAN be converted'' END';
SELECT #sql += STRING_AGG(
REPLACE(REPLACE(#tmpl, N'$colQ$',
QUOTENAME(name)), N'$colP$', name), N',')
FROM sys.columns
WHERE object_id = OBJECT_ID(#tablename)
AND name <> N'ID';
EXEC sys.sp_executesql #sql;
END
Working db<>fiddle
This is never going to be "fast" - there is no great shortcut to having to read and validate every value in the table.

How to retrieve a column name from a table that is stored as a value in another table

I'm pretty new to sql so any help will be much appreciated
I have a table containing a list of table names in a column of a table and I need to retrieve a column called [Last Refreshed] from all the tables listed. The tables all have different structures but they all have the [Last Refreshed] Column. I have managed to insert the tablenames into a sql variable but up to this point I am kind of stuck.
I hope I managed to explain what I need but I have attached my code as well.
Declare #tables nvarchar(max)
Declare #sql nvarchar(max)
Declare #cnt int
DECLARE #Counter int
SET #Counter = 1
DECLARE #RowCount INT
SET #RowCount = (SELECT COUNT(*)
FROM (
SELECT * FROM TABLE_LIST1
UNION
SELECT * FROM TABLE_LIST2) data )
DROP TABLE #DB_DUMMY
CREATE TABLE #DB_DUMMY (
[TABLENAME] VARCHAR(512),
[LAST_REFRESHED] VARCHAR(533)
);
WHILE ( #Counter <= #RowCount)
BEGIN
SELECT #tables = FinalTable, #cnt = Row_num from (
SELECT FinalTable , ROW_NUMBER() OVER(ORDER BY FinalTable DESC) AS Row_num
FROM (
SELECT FinalTable FROM TABLE_LIST1
UNION
SELECT FinalTable FROM ABLE_LIST2) data
group by FinalTable) a
where Row_num = #Counter
--This part doesnt work
INSERT INTO #DB_DUMMY(TABLENAME,LAST_REFRESHED)
SELECT #tables , [Last Refreshed] from #tables
SET #Counter = #Counter + 1
END
exec(#sql)
I expect to see a list of tablenames as well as the last refresh in the temporary table #DB_DUMMY
i add the [Last Refreshed] column to my tables and write this query and give me the correct answer
DROP TABLE IF EXISTS #DB_DUMMY
CREATE TABLE #DB_DUMMY (
[TABLENAME] VARCHAR(512),
[LAST_REFRESHED] VARCHAR(533)
);
DECLARE #COMMAND NVARCHAR(MAX)
SELECT #COMMAND = STRING_AGG(' INSERT INTO #DB_DUMMY SELECT DISTINCT '+CHAR(39)+T.name+CHAR(39)+',['+C.name+'] FROM '+S.name+'.'+T.name + ' GO', CHAR(13)+CHAR(10))
FROM sys.all_columns C
INNER JOIN sys.tables T ON C.object_id = T.object_id
INNER JOIN sys.schemas S ON T.schema_id = S.schema_id
WHERE C.name = 'Last Refreshed'
PRINT(#COMMAND)
EXEC(#COMMAND)
SELECT * FROM #DB_DUMMY
two first line with IF EXISTS is new syntax in sql server 2017
Just a suggestion You could use a INSERT SELECT
INSERT INTO #DB_DUMMY(TABLENAME,LAST_REFRESHED)
SELECT 'TABLE_LIST1', LAST_REFRESHED
FROM TABLE_LIST1
UNION ALL
SELECT 'TABLE_LIST2', LAST_REFRESHED
FROM TABLE_LIST2
UNION ALL
.....
SELECT 'TABLE_LISTN', LAST_REFRESHED
FROM TABLE_LISTN
Try something like this:
declare cur cursor for Select TableName From TABLE_LIST
declare #tablename nvarchar(max)
declare #sqlstring nvarchar(max)
open cur
fetch next from cur into #tablename
while ##fetch_status=0
begin
set #sqlstring = 'SELECT ''' + #tablename + ''' AS ''TABLE'', [LAST_REFRESHED] FROM ' + #tablename
exec sp_executesql #sqlstring
fetch next from cur into #tablename
end
close cur
deallocate cur
;
It is the weekend and I don't have access to a database to test on, so it may need some adjusting. Here is a fiddle with the sample code, but it only returns the first table http://sqlfiddle.com/#!18/a5b55b/2 (I think the fiddle execution mechanism interferes with the cursor.)
This answer is based upon the code here: I have the same column in multiple tables, and want to update that column in all tables to a specific value. How can I do this?
Note that there is no need to maintain a list of tables with the column. You can generate it dynamically from INFORMATION_SCHEMA.COLUMNS
Another possible approach is to generate and execute a dynamic statement (it's not possible to use a variable for the name of a column or a table):
Table:
CREATE TABLE #TableNames (
[TableName] nvarchar(128)
)
INSERT INTO #TableNames
([TableName])
VALUES
(N'Table1'),
(N'Table2'),
(N'Table3'),
(N'Table4'),
(N'Table5')
Statement:
-- Generate statement
DECLARE #stm nvarchar(max) = N''
SELECT #stm = CONCAT(
#stm,
N'INSERT INTO #DB_DUMMY (TABLENAME, LAST_REFRESHED) ',
N'SELECT ''',
[TableName],
N''' AS [TableName], [LastRefreshed] FROM ',
QUOTENAME([TableName]),
N'; '
)
FROM #TableNames
-- Execute statement
PRINT #stm
EXEC sp_executesql #stm

How to fetch row one by one in sql

I am fetching table-names from particular database like this
SELECT name FROM sys.Tables where name like 'some pattern'
Output :
Name
sampletable_123,
sampletable_456,
sample_789.
It can return more than one row like above output. How to fetch row one by one?
Because I want to find out column-name/date from obtained table-name. If date is less than current date, I want to drop that table.
select top 1 Udate from sampletable_123
where Udate < convert(varchar(10),getdate(),101)
delete table sampletable_123.
How to do that?
This will generate dynamic sql script which you can preview and if those are the tables you want to delete you can use EXEC on it.
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = ''
SELECT
#SQL += 'IF (OBJECT_ID(' + name + ') IS NOT NULL AND NOT EXISTS(SELECT TOP 1 uDate FROM ' + name + ' WHERE DATEDIFF(day,uDate,GETDATE()) != 0)
DROP TABLE ' + name + CHAR(10)
FROM sys.Tables
where name like 'some pattern'
PRINT #SQL --print will show you the delete query that was generated
--keep in mind that print wont be able to print the whole query if its bigger than 4000 characters
--but exec will still run it
--another way to preview it is to select the query because select has no limit it's just on one row
--EXEC(#SQL)
You can use a cursor on the above select, and fetch one row at a time.
For each row, you can retrieve the first row of the table and decide to drop it or not.
DECLARE #TABLES CURSOR
DECLARE #MYNAME VARCHAR(100)
SET #TABLES CURSOR FOR
SELECT name FROM sys.Tables WHERE name LIKE 'sample%'
OPEN #TABLES
WHILE 1 = 1 BEGIN -- INFINITE LOOP
FETCH NEXT FROM #TABLES INTO #MYNAME
IF ##FETCH_STATUS <> 0 BREAK
IF EXISTS(SELECT TOP 1 Udate FROM #MYNAME WHERE Udate < CONVERT(VARCHAR(10),GETDATE(),101))
DROP TABLE #MYNAME
END
Thanks SubqueryCrunch and Sergio Internicola for ur help.#Sergio Internicola, ur logic is right.I think curser take time.I modified SubqueryCrunch's query.It is working fine with me.
DECLARE #SQL NVARCHAR(4000)
SET #SQL = ' '
SELECT
#SQL += 'IF EXISTS(SELECT TOP 1 udate FROM ' + name + ' WHERE DATEDIFF(day,udate,GETDATE()) != 0)
DROP TABLE ' + name +' '
FROM sys.Tables
where name like 'tbl_%'
PRINT #SQL
EXEC sp_executesql #SQL

SQL: Looping through a column, stored the value as a variable, run SQL, then move on to the next line?

I'm currently shifting roles at my job and trying to teach myself some SQL Skills.
Scenario: I'm in charge of 1 database - 10 tables with 10 Primary Keys. Every month, our code team publishes updates to the tables. I am suppose to drop the tables and generate scripts to create the updated tables.
Rather than just drop the old tables and stored procedures, I want to rename my current tables to preserve the structure/data for whatever reason.
In my database, I have an additional table called "TableUpdateList" with 1 column "TableName" and 10 rows - each row containing the name of the updated column (Row 1 = TableName1, Row 2 = TableName2, Row 3 = TableName3)
I would like to be able to "loop" through the TableUpdateList Table and insert each value into a set of SQL statements.
For Example, here are the SQL statements I want to run:
--drop the previous backup table
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME = '*TableName1*'+'_Old') DROP TABLE TableName1_Old
-- rename the current tables to _old
EXEC sp_rename *TableName1*, TableName1_Old;
I'm trying to find a way to scroll through the column of my TableUpdateList and run the above two statements filling in where I've italicized with whatever value is present in that row.
Just taking a wild stab because I think in order to get an answer here, you have to try something so here is my pseudo-code:
Declare #TableNames as List
For i in #TableNames
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME = '*i*'+'_Old') DROP TABLE TableName1_Old
-- rename the current tables to _old
EXEC sp_rename *i*, TableName1_Old;
Oi, thanks in advance for any help or a point in the right direction to where I could do some further reading about the above online.
You can use sp_executesql with CURSORS for such type of work. Here is what i think you need:
Test objects:
CREATE TABLE TableName1 ( ID INT )
GO
CREATE TABLE TableName2 ( ID INT )
GO
CREATE TABLE TableNames ( Name NVARCHAR(MAX) )
GO
INSERT INTO TableNames
VALUES ( 'TableName1' ),
( 'TableName2' )
Script itself:
DECLARE #name NVARCHAR(MAX) ,
#dropStatement NVARCHAR(MAX),
#renameStatement NVARCHAR(MAX)
DECLARE cur CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT Name
FROM dbo.TableNames
OPEN cur
FETCH NEXT FROM cur INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS ( SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #name + '_Old' )
BEGIN
SET #dropStatement = 'DROP TABLE ' + #name + '_Old'
EXEC sp_executesql #dropStatement
END
SET #renameStatement = 'sp_rename ' + #name + ', ' + #name + '_Old';
EXEC sp_executesql #renameStatement
FETCH NEXT FROM cur INTO #name
END
CLOSE cur
DEALLOCATE cur
After this you should add TableName1 and TableName2 again.
Cursors must be avoided as long as possible.
--Preparing script which would check if the old tables exists. If it does,
--it drops the old table
--e.g. first the value 'Table1' is found in TableUpdateList table.
--Then, Table1_Old is deleted and Table1 is renamed to Table1_Old
SELECT 'DROP TABLE ' + b.name + '_Old; EXEC sp_rename ''' + b.name+ ''', ''' + b.name+ '_Old;''' AS [Action]
INTO #Action
FROM INFORMATION_SCHEMA.TABLES A JOIN TableUpdateList B ON A.TABLE_NAME = b.NAME + '_Old'
DECLARE #sql VARCHAR(8000)
SELECT #sql = COALESCE(#sql + ' ', '') + [Action]
FROM #Action
select #sql
--EXEC (#sql)
First verify the value of variable #sql. Then, uncomment the last line to execute the code.
SQL fiddle

Stored procedure to find number of rows in a table

In a stored procedure I pass a table name as the input variable.
I want to return the number of rows of this table with that stored procedure.
I tried something like this but it did not work:
declare #maxRowCount bigint
exec('set '+ #maxRowCount + ' =(select COUNT(1) from ' + #tableName + ')')
This is SQL Server 2008.
You can try this
CREATE PROCEDURE dbo.sp_selectcount
#tablename NVARCHAR(200)
AS
DECLARE #cmd NVARCHAR (255)
SET #cmd = 'SELECT count(*) from ' + #tablename
EXEC sp_executesql #cmd
The following example should give you something to work with.
-- fully qualify your table name (this is probably an input value in your sproc?)
-- please note that I use system view master.sys.tables as an example table here
DECLARE #tablename NVARCHAR(MAX) = N'[master].[sys].[tables]';
-- build the sql statement that you will execute
DECLARE #sql NVARCHAR(MAX) = N'SELECT COUNT(*) FROM ' + #tablename;
-- create a variable to hold the number of rows later on
DECLARE #nrofrows BIGINT;
-- create a temp table to store the result of executing the sql statement
CREATE TABLE #temp (NrOfRows BIGINT);
-- insert the result of the execution of the sql statement into the temp table
INSERT INTO #temp
EXECUTE(#sql);
-- extract the number of rows from the temp table
SET #nrofrows = (SELECT NrOfRows FROM #temp);
-- check the result so you can test!
PRINT #nrofrows;
If you want good background information on dynamic SQL, check out Erland Sommarskogs article The Curse and Blessings of Dynamic SQL.
You should remove the quotes around #maxRowCount.
Try this:
declare #maxRowCount bigint
exec('set #maxRowCount =(select COUNT(*) from ' + #tableName + ')')
OR
exec('SELECT #maxRowCount = COUNT(*) from ' + #tableName)
Analysis:
With the query you tried, it will execute:
set blablabla = (select count(1) from MyTable)
By removing the quotes:
set #maxRowCount = (select count(*) from MyTable)
You can try this instead.
declare #maxRowCount bigint(5)
exec('SELECT COUNT(*) INTO #maxRowCount FROM ' + #tableName)