Implementing a for loop in SQL to update rows - sql

How do I write the below code in SQL? The query would update each column in the table from the result.
DECLARE #table_list NVARCHAR -- What data type do I use to hold a list?
DECLARE #column_list NVARCHAR
SET #table_list = (SELECT DISTINCT [ID_TABLE_NAME] FROM dbo.VMO) -- there are more than one results
SET #column_list = (SELECT DISTINCT [USR_COL_NAME] FROM dbo.VMO) -- there are more than one results
foreach(#table IN #table_list)
{
foreach(#column IN #column_list)
{
UPDATE #table
SET #column = '101211'
WHERE #column = '10120'
}
}

You can use CURSORs to act like a foreach loop:
DECLARE #table NVARCHAR(255)
DECLARE #column NVARCHAR(255)
DECLARE OUTER_CURSOR CURSOR
FOR SELECT DISTINCT [ID_TABLE_NAME] FROM dbo.VMO
OPEN OUTER_CURSOR
FETCH NEXT FROM OUTER_CURSOR INTO #table
WHILE (##FETCH_STATUS <> -1)
BEGIN
DECLARE INNER_CURSOR CURSOR
FOR SELECT DISTINCT [USR_COL_NAME] FROM dbo.VMO
OPEN INNER_CURSOR
FETCH NEXT FROM INNER_CURSOR INTO #column
WHILE (##FETCH_STATUS <> -1)
BEGIN
DECLARE #strQuery NVARCHAR(MAX)
SET #strQuery = 'UPDATE [' + #table + '] SET [' + #column + '] = ''101211'' WHERE [' + #column + '] = ''10120'';'
EXEC(#strQUERY)
FETCH NEXT FROM INNER_CURSOR INTO #column
END
CLOSE INNER_CURSOR
DEALLOCATE INNER_CURSOR
FETCH NEXT FROM OUTER_CURSOR INTO #table
END
CLOSE OUTER_CURSOR
DEALLOCATE OUTER_CURSOR

T-SQL doesn't have a FOR loop, perhaps it has a WHILE (Transact-SQL)
Use SQL like below:
DECLARE #table_list NVARCHAR -- What data type do I use to hold a list?
DECLARE #column_list NVARCHAR
SET #table_list = (SELECT DISTINCT [ID_TABLE_NAME] FROM dbo.VMO) -- there are more than one results
SET #column_list = (SELECT DISTINCT [USR_COL_NAME] FROM dbo.VMO) -- there are more than one results
WHILE #column_list > #table_list
BEGIN
UPDATE #table
SET #column = '101211'
WHERE #column = '10120'
END;

Related

How do I select only the columns with no data in them?

I am trying to return only columns with no data in them (all NULL values). Is there an easy and quick way to do that with a select statement?
Thanks,
Editing based on response
This will return a list of all the columns and the number of records within it.
All you have to do is filter the output to non_null
declare #table_name as varchar(128) = ''; -- your table name here
declare #column_name as varchar(128);
declare #sql as nvarchar(4000);
declare #count_column int;
declare #tmp_table_result as table (
id int identity(1,1),
table_object_id bigint,
column_name varchar(128),
non_null_records int
);
declare csr cursor for
select name
from sys.columns where object_id = OBJECT_ID(#table_name);
open csr;
fetch next from csr into #column_name;
WHILE ##FETCH_STATUS = 0
BEGIN
set #sql = 'select #cnt_col = count(' + #column_name + ') from ' + #table_name;
print #sql
execute sp_executesql #sql, N'#cnt_col int output', #cnt_col=#count_column OUTPUT;
insert into #tmp_table_result (table_object_id, column_name, non_null_records)
values (
object_id(#table_name),
#column_name,
#count_column
);
fetch next from csr into #column_name;
END
close csr;
deallocate csr;
select *
from #tmp_table_result
Try this:
select * from table where 1=2

Create tables based on the rows value in a table in sql

My requirement is some thing like this
I have a table tblDetails which contains all the details about the table
TblCol TblName ColNo ColName Type Length Sacle KeyType description
Now based on values in tblDetails I want to create the tables.
Here is a (very) basic example of how to approach this sort of dynamic SQL. This example does not add indexes, keys, or anything fancy - just some tables and columns with datatypes, but it should get you started in the right direction:
DECLARE #sql NVARCHAR(MAX)
DECLARE #tblName nvarchar(255)
DECLARE tbl_cursor CURSOR LOCAL FOR
SELECT DISTINCT tblName
FROM tblDetails
ORDER BY tblName
OPEN tbl_cursor
FETCH NEXT FROM tbl_cursor INTO #tblName
--OUTER LOOP THROUGH TABLES:
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'CREATE TABLE ' + #tblName + '( ';
DECLARE #colName nvarchar(255),
#type nvarchar(50),
#length nvarchar(50),
#ctr int = 0;
DECLARE col_cursor CURSOR LOCAL FOR
SELECT [colName], [Type], [Length]
FROM tblDetails
WHERE tblName = #tblName
ORDER BY ColNo
OPEN col_cursor
FETCH NEXT FROM col_cursor INTO #colName, #type, #length
--INNER LOOP THROUGH COLUMNS
WHILE ##FETCH_STATUS = 0
BEGIN
IF #ctr != 0 --if this is not the first column, prefix w/ comma
SET #sql += ', ';
SET #sql += '[' + #colName + '] ' + #type;
IF #type IN ('CHAR','VARCHAR','NCHAR','NVARCHAR','BINARY','VARBINARY') --add size if appropriate
SET #sql += '(' + #length + ')'
SET #ctr += 1;
FETCH NEXT FROM col_cursor INTO #colName, #type, #length
END --inner loop
CLOSE col_cursor
SET #sql += ')';
EXECUTE sp_executesql #sql
FETCH NEXT FROM tbl_cursor INTO #tblName
END --outer loop
CLOSE tbl_cursor

How to Loop table creation

I have the following code which works fine for one table but I am trying to figure out how to make it loop.
DECLARE #sql NVARCHAR(2000)
DECLARE #table VARCHAR(100)
DECLARE #command NVARCHAR(1000)
SELECT #table = ( VV.SRC_CD )
FROM VV
SET #sql = 'SELECT [CD],[SRC_CD],[SRC_CD_DESC],[DSC],[REFRESH_DT],[GEN_DSC] = CAST(null as VARCHAR(50)) INTO dbo.vv_' + #table
+
' FROM [vv] A WHERE A.SRC_CD <> ''GEN'' AND A.DSC <> ''NO DATA'' AND A.DSC <> ''(BLANK) NO'' AND A.src_cd = '''
+ #table + ''''
EXEC Sp_executesql
#stmt = #sql
The code that populates the #table variable returns one row. What I am trying to do is get this procedure to loop one time for each distinct value in the table for that row. The distinct clause doesn't work here and I have tried a basic table array with no luck.
Do I need to modify this to use a cursor?
It sounds like you're in need of a cursor, something like:
DECLARE #Iterator NVARCHAR(100)
,#sql NVARCHAR(MAX)
DECLARE xyz CURSOR
FOR
--Select stuff to iterate over
SELECT DISTINCT SRC_CD
FROM w
OPEN xyz
FETCH NEXT FROM xyz
INTO #Iterator
WHILE ##FETCH_STATUS = 0
BEGIN
--Do stuff
SET #sql = 'SELECT [CD],[SRC_CD],[SRC_CD_DESC],[DSC],[REFRESH_DT],[GEN_DSC] = CAST(null as VARCHAR(50))
INTO dbo.vv_' + #Iterator +'
FROM [vv] A
WHERE A.SRC_CD <> ''GEN''
AND A.DSC <> ''NO DATA''
AND A.DSC <> ''(BLANK) NO''
AND A.src_cd = ''' + #Iterator + ''''
PRINT (#sql)
FETCH NEXT FROM xyz
INTO #Iterator
END
CLOSE xyz
DEALLOCATE xyz
GO
I left PRINT in there so you can ensure the output is as desired before executing.

How to do a huge search for Primary Key ID's that is used across the database where these Primary Key ID's have similar values in columns

BackDrop: We are researching why a number of accounts were missed in a process. We have went back to as far as we have data. We now have a rather large list of accounts that for whatever reason were missed. Now this process without going into too much detail is VERY VERY complex and we need to know why these accounts and only these accounts were missed. As any DataBase we have many many automated procedures that run all the time, so there is really at this point no telling what on earth happened to cause these accounts to get missed. My only bet I think at solving this is to find similarities between these accounts. Obviously we have tried looking at the more common places and have since found nothing.
Issue: I want to use SQL to return all the tablenames and columnnames in our database Where these list of accounts have the same value in a column or columns of a table. I have created a query to find tablenames, columns, and so forth but dont know how to bring it all together to create one query that will give me all the results I want. I am certain that a cursor will need to be used and lots of inner joining but I am just not sure how this should be done.
Again:
Lets say I have account numbers 123456 and 654321 and I know our DataBase has 3,000 tables with a column reference to account number with a name of either AccountNumber, AccountNum, or Account. I want to search and find all tables that have a column with the name AccountNumber, AccountNum, or Account that has a value of 123456 or 654321. Then with these tables, for each table I want to take the rows Where the column whether the name be AccountNumber, AccountNum, or Account has a value of either 123456 and 654321 and then for each of those rows I want to check each column of each row to see if the columns on a row for account number 123456 is eqaul to a column on a row for account number 654321 , if so then I want it to return the column name and the tablename. This way I can see what these accounts have in common.
ADVANCED PORTION:
IF some poor soul is able to do the above then I'd also like to create a query that will return
The tablename and when it was updated. I would get the updated value by checking each column in each table and if the column has a type of "timestamp" or a default value of "GetDate()" then that column would be used as updated. In final result set that shows were all changes have happened for those account nubmers it will order by updated.
A first approach, rustic (I'm not that used to T-SQL, I did more PL/SQL), but which may help you going further, AND TESTED IN SQL SERVER 2008. Hope it works in 2005...)
So, we create two procedures, one calling the other
The provided code can only check, in one time
- for 2 differents IDs
- for all concerned fields (Account, AccountNum, AccountNumber)
The idea (checking for AccountNumber column)
Find the tables (in table INFORMATION_SCHEMA.columns, which lists your database tables) which have a column with one of the 3 names provided
For every table found :
create a dynamic query :
select count(*) from <THE_TABLE> where <Account_column_name> IN (123456 654321);
If we have 2 results (mean that our Ids are both present in table), we launch the second procedure, with parameters : #TableName = <THE_TABLE>, #FieldName = <Account_column_name>, #FirstId = 123456, #SecondId = 654321
We find the column names for <THE_TABLE> (again in INFORMATION_SCHEMA.columns).
For every column found :
create a dynamic query
select count(*) from <THE_TABLE> as T1
inner join <THE_TABLE> as T2 on T1.<COLUMN_NAME> = T2.<COLUMN_NAME>
where T1.<Account_column_name>= 123456
and T2.<Account_column_name>= 654321
if count(*) = 1, it means that the same value exists in the same column of the same table for the given ids.
In that case, we print <THE_TABLE> and <THE_COLUMN>
To launch search, in sql management studio, just make
EXEC GetSimilarValuesForFieldAndValue 123456, 654321
and in the "Messages" part, you should have a list of "results".
CREATE procedure [dbo].[GetSimilarValuesForFieldAndValue](#FirstId int, #SecondId int)
AS
DECLARE #sql nvarchar(MAX);
DECLARE #params NVARCHAR(MAX);
DECLARE #Count int;
DECLARE #Name NVARCHAR(MAX);
DECLARE #FieldName NVARCHAR(MAX);
DECLARE db_cursor CURSOR for
select TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.columns
where COLUMN_NAME IN('Account', 'AccountNumber', 'AccountNum');
OPEN db_cursor
FETCH next from db_cursor into #Name, #FieldName
while ##FETCH_STATUS = 0
begin
select #sql =
N' SELECT #Count=Count(*) FROM ' + #Name +
N' WHERE ' +#FieldName+' IN (#FirstId,#SecondId)'
SELECT #params = N'#FieldName NVARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
EXEC sp_executesql #sql, #params, #FieldName, #FirstId, #SecondId, #Count OUT
if (#Count = 2)
begin
exec dbo.CompareFields #Name, #FieldName, #FirstId, #SecondId
end
FETCH NEXT FROM db_cursor INTO #Name, #FieldName
end
close db_cursor
DEALLOCATE db_cursor
GO
The second one :
CREATE procedure [dbo].[CompareFields](#TableName NVARCHAR(MAX), #FieldName NVARCHAR(MAX), #FirstId int, #SecondId int)
as
DECLARE #ColumnName NVARCHAR(MAX)
DECLARE #Sql NVARCHAR(MAX)
DECLARE #Params NVARCHAR(MAX)
DECLARE #Count int
DECLARE cfCursor CURSOR FOR
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = ''+#TableName+' '
AND COLUMN_NAME <> ' '+#FieldName+''
OPEN cfCursor
FETCH next from cfCursor into #ColumnName
while ##FETCH_STATUS = 0
begin
select #Sql =
N' SELECT #Count = count(*) from ' +#TableName + ' T1 '+
N' INNER JOIN ' + #TableName + ' T2 ON T1.' +#ColumnName + ' = T2.' + #ColumnName +
N' WHERE T1.' +#FieldName + ' = '+ CAST(#FirstId as varchar) +
N' AND T2.' + #FieldName + ' = '+CAST(#SecondId as varchar)
SELECT #Params =
N'#TableName VARCHAR(MAX), #ColumnName VARCHAR(MAX), '+
N'#FieldName VARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
exec sp_executesql #sql, #Params, #TableName, #ColumnName, #FieldName, #FirstId, #SecondId, #Count OUT
if #Count = 1
begin
--print tableName and column Name if value is identic
print 'Table : ' + #TableName + ' : same value for ' + #ColumnName
end
FETCH NEXT FROM cfCursor INTO #ColumnName
end
close cfCursor
DEALLOCATE cfCursor
GO
I actually had to do this for Guids at one point. Here is the script for doing with Guids. One sec and I'll work on modifying it to suit your needs:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value INT
SET #value = '06B8BD6C-A8EC-4EB3-9562-6666EE86952D'
DECLARE table_cursor CURSOR
FOR select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where system_type_id = 36
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' +
#column + ''' AS COL FROM [' + #table + ']
WITH(NOLOCK) WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor
Updated to handle for searching on a field name:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value UNIQUEIDENTIFIER
SET #value = --ENTER YOUR ACCOUNT NUMBER HERE
DECLARE table_cursor CURSOR
select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where cols.Name = 'AccountNumber'
OR cols.Name = 'AccountNum' OR cols.Name = 'Account'
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' + #column +
''' AS COL FROM [' + #table + '] WITH(NOLOCK)
WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor

SQL Cursor within Stored Procedure to populate string variable

I have a stored procedure that contains a cursor to loop through SQL records and populates the string which I will use later as my email text. I'm trying to print it out to verify before I can proceed with it but it seems to not populate the string. Here is my stored procedure in SQL Server 2005.
CREATE PROCEDURE [dbo].[spBody]
AS
DECLARE #MyCursor CURSOR
DECLARE #emailBody nvarchar(max)
DECLARE #statusName nvarchar(max)
DECLARE #deptCode nvarchar(max)
DECLARE #instructors nvarchar(max)
DECLARE #meetingTime nvarchar(max)
SET #MyCursor = CURSOR FAST_FORWARD For
Select StatusName, DeptCode, Instructors, Description from MyTable where StatusID = (select CAST(value AS INT) from Table2 where ConfigOption = 'RequiredStatus')
Open #MyCursor
FETCH NEXT FROM #MyCursor INTO #statusName, #deptCode, #instructors, #meetingTime
WHILE ##FETCH_STATUS = 0
BEGIN
SET #emailBody = #emailBody + #statusName + ' ' + #deptCode + ' ' + #instructors + ' ' + #meetingTime
FETCH NEXT FROM #MyCursor INTO #statusName, #deptCode, #instructors, #meetingTime
END
CLOSE #MyCursor
Print #emailBody
DEALLOCATE #MyCursor
It's because #emailBody starts out as NULL, and any concatenation with NULL yields NULL by default. Do a
SET #emailBody = '';
at the beginning of your script.
Also, strongly consider adding a SET NOCOUNT ON; statement at the top of your stored procedure -- not having NOCOUNT ON can greatly slow the execution of your proc.
Why do you need a cursor for this string concat. Wont the following query suffix
DECLARE #emailBody nvarchar(max)
Set #emailBody = ''
Select #emailBody = #emailBody + StatusName + ' ' + DeptCode + ' ' + Instructors + ' ' + [Description] from MyTable where StatusID = (select CAST(value AS INT) from Table2 where ConfigOption = 'RequiredStatus')
Print #emailBody