Unable to execute the create statement generate by cursor - sql-server

DECLARE #COLNAME VARCHAR(100)
DECLARE #CREATE VARCHAR (1000)
DECLARE #TYPE VARCHAR(1)
SET #COLNAME = ''
SET #TYPE = 5
SET #CREATE = 'CRATE TABLE TABLE_TYPE_' + #TYPE + '('
DECLARE MyCursor CURSOR FOR
SELECT Name FROM LAYOUT WHERE RecordType = #TYPE
ORDER BY Start
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO #COLNAME
WHILE ##FETCH_STATUS = 0
BEGIN
SET #COLNAME = REPLACE(#COLNAME, 'WX-', '')
SET #COLNAME = REPLACE(#COLNAME, '(', '')
SET #COLNAME = REPLACE(#COLNAME, ')', '')
SET #CREATE = #CREATE + '[' + #COLNAME + ']' + ' VARCHAR(1000),'
FETCH NEXT FROM MyCursor INTO #COLNAME
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET #CREATE = LEFT(#CREATE, len(#CREATE) -1) + ')' --get rid of last comma
PRINT (#CREATE) --EXEC (#CREATE)
It seem like there is the maximum limit on MS. When I print the #CREATE, many rows have been cut off.
EX. There are 300 columns but somehow it can only print out up to 200 columns
How can I gerenrate the create table statements without any cut offs.
I know some people will suggest don't use the cursor, but since there are so many columns for one table, it will be very time consuming to type out all the columns name.

You really should not use a cursor for that.
Not only is it possible to do without a cursor, it's simpler:
DECLARE #CREATE VARCHAR (max),
#TYPE int = 5
SET #CREATE = 'CRATE TABLE TABLE_TYPE_' + CAST(#TYPE as varchar) + '('
SELECT #CREATE = #CREATE + REPLACE(
REPLACE(
REPLACE([Name], 'WX-', ''),
'(', ''),
')', '') + ' VARCHAR(1000),'
FROM Layout
WHERE RecordType = #TYPE
SET #CREATE = LEFT(#CREATE, len(#CREATE) -1) + ')' --get rid of last comma
PRINT #CREATE
see fiddle here

Related

How to use while loop to iterate through each table name in SQL Server?

I'm working with Dynamic SQL (still in the learning phase) and I'm stuck at a part where I need to use a WHILE loop:
SET #tableName = (SELECT DISTINCT TableName FROM #dataStructure)
Here basically I want to make sure that the operations inside the while loop should occur for all the tables in the #tableName (defined above). I don't know how I can give this condition as an input for the while loop.
WHILE() #HOW CAN I PUT THE CONDITION HERE????
BEGIN
SET #str = ''
SET #sqlstr = ''
SELECT #table = TableName FROM #dataStructure
SET #str = 'UPDATE a0' + char(13) + char(10)
+ ' SET a0.Mjolnir_Source_ID = CONCAT( '
SELECT #str = #str + IIF(ReferenceTable IS NULL, 'a0.' + columnName , alias + '.Mjolnir_Source_ID') + ','
FROM #dataStructure
WHERE TableName = #tableName AND ReferenceTable IS NOT NULL
ORDER BY columnName
SELECT #str = #str + ') FROM ' + #table + ' a0'
SELECT #sqlstr = #sqlstr + +
+ ' INNER JOIN ' + QUOTENAME(#U4SM_db_name) + '.dbo.' + QUOTENAME(ReferenceTable) + ' ' + alias + char(13) + char(10)
+ ' ON a0.' + columnName + ' = ' + alias + '.' + ReferenceColumn + char(13) + char(10)
FROM #dataStructure
WHERE TableName = #tableName AND ReferenceTable IS NOT NULL
ORDER BY columnPosition
select #str + #sqlstr
select #sqlstr
SET #tableName = #tableName + 1
END
Can anyone please help me out here?
Here's an example of a WHILE loop. Basically, you get the first TableName, then if it's NOT NULL, you do your functions. Then get the next table name, and repeat as necessary.
DECLARE #CurrentTableName nvarchar(100)
DECLARE #CustomSQL nvarchar(4000)
SET #CurrentTableName = (SELECT TOP 1 TableName FROM #dataStructure ORDER BY TableName)
WHILE #CurrentTableName IS NOT NULL
BEGIN
SET #CustomSQL = 'SELECT TOP 10 * FROM ' + #CurrentTableName
EXEC (#CustomSQL)
SET #CurrentTableName = (SELECT TOP 1 TableName FROM #dataStructure WHERE TableName > #CurrentTableName ORDER BY TableName)
END
Note that SQL commands often cannot contain variable names in key spots (e.g., SELECT * FROM #tableName). Instead, you save it as an SQL string (what I've called #CustomSQL above) and then EXEC it (put brackets around the variable name though).
Edit: Do this on a test site first before production, and know where the 'cancel query' button is. It's not often, but it's also not unknown, that the 'getting the next row' part isn't properly written and it just runs in a perpetual loop.
FETCH CURSOR with WHILE. Example:
DECLARE myCursor CURSOR FOR
SELECT DISTINCT TableName FROM #dataStructure;
OPEN myCursor;
FETCH NEXT FROM myCursor INTO #table:Name;
WHILE ##FETCH_STATUS = 0
BEGIN
Print ' ' + #TableName
FETCH NEXT FROM myCursor INTO #TableName;
END;
CLOSE myCursor;
DEALLOCATE myCursor;
GO
Don't recreate the wheel unless you need a better wheel:
sp_MSforeachtable
https://www.sqlshack.com/an-introduction-to-sp_msforeachtable-run-commands-iteratively-through-all-tables-in-a-database/
If you are worried about using an undocumented procedure in production that might change in the future, simply script it out and create your own custom named version.

T-SQL context issue

I have created a script to compare the tables of two databases to find differences. I want to be able to manually set two variables (one for each database) and have the 'USE' statement use the variable value for one of the databases but it changes context and does work correctly.
This is what I am trying to use to populating a variable (#Use) with the USE code to execute
--set #Use = 'USE ' + #NewProdDB + ';SELECT name FROM sys.tables'
--EXEC (#Use)
This is the entire query:
--========================================================================================================
-- Used to find table values in the test upgrade system database not found the newly upgraded system
-- database. Make sure change the system database names before running the query.
-- Also, select the upgraded database to run against
--========================================================================================================
DECLARE #NewProdDB VARCHAR(100)
DECLARE #TestUpgradeDB VARCHAR(100)
DECLARE #Columns VARCHAR (MAX)
DECLARE #Query VARCHAR(MAX)
DECLARE #ResultsTest VARCHAR(MAX)
DECLARE #SetResultsCursor NVARCHAR(MAX)
DECLARE #Fetcher NVARCHAR(MAX)
DECLARE #Use NVARCHAR(50)
DECLARE #ListOfTables VARCHAR(100)
DECLARE #WTF NVARCHAR(max)
DECLARE #rescanCode nvarchar(max)
/* Enter Upgraded system DB here */
SET #NewProdDB = 'zSBSSYS'
/* Enter Test Upgrade system DB here */
SET #TestUpgradeDB = 'TESTzSBSSYS'
--set #Use = 'USE ' + #NewProdDB + ';SELECT name FROM sys.tables'
--EXEC (#Use)
SET NOCOUNT ON
set #rescanCode = 'DECLARE recscan CURSOR FOR
SELECT TABLE_NAME FROM ' + #TestUpgradeDB + '.INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME NOT LIKE ''Mbf%'' AND TABLE_TYPE = ''BASE TABLE''
ORDER BY TABLE_NAME'
exec (#rescanCode)
OPEN recscan
FETCH NEXT FROM recscan INTO #ListOfTables
WHILE ##fetch_status = 0
BEGIN
BEGIN TRY
/* START Table column query */
Declare #Table varchar(60)
set #Table = #ListOfTables
set #table = (SELECT top 1 name FROM sys.tables where name = #Table)
DECLARE
#sql1 nvarchar(max) = ''
,#INTOColumns NVARCHAR(MAX)=''
,#Where nvarchar(max) = ''
,#ColumnID Int
,#Column Varchar(8000)
,#Type Varchar(8000)
,#Length Int
,#del1 varchar(2) = ','
,#CRLF bit = 1
,#aliasfield varchar(5) = '[a].'
DECLARE CSR_Attributes CURSOR FOR
SELECT
[ColumnID] = Col.column_id,
[Column] = Col.Name,
[Type] = [types].name,
[Length] = Col.max_length
FROM sys.objects Obj, sys.columns Col, sys.types [types]
WHERE Obj.OBJECT_ID = Col.OBJECT_ID AND Obj.TYPE = 'U'
AND Col.system_type_id = [types].system_type_id
AND Obj.name = #table
AND Col.name <> 'tstamp'
ORDER BY Obj.name, Col.column_id, Col.name
OPEN CSR_Attributes
FETCH NEXT FROM CSR_Attributes INTO #ColumnID, #Column, #Type, #Length
WHILE ##FETCH_STATUS = 0
BEGIN
set #sql1 += #column + #del1
set #Where += 'b.' + #column + '=a.' + #column + ' and '
set #INTOColumns += '#INTOcol,'
FETCH NEXT FROM CSR_Attributes INTO #ColumnID, #Column, #Type, #Length
END
CLOSE CSR_Attributes
DEALLOCATE CSR_Attributes
SET #Columns = SUBSTRING(#sql1,1,len(#sql1)-1) -- get rid of last comma
SET #Where = SUBSTRING(#Where,1,len(#Where)-4) -- get rid of last 'and'
SET #INTOColumns = SUBSTRING(#INTOColumns,1,len(#INTOColumns)-1) -- get rid of last comma
/* END Table column query */
/* Create SELECT statement here */
SET #ResultsTest='SELECT TOP 1 ' + #Columns + '
FROM ' + #TestUpgradeDB + '..' + #Table + ' A
WHERE NOT EXISTS
(SELECT ' + #Columns + '
FROM ' + #NewProdDB + '..' + #Table + ' B WHERE ' + #Where + ')'
SET #SetResultsCursor = 'DECLARE DataReturned CURSOR FOR ' + #ResultsTest
exec (#SetResultsCursor)
OPEN DataReturned
SET #Fetcher = 'DECLARE #INTOcol NVARCHAR(Max)
FETCH NEXT FROM DataReturned INTO ' + #INTOColumns
exec (#Fetcher)
if ##FETCH_STATUS = 0
begin
CLOSE DataReturned
DEALLOCATE DataReturned
SET #Query='SELECT ' + #Columns + '
FROM ' + #TestUpgradeDB + '..' + #Table + ' A WHERE NOT EXISTS
(SELECT ' + #Columns + ' FROM ' + #NewProdDB + '..' + #Table + ' B WHERE ' + #Where + ')'
select #Table
exec (#Query)
end
else
begin
CLOSE DataReturned
DEALLOCATE DataReturned
end
end try
begin catch
--SELECT ERROR_MESSAGE() AS ErrorMessage;
end catch
FETCH NEXT FROM recscan INTO #ListOfTables
end
CLOSE recscan
DEALLOCATE recscan
SET NOCOUNT OFF
You can't do this:
DECLARE #DB varchar(10) = 'beep';
DECLARE #USE varchar(50) = 'USE '+ #DB;
EXEC sp_sqlexec #USE;
SELECT blah FROM bloop
Database context is only used during the #USE sql, then reverts
You can do this:
DECLARE #DB varchar(10) = 'beep';
DECLARE #SQL varchar(50) = 'USE '+#DB+'; SELECT blah FROM bloop;'
EXEC sp_sqlexec #USE;
That aside, look into SQL Server Data Tools (SSDT)
It does exactly what it seems you are trying to do, schema and/or data compare between databases, and it's very easy to use.

SQL counting in columns inside cursor

What is want to do is the following:
declare #Count numeric (10)
set #Count = 0
set #Count = (select count(*) from FILENAME where COLUMNNAME like '%e%')
print(#tel)
I want it do be done on a few files that have a lot of columns and have a total count for all the columns, so my idea was to get a columnlist of each file by doint something like:
select * from sys.columns
where object_id = ( select object_id from sys.objects where name = 'FILENAME')
Now the easiest way i thought would be using a cursor and have the #Counts added up there in another variable. But the problem i am having is that when COLUMNNAME is in a variable in the cursor i can only (as far as i know) make the query like:
declare #SQL varchar(max)
set #SQL = #SQL + 'set #Count = (select count(*) from FILENAME where '
+ #columnname + ' like ''%e%'' )'
set #tot_Count = #tot_Count + #Count
But as I understand from reading around dynamic sql inside a cursor is a big no.
And though when i see the "print (sql)" the queries seem to be right it doesnt seem to work indeed in a cursos.
Is there another way to get the results i want?
Or am i doing something wrong here?
For reference, this was the idea i had in total:
DECLARE #columnname VARCHAR(50)
DECLARE #tot_Count NUMERIC(10), #Count NUMERIC(10)
set #tot_Count = 0
DECLARE MyCursor CURSOR FOR
SELECT name
from sys.columns
where object_id = ( select object_id from sys.objects where name = 'FILENAME' )
open MyCursor
FETCH NEXT FROM MyCursor INTO #columnname
WHILE ##FETCH_STATUS = 0
BEGIN
declare #SQL varchar(max)
set #SQL =''
set #Count = 0
set #SQL = #SQL + 'set #teller = (select count(*) from FILENAME where ' + #columnname + ' like ''%e%'' )'
print (#SQL)
exec (#SQL)
set #tot_Count = #tot_Count + #Count
FETCH NEXT FROM MyCursor into #name
END
CLOSE MyCursor
DEALLOCATE MyCursor
print (#tot_teller)
Variant 1: You can use dynamic variable instead of cursor operation.
declare #qry varchar(max)
select #qry = isnull(#qry + ' union all', '') + ' select count(*) cnt from ' + object_name(object_id) + ' where ' + name + ' like ''%e%'''
from sys.columns
where object_id = object_id('YourTableName')
set #qry = 'select sum(cnt) from (' + #qry + ') q'
print #qry
exec (#qry)
It's shorter, better, but exec is limited by 4000 chars.
Variant 2: Or you can use your cursor and via dynamic qry insert into your preprepared temp table.
if object_id('tempdb..#tmptbl') is not null drop table #tmptbl
create table #tmptbl (cnt int not null)
declare #colqry varchar(800)
declare mycursor cursor for
select 'insert into #tmptbl (cnt) select count(*) cnt from ' + object_name(object_id) + ' where ' + name + ' like ''%e%'''
from sys.columns
where object_id = object_id('YourTableName')
open mycursor
fetch next from mycursor into #colqry
while ##fetch_status = 0
begin
print #colqry
exec (#colqry)
fetch next from mycursor into #colqry
end
close mycursor
deallocate mycursor
select sum(cnt) from #tmptbl
DECLARE #tot_Count int = 0
DECLARE #Separator varchar(1) = ','
DECLARE #Position INT = 1
DECLARE #ColumnNameList varchar(MAX)
DECLARE #ColumnName varchar(1000)
DECLARE #SQL varchar(max) = ''
SET #ColumnNameList = STUFF(( SELECT name
from sys.columns
where object_id in ( select object_id from sys.objects where name = 'FILENAME' ) FOR XML PATH('')),1,1,'')+','
--print #FileNameList
WHILE (CHARINDEX(#Separator,#ColumnNameList,#Position)!=0)
BEGIN
SET #ColumnName = SUBSTRING(#ColumnNameList,#Position,CHARINDEX(#Separator,#ColumnNameList,#Position)-#Position)
SET #SQL = #SQL + 'set #tot_Count = (select count(*) from FILENAME where ' + #ColumnName + ' like ''%e%'' )'
-- print (#SQL)
EXEC (#SQL)
SET #tot_Count = #tot_Count + #tot_Count
SET #Position = CHARINDEX(#Separator,#ColumnNameList,#Position)+1
END
PRINT (#tot_Count)

Msg 8169, Level 16, State 2, Line 52 Conversion failed when converting from a character string to uniqueidentifier

I am new to this SQL scripting.
Getting error while trying to execute a delete command.
Msg 8169, Level 16, State 2, Line 52 Conversion failed when converting
from a character string to uniqueidentifier.
And the script which I want to execute is:
-- attachments cleardown script
-- SJM 18/09/2009
set nocount on
declare #tableName nvarchar(200)
declare #columnName nvarchar(200)
declare #moduleName nvarchar(200)
declare #objectName nvarchar(200)
declare #attributeName nvarchar(200)
declare #dynamicSQL nvarchar(500)
declare #attachXML varchar(2000)
declare #fileGuid varchar(36)
declare #deletedXML varchar(100)
set #deletedXML = '<DataObjectAttachment><FileName>Deleted</FileName></DataObjectAttachment>'
declare attachCursor cursor fast_forward for
select t5.md_name, t4.md_name, t3.md_title, t2.md_title, t1.md_title
from md_attribute_type t1
join md_class_type t2 on t1.md_class_type_guid = t2.md_guid
join md_module t3 on t2.md_module_guid = t3.md_guid
join md_database_column t4 on t1.md_database_column_guid = t4.md_guid
join md_database_table t5 on t2.md_database_table_guid = t5.md_guid
where t1.md_data_type = 16 and (
t1.md_defined_by = 2 or
t1.md_guid in ('103DBEAB-252F-4C9A-952A-90A7800FFC00', '2515E980-788D-443D-AA89-AA7CC3653867',
'2D29495E-785E-4062-A49C-712A8136EBC7', '6A204C77-1007-48CC-B3BC-077B094540AE',
'9A24BFEF-2CAB-4604-8814-656BA0B9ECC3', 'C368CB18-B4C4-4C4F-839C-F7E6E1E17AA8',
'0814586B-A11E-4129-AF9B-9EC58120C9AF', 'BD4F73C4-07CA-4DC2-86AD-D713FBC6E7BA')
)
and t2.md_behaviour not in (2, 4, 8) -- exclude reference lists
and t2.md_guid not in (select b.md_class_type_guid from md_class_behaviour b where b.md_behaviour_type_guid in
(select bt.md_guid from md_behaviour_type bt where bt.md_name in ('Category','Ranked'))) -- exclude categories and ordered lists
and t3.md_name != 'Lifecycle'
order by t3.md_title, t2.md_title, t1.md_title
open attachCursor
fetch next from attachCursor into #tableName, #columnName, #moduleName, #objectName, #attributeName
while (##FETCH_STATUS = 0)
begin
print 'Deleting from ' + #moduleName + ' -> ' + #objectName + ' (' + #tableName + ') -> ' + #attributeName + ' (' + #columnName + ')...'
set #dynamicSQL = 'declare attachCursor1 cursor fast_forward for'
set #dynamicSQL = #dynamicSQL + ' select ' + #columnName + ' from ' + #tableName
set #dynamicSQL = #dynamicSQL + ' where ' + #columnName + ' != ''' + #deletedXML + ''''
exec sp_executesql #dynamicSQL
open attachCursor1
fetch next from attachCursor1 into #attachXML
while (##FETCH_STATUS = 0)
begin
set #fileGuid = substring(#attachXML, charindex('<Guid>', #attachXML) + 6, 36)
delete from tps_attachment_data where tps_guid = convert(uniqueidentifier, #fileGuid)
fetch next from attachCursor1 into #attachXML
end
close attachCursor1
deallocate attachCursor1
set #dynamicSQL = 'update ' + #tableName + ' set ' + #columnName + ' = ''' + #deletedXML + ''''
set #dynamicSQL = #dynamicSQL + ' where ' + #columnName + ' != ''' + #deletedXML + ''''
exec sp_executesql #dynamicSQL
print '- Deleted ' + convert(varchar(10),##Rowcount) + ' records.'
fetch next from attachCursor into #tableName, #columnName, #moduleName, #objectName, #attributeName
end
close attachCursor
deallocate attachCursor
set nocount off
print char(13) + 'Attachment cleardown complete.'
print char(13) + 'Next steps to reclaim data file space:'
print '1. Take a backup!'
print '2. Shrink the database using the command: DBCC SHRINKDATABASE(''' + convert(varchar(100),SERVERPROPERTY('ServerName')) + ''')'
print '3. Put the database into SINGLE_USER mode (Properties -> Options -> Restrict Access)'
print '4. Detach the database'
print '5. Rename the database .LDF log file'
print '6. Re-attach the database using single file method'
When I parse above script then it is completing successfully but on execution it throws the error.
There are two new functions that have been available since SQL Server 2012: TRY_CAST and TRY_CONVERT, which complement the existing CAST and CONVERT functions.
If you cast an empty string to a unique identifier:
SELECT CAST('' as uniqueidentifier) uid
This would result in the error you are seeing. The same would be true or other strings that aren't GUIDs. Use TRY_CAST instead and the function returns a NULL instead of throwing an error. You can then skip records that don't have a valid GUID.
begin
set #fileGuid = substring(#attachXML, charindex('<Guid>', #attachXML) + 6, 36)
DECLARE #fileGuidOrNull uniqueidentifier = try_convert(uniqueidentifier, #fileGuid)
IF #fileGuidOrNull IS NOT NULL
delete from tps_attachment_data where tps_guid = #fileGuidOrNull
fetch next from attachCursor1 into #attachXML
end

SQL Server Stored Procedure with cursor

I have following stored procedure which basically uses cursor to get people who have appointments today and sends them email. For some reason this query won't complete. When I try to execute it, it just spins and I had to stop it. I let it run for as long as 7 minutes once and still I didn't get either an error or timeout. Not sure what it is doing. Any help will be appreciated. Here is my query:
ALTER PROCEDURE [dbo].[spReminderEmail]
AS
SET NOCOUNT ON;
DECLARE #MyCursor CURSOR
DECLARE #emailBody nvarchar(max)
DECLARE #subject nvarchar(max)
DECLARE #recipients nvarchar(max)
DECLARE #appointmentDate datetime
DECLARE #doctorFirstName nvarchar(max)
DECLARE #doctorLastName nvarchar(max)
DECLARE #groupName nvarchar(max)
DECLARE #location nvarchar(max)
DECLARE #count int
SET #count = 0
SET #MyCursor = CURSOR FAST_FORWARD For
Select StartTime, PersonEmail, DoctorFirstName, DoctorLastName, GroupName, Location from vwAppointment where StartTime BETWEEN CAST(GETDATE() AS DATE) AND DATEADD(DAY, 1, CAST(GETDATE() AS DATE)) AND IsActive = 1
Open #MyCursor
FETCH NEXT FROM #MyCursor INTO #appointmentDate, #recipients, #doctorFirstName, #doctorLastName, #groupName, #location
WHILE ##FETCH_STATUS = 0
BEGIN
SET #emailBody = 'Hello from ' + #groupName + '. This is an email reminder of your appointment with ' + #doctorFirstName + ' ' + #doctorLastName + ' on ' + convert(varchar, #appointmentDate, 1) + ' at ' + #location + '.' + CHAR(13) + CHAR(10) + 'To help facilitate the meeting, please remember to bring with you any relevant documents (ID, insurance, etc.) and be prepared with questions for the office.' + CHAR(13) + CHAR(10) + 'If you are unable to make the appointment, please call ' + #groupName + ' or return to KSUAdvising and use the cancellation function. Cancellations are requested at least a day in advance.' + CHAR(13) + CHAR(10) + 'Late Policy:' + CHAR(13) + CHAR(10) + 'some text here...';
SET #subject = 'REMINDER: Your Appointment with the ' + #groupName;
SET #count = #count + 1
END
CLOSE #MyCursor
DEALLOCATE #MyCursor
PRINT #count
if (#count > 0)
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name='my_profile',
#recipients=#recipients,
#body=#emailBody,
#subject=#subject
END
Inside the WHILE loop (before END line) you must add this:
FETCH NEXT FROM #MyCursor INTO #appointmentDate, #recipients,
#doctorFirstName, #doctorLastName, #groupName, #location
to scroll for every loop a record of your query
You're never fetching your next row. So your loop will go on forever doing nothing. You need to add FETCH NEXT FROM #MyCursor INTO #appointmentDate, #recipients, #doctorFirstName, #doctorLastName, #groupName, #location right before your END statment . See the example at the bottom of This
You also may try to debug your sp to find exact place of issues in you SP in the future.

Resources