How to iterate through each row in sql server? - sql

My query returns 26 table names.
select name from sys.tables where name like '%JPro_VP_Service%'
Now I'm trying to write a query to check in every table return from the above query.
--consider this is my first table
select * from JPro_VP_Service
where row_id like '%1-101%' or row_id like '%1-102%'
-- likewise I want to search in 26 tables return from above query
I think I need to write for or cursor to accomplish this.
Can anyone help me how to achieve this?

The easiest way to do this is
Try this:
SELECT 'select * from ' + name
+ ' where row_id like ''%1-101%'' or row_id like ''%1-102%'''
FROM sys.tables
WHERE name LIKE '%JPro_VP_Service%'
you will get all tables together with the same conditions. You could execute them together.

Yes, you would have to use a cursor for this, and probably also dynamic sql
Also see
Generate dynamic SQL statements in SQL Server
Dynamic SQL PROs & CONs

DECLARE #mn INT
DECLARE #mx INT
DECLARE #tblname VARCHAR(100);
WITH cte
AS (SELECT Row_number()
OVER (
ORDER BY (SELECT 0)) AS rn,
name
FROM sys.tables
WHERE name LIKE '%JPro_VP_Service%')
SELECT #mn = Min(rn),
#mx = Max(rn)
FROM cte
WHILE( #mn >= #mx )
BEGIN
SELECT #tblname = name
FROM cte
WHERE rn = #mn
SELECT *
FROM #tblname
WHERE row_id LIKE '%1-101%'
OR row_id LIKE '%1-102%'
--Do something else
SET #mn=#mn + 1
END

This route may work, though you might want the results saved to a table:
DECLARE #tables TABLE(
ID INT IDENTITY(1,1),
Name VARCHAR(100)
)
INSERT INTO #tables (Name)
SELECT name
FROM sys.tables
WHERE name like '%JPro_VP_Service%'
DECLARE #b INT = 1, #m INT, #table VARCHAR(100), #cmd NVARCHAR(MAX)
SELECT #m = MAX(ID) FROM #tables
WHILE #b <= #m
BEGIN
SELECT #table = Name FROM #tables WHERE ID = #b
SET #cmd = 'select * from ' + #table + '
where row_id like ''%1-101%'' or row_id like ''%1-102%''
'
EXECUTE sp_executesql #cmd
SET #b = #b + 1
SET #cmd = ''
END

Related

T-SQL query gone wrong exec not in table

I'm trying to build a query which puts his output in a table.
The exec(#inloop_query) doesn't know a declared table from before.
(that part between the ------------------
Is this possible or do I try to do something that doesn't work?
please advise.
(The error I've is : Must declare the table variable "#inloop_table". Severity 15 State 2)
DECLARE #frame_db_name VARCHAR(max)
DECLARE #frame_db_id INT
DECLARE #frame_table TABLE (
db_id INT ,
names VARCHAR(max))
DECLARE #frame_count INT
DECLARE #frame_count_max INT
SET #frame_count = 1
SET #frame_count_max = 0
SELECT #frame_count_max = count (name) FROM sys.databases WHERE Name LIKE 'B%' and state_desc = 'online'
INSERT INTO #frame_table SELECT database_id , name FROM sys.databases WHERE Name LIKE 'B%' and state_desc = 'online' ORDER BY database_id
DECLARE #inloop_query VARCHAR(max)
DECLARE #Inloop_table TABLE (
IL_SchemaName VARCHAR(max) ,
IL_TableName VARCHAR(max) ,
IL_IndexName VARCHAR(max) ,
IL_IndexID INT ,
IL_Fragment INT)
IF #frame_count_max <= 0
PRINT '#count_max (<=0) = ' + CAST(#frame_count_max AS VARCHAR)
ELSE
WHILE #frame_count <= #frame_count_max
BEGIN
SELECT #frame_db_name = names , #frame_db_id = db_id FROM #frame_table WHERE db_id IN (SELECT TOP 1 db_id FROM #frame_table ORDER BY db_id)
PRINT '#count_max (>=0) = ' + CAST(#frame_count_max AS VARCHAR)
PRINT '#count = ' + CAST(#frame_count AS VARCHAR(max))
PRINT 'current DB name = ' + CAST(#frame_db_name AS VARCHAR(max))
PRINT 'current DB ID = ' + CAST(#frame_db_id AS VARCHAR(max))
------------------------------------------------------------
SET #inloop_query = '
USE ' + CAST(#frame_db_name AS VARCHAR(max)) +
' INSERT INTO #inloop_table
SELECT SCHEMA_NAME(o.schema_id) AS SchemaName,
OBJECT_NAME(a.object_id) AS TableName,
i.name AS IndexName,
a.index_id AS IndexID,
convert(tinyint,a.avg_fragmentation_in_percent) AS [Fragment]
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL,NULL, ''LIMITED'') AS a
INNER JOIN sys.indexes i ON i.index_id = a.index_id
AND i.object_id = a.object_id
INNER JOIN sys.objects o ON a.object_id = o.object_id
ORDER BY SchemaName, TableName, IndexID'
EXEC(#inloop_query)
------------------------------------------------------------
SET #frame_count = #frame_count + 1
DELETE FROM #frame_table WHERE db_id IN (SELECT TOP 1 db_id FROM #frame_table ORDER BY db_id)
END
#inloop_table is declared outside your #inloop_query; when the latter is executed, it has no idea about this variable. How about using an actual table?
/* comment this out:
DECLARE #inloop_query VARCHAR(max)
DECLARE #Inloop_table TABLE (
IL_SchemaName VARCHAR(max) ,
IL_TableName VARCHAR(max) ,
IL_IndexName VARCHAR(max) ,
IL_IndexID INT ,
IL_Fragment INT)
*/
-- Create an auxiliary table
CREATE TABLE InLoop_Table (
IL_SchemaName VARCHAR(max) ,
IL_TableName VARCHAR(max) ,
IL_IndexName VARCHAR(max) ,
IL_IndexID INT ,
IL_Fragment INT
);
-- ... And use this table in your dynamic sql:
SET #inloop_query = '
USE ' + CAST(#frame_db_name AS VARCHAR(max)) +
' INSERT INTO InLoop_Table ...
-- Finally, clean up:
DROP TABLE InLoop_Table;
The scope of a table variable is specific to a batch, so since your dynamic sql executes as a new batch it falls out of scope and is unrecognised. You could of course declare it within your dynamic sql, but that would be pretty pointless as you couldn't access it later. You have two decent choices:
You can put the insert outside of the sql, e.g.
DECLARE #inloop_query NVARCHAR(MAX) = 'USE Master; SELECT 1, 2, 3;';
DECLARE #inloop_table TABLE (A INT, B INT, C INT);
INSERT #inloop_table
EXEC(#inloop_query);
SELECT * FROM #inloop_table;
Or you can use a temporary table, rather than a table variable. A temporary table has session scope, so is still recognised with EXEC():
CREATE TABLE #inloop_table (A INT, B INT, C INT);
DECLARE #inloop_query NVARCHAR(MAX) = 'USE Master; INSERT #inloop_table SELECT 1, 2, 3;';
EXEC(#inloop_query);
SELECT * FROM #inloop_table;
I would also recommend using a properly declared cursor rather than a WHILE loop iterating through a table variable. The key aspect here being properly defined. Often people just use DECLARE .. CURSOR FOR SELECT.. and the default options are much slower and more memory consuming than if you tell the cursor that you won't be making updates, won't be moving backwards etc.
DECLARE DBCursor CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
SELECT database_id , name
FROM sys.databases
WHERE Name LIKE 'B%' and state_desc = 'online'
ORDER BY database_id;
OPEN DBCursor;
FETCH NEXT FROM DBCursor INTO #frame_db_id, #frame_db_name;
WHILE ##FETCH_STATUS = 0
BEGIN
-- DO WHATEVER YOU NEED WITH EACH DB
FETCH NEXT FROM DBCursor INTO #frame_db_id, #frame_db_name;
END
CLOSE DBCursor;
DEALLOCATE DBCursor;
One final comment, is that I always perfer sp_executesql over EXEC() and this article pretty much covers why, in this instance it doesn't make much difference, but it is worth noting.

Select all from tables where table names are from another table in SQL

I have a temp table which has a TableName column. I would like to loop through the temporary table and select everything in the the table (where table is the TableName column in the temp table).
I have been looking through the following link and related links however I am unable to adapt it to my needs. Any help is greatly appreciated.
I am using SQL Server 2014
Something which i have tried
Declare #id int
WHILE EXISTS(SELECT * FROM ##tt_tableList)
BEGIN
Select Top 1 #id = Id from ##tt_tableList
-- Do the work --
declare #query nvarchar(max)
set #query = 'Select * from (select TableName from ##tt_tablelist where id = '' +Cast(#id as nvarchar(50))+'')'
select #query
declare #tableName nvarchar(50)
set #tableName = (select TableName from ##tt_tableList where id = #id)
select #tableName
execute(#query)
-- Scrap the ID and Move On --
Delete ##tt_tableList where ID = #id
END
If I understood you correctly this is what you are asking for:
DECLARE #tbl table (TableName varchar(50))
insert into #tbl values ('SomeTableName')
insert into #tbl values ('AnotherTableName')
DECLARE #Tables VARCHAR(8000)
SELECT #Tables = COALESCE(#Tables + CHAR(13), '') + 'SELECT * FROM '+ TableName
FROM #tbl
exec(#Tables)
Just insert your table names in #tbl
I tried this based on answer from one of our fellow stack overflower and it works.
DECLARE #Tables VARCHAR(8000)
SELECT #Tables = COALESCE(#Tables + CHAR(13), '') + 'SELECT * FROM '+ TableName + ' Where Event like ''%CM_Manual_Change%'''
FROM ##tt_tableList
select #Tables
exec(#Tables)

How do I get a collection of every value in every column of a table?

I have two tables, Values and SpecialValues.
Values has two columns, RecordID and ValueName.
SpecialValues is a table which contains a single row, and thirty columns named SpecialValueName1, SpecialValueName2, SpecialValueName3, etc.
There are obvious database design problems with this system.
That aside, can someone explain to me how to query SpecialValues so that I can get a collection of all the values of every row from the table, and exclude them from a Select from Values?
There's probably some easy way to do this or create a View for it or something, but I think looking at this code might have broken me for the moment...
EDIT: I'd like a query to get all the individual values from every row and column of a given table (in this case the SpecialValues table) so that the query does not need to be updated the next time someone adds another column to the SpecialValues table.
This creates a #SpecialValuesColumns temporary table to store all the column names from SpecialValues.
It then uses a cursor to insert all the values from each of those columns into another temporary table #ProtectedValues.
It then uses a NOT IN query to exclude all of those values from a query to Values.
This code is bad and I feel bad for writing it, but it seems like the least-worst option open to me right now.
DECLARE #SpecialColumnsCount INT;
DECLARE #Counter INT;
DECLARE #CurrentColumnName VARCHAR(255);
DECLARE #ExecSQL VARCHAR(1024);
SET #Counter = 1;
CREATE TABLE #ProtectedValues(RecordID INT IDENTITY(1,1) PRIMARY KEY NOT NULL, Value VARCHAR(255));
DECLARE #SpecialValuesColumns TABLE (RecordID INT IDENTITY(1,1) PRIMARY KEY NOT NULL, ColumnName VARCHAR(255));
INSERT INTO #SpecialValuesColumns (ColumnName)
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'SpecialValues' AND
DATA_TYPE = 'varchar' AND
CHARACTER_MAXIMUM_LENGTH = 255
SELECT #SpecialColumnsCount = COUNT(*) FROM #SpecialValuesColumns
WHILE #Counter <= #SpecialColumnsCount
BEGIN
SELECT #CurrentColumnName = ColumnName FROM #SpecialValuesColumns WHERE RecordID = #Counter;
SET #ExecSQL = 'INSERT INTO #ProtectedValues (Value) SELECT ' + #CurrentColumnName + ' FROM SpecialValues'
EXEC (#ExecSQL)
SET #Counter = #Counter + 1;
END
SELECT * FROM Values WHERE ValueName NOT IN (SELECT ValueName COLLATE DATABASE_DEFAULT FROM #ProtectedValues)
DROP TABLE #ProtectedValues;
I might have misunderstood but doesn't this do it?
SELECT * FROM Values
WHERE ValueName NOT IN (
SELECT SpecialValueName1 FROM SpecialValues
UNION SELECT SpecialValueName2 FROM SpecialValues
UNION SELECT SpecialValueName3 FROM SpecialValues
etc..
)
You could of course make the subquery into a view instead.
*Edit:
This is quite ugly but should solve your problem:
First Create procedure #1
CREATE PROCEDURE [dbo].[SP1]
As
DECLARE
#Query nvarchar(MAX),
#Table nvarchar(255),
#Columns nvarchar(255)
CREATE TABLE #TempTable (Value nvarchar(255))
SET #Table = 'SpecialValues'
SELECT [COLUMN_NAME]
FROM [INFORMATION_SCHEMA].[COLUMNS]
WHERE [TABLE_NAME] = #Table
DECLARE Table_Cursor CURSOR FOR
SELECT COLUMN_NAME
FROM [INFORMATION_SCHEMA].[COLUMNS]
WHERE [TABLE_NAME] = #Table
OPEN Table_Cursor
FETCH NEXT FROM Table_Cursor INTO #Columns
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #TempTable EXEC SP2 #Columns = #Columns, #Table = #Table
FETCH NEXT FROM Table_Cursor INTO #Columns
END
CLOSE Table_Cursor
DEALLOCATE Table_Cursor
SELECT ValueName FROM Value WHERE Value NOT IN (SELECT * FROM #TempTable)
TRUNCATE TABLE #TempTable
DROP TABLE #TempTable
Then Create procedure #2
CREATE PROCEDURE [dbo].[SP2]
#Columns nvarchar(255) = '',
#Table nvarchar(255)
AS
DECLARE
#Query nvarchar(MAX)
SET #Query = 'SELECT TOP 1 CONVERT(nvarchar, ' + #Columns + ') FROM ' + #Table
EXEC (#Query)
Then lastly execute the procedure
EXEC SP1
You need to unpivot the values in specialvalues. A pretty easy way to do that is with cross apply syntax:
select sv.value
from specialvalues sv cross apply
(values(sv.SpecialValueName1), (sv.SpecialValueName2), . . .
) sv(value)
where sv.value is not null;
You can exclude these from the list using not in, not exists or a left join.
What ever way you cut it, you have to specify the columns in SpecialValues, you can do this with a long set of UNION queries, or use UNPIVOT:
select SpecialValue
from (select SpecialValueName1,SpecialValueName2,SpecialValueName3 from #SpecialValues) p
unpivot (SpecialValue FOR ROW IN (SpecialValueName1,SpecialValueName2,SpecialValueName3))
AS unpvt
You can then incorporate this into a query on Values using NOT IN
select * from [Values] where ValueName not in (
select SpecialValue
from (select SpecialValueName1,SpecialValueName2,SpecialValueName3 from #SpecialValues) p
unpivot (SpecialValue FOR ROW IN (SpecialValueName1,SpecialValueName2,SpecialValueName3))
AS unpvt
)

Determine all columns from a table and write them separated by commas in a variable

--Dummy table
create table table1 (
column_order varchar (100)
)
insert into table1 values ('column1')
insert into table1 values ('column2')
insert into table1 values ('column3')
insert into table1 values ('column4')
insert into table1 values ('column5')
insert into table1 values ('column6')
--Start of select
declare #rowsCount INT
declare #i INT = 1
declare #column varchar(1000) = ''
set #rowsCount = (select COUNT(*) from table1)
while #i <= #rowsCount
begin
set #column = #column + (select column_order from table1 where rowid(table1) = #i) + ', '
set #i = #i + 1
end
select #column
This code has the function ROWID thats an IQ-Sybase funktion, and im not sure what other DBMS can use it. And above you have a example what i want my select to look like.
My problem is, you cant use the ROWID function with sys.column or any other systables. Has anyone an idea how to get the same select as mine without using the ROWID function.
If you are using IQ, i constructed the code so you can just type f5 and see the select statement, after that just drop the dummy table.
Use list(). It works in both the ASA system and IQ catalogs.
drop table if exists table1
go
create local temporary table table1 (
column_order varchar (100)
) in system --create table in system
insert into table1 values ('column1')
insert into table1 values ('column2')
insert into table1 values ('column3')
insert into table1 values ('column4')
insert into table1 values ('column5')
insert into table1 values ('column6')
declare #columns varchar(100)
select #columns = list(column_order) from table1
select #columns
go
I may be not understand your need, because I can't see why you need rowdid.
Usually, in TSQL, I do as follow:
declare #someVar as nvarchar(max)
set #someVar = (select
'[' + c.name + '],' as 'data()'
from
sys.columns c
join sys.tables t on c.object_id = t.object_id
where
t.name = 'SomeTableName'
for xml path(''))
print Left(#someVar, Len(#someVar) - 1)
Maybe you will need to use a cursor:
-- #MyTableID has to be definded somewhere or replace
DECLARE #columns varchar(32000)
DECLARE my_cursor CURSOR FOR
SELECT syscolumn.column_name FROM syscolumn WHERE syscolumn.table_id = #MyTableID
OPEN my_cursor
FETCH NEXT my_cursor into #column
WHILE ##FETCH_STATUS = 0
BEGIN
-- put your magic here
-- e.g.
-- SET #columns = #columns + column
FETCH NEXT my_cursor_pkey into #column
END
CLOSE my_cursor
DEALLOCATE my_cursor
Not tested yet, but something like that should work.

How to store a dynamic SQL result in a variable in T-SQL?

I have a stored procedure which takes 'table name' as parameter. I want to store my 'exec' results to a variable and display using that variable.
Here is my T-SQL stored procedure..
create procedure DisplayTable( #tab varchar(30))
as
begin
Declare #Query VARCHAR(30)
set #Query='select * from ' +#tab
EXEC (#Query)
END
I want to do something like this..
SET #QueryResult = EXEC (#Query)
select #QueryResult
How do i achieve this.. Please help.. I am a beginner..
You can use XML for that. Just add e.g. "FOR XML AUTO" at the end of your SELECT. It's not tabular format, but at least it fulfills your requirement, and allows you to query and even update the result. XML support in SQL Server is very strong, just make yourself acquainted with the topic. You can start here: http://technet.microsoft.com/en-us/library/ms178107.aspx
alter procedure DisplayTable(
#tab varchar(30)
,#query varchar(max) output
)
as
BEGIN
Declare #execution varchar(max) = 'select * from ' +#tab
declare #tempStructure as table (
pk_id int identity
,ColumnName varchar(max)
,ColumnDataType varchar(max)
)
insert into
#tempStructure
select
COLUMN_NAME
,DATA_TYPE
from
INFORMATION_SCHEMA.columns
where TABLE_NAME= #tab
EXEC(#execution)
declare #ColumnCount int = (SELECT count(*) from #tempStructure)
declare #counter int = 1
while #counter <= #ColumnCount
BEGIN
IF #counter = 1
BEGIN
set #query = (SELECT ColumnName + ' ' + ColumnDataType FROM #tempStructure where pk_id= #counter)
END
IF #counter <> 1
BEGIN
set #query = #query + (SELECT ',' + ColumnName + ' ' + ColumnDataType FROM #tempStructure where #counter = pk_id)
END
set #counter = #counter + 1
END
END
When you execute the SP, you'll now get a return of the structure of the table you want.
This should hopefully get you moving.
If you want the table CONTENTS included, create yourself a loop for the entries, and append them to the #query parameter.
Remember to delimit the #query, else when you read it later on, you will not be able to restructure your table.
First of all you have to understand that you can't just store the value of a SELECTon a table in simple variable. It has to be TABLE variable which can store the value of a SELECTquery.
Try the below:
select 'name1' name, 12 age
into MyTable
union select 'name2', 15 union
select 'name3', 19
--declaring the table variable and selecting out of it..
declare #QueryResult table(name varchar(30), age int)
insert #QueryResult exec DisplayTable 'MyTable'
select * from #QueryResult
Hope this helps!