MSSQL stored procedure call from ADO - not running properly - sql-server

I have sp in MSSQL server - code below. When I run it from job, or SSMS it runs OK. But I need to run it from VB6 app with ADODB.
My VB6 code:
Dim cmd As New ADODB.Command
cmd.ActiveConnection = CNN
cmd.CommandTimeout = 180
cmd.CommandText = "dbbackup"
cmd.CommandType = ADODB.CommandTypeEnum.adCmdStoredProc
cmd.Execute(, , ADODB.ConnectOptionEnum.adAsyncConnect)
Problem is: When database backup is almost done - about 90+%, cmd.State changes from Executing to Closed and VB6 code continue in executing (to this moment it waits for sp to complete). But there is a lot of code after backup which never run this way(old backup delete,...). I realized that “Last database backup” property on MSSQL database was not set and in table msdb.dbo.backupset there are no rows for my backup. But there si good restorable backup on HDD.
When i stops program for 5 minutes in debug, sp runs properly to end and everything is OK. This backup code is last code in app run and after it ends program closes all connections and exits. I added wait to VB6 code and it helps on some servers, but many other servers still has same problem.
I think main question is why MSSQL server returns control flow to VB6 code and sp is not completed yet.
Thanks
sp code:
PROCEDURE [dbo].[dbBackup]
AS
BEGIN
SET NOCOUNT ON;
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE
If OBJECT_ID('tempdb..#DBName','u') IS NULL
Create Table #DBName
(
ID int identity (1,1) ,
Name varchar(128) not null ,
RetentionPeriod int null,
BackupPath varchar(255) default(''),
DBSize float default(0)
)
If OBJECT_ID('tempdb..#ExistingBackups', 'u') IS NULL
Create Table #ExistingBackups
(
Name varchar(128) ,
ID int identity (1,1)
)
Declare #Path varchar(255)
Declare #sql varchar(1000)
Declare #Name varchar(128)
Declare #RetentionPeriod int
Declare #LastBackupToKeep varchar(8)
Declare #ID int
Declare #MaxID int
Declare #eName varchar(255)
Declare #eMaxID int
Declare #eID int
Declare #eTimeStamp varchar(20)
Declare #errMsg nvarchar(2048)
Declare #errCount int; set #errCount = 0;
Declare #freeSpace bigint
Declare #pageSize float
Declare #dbSize bigint
Declare #procDate datetime
Declare #Sklad char(3)
Declare #backupName as varchar(255)
Select #pageSize = v.low / 1024 From master..spt_values v (noLock) Where v.number = 1 And v.[type] = 'E'
Select Top 1 #sklad = sklad_id From dbo.pohyb (noLock) Where Convert(int, sklad_id) > 500
Set #procDate = GETDATE()
Truncate Table #DBName
Insert Into #DBName (Name, RetentionPeriod, BackupPath)
Select DBName, BackupsToStore, BackupPath
From dbo.databaseBackup (noLock)
Where runBackup = 1
Select #MaxID = max(ID), #ID = 0 From #DBName
While #ID < #MaxID
Begin
Select #ID = min(ID) From #DBName Where ID > #ID
Select #Name = Name, #RetentionPeriod = RetentionPeriod, #Path = BackupPath
From #DBName
Where ID = #ID
If SUBSTRING(#Path, Len(#Path), 1) <> '\' Set #Path = #Path + '\'
Set #sql = 'Update #DBName Set DBSize= (Select Round(Sum(size) *' + CONVERT(varchar, #pageSize) + '/1024, 0) From ' + #Name + '.dbo.sysfiles (noLock)) Where Name = ''' + #Name + ''''
Exec (#sql)
Select #dbSize = DBSize From #DBName
--Exec #freeSpace = dbo.getDiskFreeSpace #drive = #Path
--If #freeSpace > #dbSize
--Begin
Set #eTimeStamp = REPLACE(REPLACE(CONVERT(varchar, #procDate, 113), ' ', '_'), ':', '-')
Set #sql = #Path + #Name + '_' + #eTimeStamp + '.bak'
Set #errMsg = 'OK'
Begin Try
SET #backupName = 'Objednavky backup by job ' + CONVERT(varchar, GETDATE(), 104) + ' ' + CONVERT(varchar, GETDATE(), 108);
Backup Database #Name To Disk = #sql
WITH NAME = #backupName;
-------mazanie backupu begin
Truncate Table #ExistingBackups
Set #sql = 'dir /B /OD ' + #Path + #Name + '_*.bak'
Insert #ExistingBackups Exec master..xp_cmdshell #sql
If Exists (Select 1 From #ExistingBackups Where PATINDEX('%File Not Found%', Name) > 0)
Truncate Table #ExistingBackups
Delete From #ExistingBackups Where Name IS NULL
Select #eID = 0
Select #eMaxID = Max(ID) - #RetentionPeriod From #ExistingBackups
While #eID < #eMaxID
Begin
Select #eID = Min(ID) From #ExistingBackups Where ID > #eID
Select #eName = Name From #ExistingBackups Where ID = #eID
Set #sql = 'del ' + #Path + #eName
Exec master..xp_cmdshell #sql
End
Truncate Table #ExistingBackups
-------mazanie backupu end
End Try
Begin Catch
Set #errMsg = #errMsg + '||' + CONVERT(varchar,ERROR_MESSAGE())
Set #errCount = #errCount + 1;
End Catch
--End
--Else
--Set #errMsg = 'Pln? disk (Vo?n? miesto: ' + CONVERT(varchar, #freeSpace) + ' MB, potrebn? aspo?: ' + CONVERT(varchar, #dbSize) + ' MB)'
Insert Into [dbo].[databaseBackup_log] ([Sklad_id], [DBName], [BackupDate], [Status]) Values (#Sklad, #Name, #procDate, Ltrim(Rtrim(CONVERT(varchar,#errMsg))))
End
Drop Table #DBName
Drop Table #ExistingBackups
IF #errCount > 0 BEGIN
RAISERROR (#errMsg, 16, 2) WITH SETERROR
END
RETURN 0;
END

Related

How to create a temptable for this query

I would like to create a temptable that will hold the result of this query:
set #sql = 'use ' + #dbname + '; select db_name() , name from sys.tables where is_ms_shipped = 0 and type_desc = ''USER_TABLE'' '
exec (#sql)
Here is the full query I have:
declare #minx int = 0
declare #maxx int = (select max(id) from #DBS)
declare #sql nvarchar(1000)
declare #dbname varchar (130)
declare #count int
Select *
from #DBS
while (#count is not null
and #count <= #maxx)
begin
select #dbname = dbname
from #DBS where id = #count
print 'id = ' + convert (varchar, #count) + ' dbname = ' + #dbname
set #sql = 'use ' + #dbname + '; select db_name() , name from sys.tables where is_ms_shipped = 0 and type_desc = ''USER_TABLE'' '
exec (#sql)
set #count = #count + 1
break
end;
I have tried so many things and always getting error messages.
Anybody could give me an insight?
Not sure how you populate #DBS but:
CREATE TABLE #tables(db sysname, table_name sysname);
DECLARE #dbs cursor,
#context nvarchar(1000),
#sql nvarchar(max);
SET #sql = N'INSERT #tables(db, table_name)
SELECT DB_NAME(), name
FROM sys.tables
WHERE type = ''U'' AND is_ms_shipped = 0;';
SET #dbs = CURSOR FOR SELECT QUOTENAME(dbname)
+ N'.sys.sp_executesql' FROM #DBS;
OPEN #dbs;
FETCH NEXT FROM #dbs INTO #context;
WHILE ##FETCH_STATUS = 0
BEGIN
EXECUTE #context #sql;
FETCH NEXT FROM #dbs INTO #context;
END
SELECT db, table_name FROM #tables;
Also see this related question.

Limiting SQL Server Transaction log

I currently have a SQL script that takes all the tables with a certain name and deletes records from it.
There are millions of records in these tables so the transaction log keeps growing even with recovery set to SIMPLE. I am putting the delete in side a transaction and it's only deleting around 50000 records at a time. Can anyone suggest some way to not let the transaction log grow uncontrollably. Below is the rough example of the SQL I am running.
DECLARE delete_cur CURSOR FOR
SELECT DISTINCT Name FROM Sys.tables WHERE name like 'ToDelete_%'
OPEN delete_cur
FETCH NEXT FROM delete_cur
INTO #tableName
WHILE ##FETCH_STATUS = 0 AND #timeToStop = 0
BEGIN
SELECT #RowCount = NULL, #MaxID = NULL, #MinID = NULL, #ID = NULL, #TableRowCount = 0
IF NOT Exists (Select 1 from ArchiveInfoAuditTables WHERE Name = #tableName)
BEGIN
SET #params = N'#MaxID_Out DECIMAL(18,0) OUT, #MinID_Out DECIMAL(18,0) OUT, #RowCount_Out DECIMAL(18,0) OUT';
SET #SQL = 'SELECT #RowCount_Out = COUNT(ID), #MaxID_Out = MAX(ID), #MinID_Out = MIN(ID) FROM ' + #tableName
print #SQL
EXEC sp_executesql #SQL, #params, #RowCount_Out = #RowCount OUT, #MaxID_Out = #MaxID OUT, #MinID_Out = #MinID OUT;
PRINT #tableName + ' Row Count: ' + CONVERT(VARCHAR, #RowCount) + ' Max ID: ' + CONVERT(VARCHAR, #MaxID) + ' Min ID: ' + CONVERT(VARCHAR, #MinID)
SET #params = N'#ID_Out DECIMAL(18,0) OUT, #Date DATETIME';
SET #SQL = 'SELECT TOP 1 #ID_Out = ID FROM ' + #tableName + ' WHERE AuditTimeStamp < #Date ORDER BY ID DESC'
print #SQL
EXEC sp_executesql #SQL, #params, #ID_Out = #ID OUT, #Date = #JanOfCurrentYear
INSERT INTO DeleteInfo (Name, StartDeletingFromID, MaxID, MinID, NumberOfRows)
VALUES (#tableName, #ID, #MaxID, #MinID, #RowCount)
END
ELSE
BEGIN
SELECT TOP 1 #ID = StartDeletingFromID FROM DeleteInfo WHERE Name = #tableName
END
IF (#ID IS NULL)
BEGIN
PRINT 'No Record needs to be deleted for Table: ' + #tableName
GOTO Fetch_Next
END
WHILE 1 = 1
BEGIN
BEGIN TRANSACTION
SET #params = N'#RowCount_Out DECIMAL(18,0) OUT, #NumOfRowsToDelete_Out BIGINT, #ID_Out DECIMAL(18,0)'
SET #SQL = 'DELETE TOP (#NumOfRowsToDelete_Out) FROM ' + #tableName + ' WHERE ID <= #ID_Out ;'
+ 'SELECT #RowCount_Out = ##RowCount'
PRINT #SQL
EXEC sp_executesql #SQL, #params, #RowCount_Out = #TempRowCount OUT, #NumOfRowsToDelete_Out = #NumOfRowsToDelete, #ID_Out = #ID
SET #TableRowCount += #TempRowCount
SET #TotalRowCount += #TableRowCount
COMMIT TRANSACTION
CHECKPOINT;
SET #MSG = 'Deleted ' + CAST(#TableRowCount AS VARCHAR) + '. ' + CONVERT(varchar, #TimeElapsed) + ' elapsed.'
RAISERROR (#MSG, 0, 1) WITH NOWAIT
IF #TempRowCount < #NumOfRowsToDelete BREAK;
END
Fetch_Next:
PRINT '/******************************************************************/'
FETCH NEXT FROM delete_cur
INTO #tableName
END
END_HERE:
CLOSE delete_cur;
DEALLOCATE delete_cur;
You can limit the size of the log (here we limit the size to 512 MB):
ALTER DATABASE [DatabaseName] MODIFY FILE ( NAME = N'DATABASENAME_Log', SIZE = 512000KB , FILEGROWTH = 0)
Create a maintenance job for backups the DB and shrinks the log
you can use this simple command to shrink the log file
DBCC SHRINKFILE (DataFile1, 512)

Challenge with dynamic SQL

I have a procedure that generates dynamic SQL that creates an insert into statement while querying an excel spreadsheet.
The resulting print from the messages screen can be pasted into an ssms window and executes. When I try to execute the SQL from within the stored procedure I get a syntax error as follows:
'SELECT * into TestClient FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 'Excel 12.0;HDR=YES;Database=G:\CustomerETL\Employee\PendingETL\ETLEmployeexls.xls;', [Sheet1$])'
Msg 102, Level 15, State 1, Line 15
Incorrect syntax near 'SELECT * into TestClient FROM OPENROWSET('.
Below is the entire stored procedure. I know the problem is in the ticks (within the SET blocks that create the dynamic SQL I just can't figure out where the missing ticks are.
Here is the proc:
USE [ETL]
GO
/****** Object: StoredProcedure [dbo].[ImportExcelSheetForCustomerEmployeeUpdate2] Script Date: 12/19/2017 4:03:05 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ImportExcelSheetForCustomerEmployeeUpdate2](#BatchID int)
as
--EXEC ImportExcelSheetForCustomerEmployeeUpdate 2
/* -- TRUNCATE TABLE FilesToImport
UPDATE FilesToImport
SET StatusID = 1
*/
-- Jeffery Williams
-- 12/18/2017
DECLARE #FileID int
,#ETLFilename varchar(250)
,#ClientName varchar(100)
,#FileType varchar(5)
,#ColumnCount int
,#RowsToETL int
,#StatusID int
,#Processed bit = 0
,#Count int
,#SQL nvarchar(4000)
,#Sheetname varchar(50) = '[Sheet1$]'
,#CMDSQL as varchar(4000)
,#SQLCmd NVARCHAR(MAX)
SELECT *
FROM FilesToImport
BEGIN
SELECT #Count = count(*)
FROM FilesToImport
WHERE BatchID = #BatchID
AND StatusID = 1
END
PRINT 'Count of records to process: ' + cast(#Count as varchar)
WHILE #Count > 0
BEGIN
BEGIN
SELECT TOP 1 #FileID = FileID, #ETLFilename = ETLFilename, #ClientName = ClientName
,#FileType = FileType, #ColumnCount = ColumnCount, #RowsToETL = RowsToETL
FROM FilesToImport
WHERE StatusID = 1
AND BatchID = #BatchID
END
-- Rename the file
set #CMDSQL = 'rename G:\CustomerETL\Employee\PendingETL\' + #ETLFilename + ' ETLEmployeexls.xls'
exec master..xp_cmdshell #CMDSQL
--PRINT cast(#cmdsql as varchar(4000))
-- Ciode below generates our select. Need to add an INTO clause and create a staging table for each import. Prior to this step we need to rename the file.
SET #SQL = ''''
SET #SQL = #SQL + 'SELECT * into ' + coalesce(#ClientName, 'TestClient') + ' FROM OPENROWSET('
SET #SQL = #SQL + ''''
SET #SQL = #SQL + '''' + 'Microsoft.ACE.OLEDB.12.0' + '''' --+ ', '
-- Excel 12.0;HDR=NO;Database=g:\UnZip\ImportSampleXLSX.xlsx;' + ''
SET #SQL = #SQL + '''' + ', '
SET #SQL = #SQL + '''' + '''Excel 12.0;HDR=YES;Database=G:\CustomerETL\Employee\PendingETL\ETLEmployeexls.xls;''' + '''' + ', ' + #Sheetname + ')'
SET #SQL = #SQL + ''''
PRINT cast(#SQL as varchar(8000))
EXEC sp_executesql #SQL
set #CMDSQL = 'rename G:\CustomerETL\Employee\PendingETL\ETLEmployeexls.xls ' + #ETLFilename
exec master..xp_cmdshell #CMDSQL
UPDATE FilesToImport
SET StatusID = 2
WHERE FileID = #FileID
/* -- TRUNCATE TABLE FilesToImport
UPDATE FilesToImport
SET StatusID = 1
*/
SET #Count = (#Count - 1)
CONTINUE
END
I am posting this as an answer but it should be comment. When I tried adding this as a comment StackOveflow kept thinking that I was trying to add #count as an email target.
In your code:
WHILE #Count > 0
BEGIN
BEGIN
SELECT TOP 1 #FileID = FileID, #ETLFilename = ETLFilename, #ClientName = ClientName
,#FileType = FileType, #ColumnCount = ColumnCount, #RowsToETL = RowsToETL
FROM FilesToImport
WHERE StatusID = 1
AND BatchID = #BatchID
END
you are not updating the value of #count. This will either never loop or loop forever. You probably want to add a statement (right before the end) such as this:
Set #count= ##rowcount;
Ben

programmatically generate script for a table

I learned how to generate script for a table.
Eg for this table:
to generate script like this (I omitted something):
CREATE TABLE [dbo].[singer_and_album](
[singer] [varchar](50) NULL,
[album_title] [varchar](100) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[test_double_quote] ([singer], [album_title]) VALUES (N'Adale', N'19')
GO
INSERT [dbo].[test_double_quote] ([singer], [album_title]) VALUES (N'Michael Jaskson', N'Thriller"')
GO
I tried programmatically generating the script using this shell code. And got error:
PS
SQLSERVER:\SQL\DESKTOP-KHTRJOJ\MSSQL\Databases\yzhang\Tables\dbo.test_double_quote>
C:\Users\yzhang\Documents\script_out_table.ps1 "DESKTOP-KHTRJOJ\MSSQL"
"yzhang" "dbo" "test_double_quote",
"C:\Users\yzhang\Documents\script_out.sql"
Multiple ambiguous overloads found for "EnumScript" and the argument
count: "1". At C:\Users\yzhang\Documents\script_out_table.ps1:41
char:16
+ foreach ($s in $scripter.EnumScript($tbl.Urn)) { write-host $s }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Anybody can help? I don't know much about shell.
btw is shell the only way to generate scripts? Can we do it with some sql code? Thank you--
FYI, see this for how to manually generate script. In my case (sql server 2016 management studio) it's like
right click the database name (not table name) -> Tasks -> Generate scripts
choose a table or all tables
click advanced and select schema and data
this is a sql script to genrate table script
declare #vsSQL varchar(8000)
declare #vsTableName varchar(50)
select #vsTableName = '_PRODUCT'--- Your Table Name here
select #vsSQL = 'CREATE TABLE ' + #vsTableName + char(10) + '(' + char(10)
select #vsSQL = #vsSQL + ' ' + sc.Name + ' ' +
st.Name +
case when st.Name in ('varchar','varchar','char','nchar') then '(' + cast(sc.Length as varchar) + ') ' else ' ' end +
case when sc.IsNullable = 1 then 'NULL' else 'NOT NULL' end + ',' + char(10)
from sysobjects so
join syscolumns sc on sc.id = so.id
join systypes st on st.xusertype = sc.xusertype
where so.name = #vsTableName
order by
sc.ColID
select substring(#vsSQL,1,len(#vsSQL) - 2) + char(10) + ')'
Edit: c# code
public string GetScript(string strConnectionString
, string strObject
, int ObjType)
{
string strScript = null;
int intCounter = 0;
if (ObjType != 0)
{
ObjSqlConnection = new SqlConnection(strConnectionString.Trim());
try
{
ObjDataSet = new DataSet();
ObjSqlCommand = new SqlCommand("exec sp_helptext
[" + strObject + "]", ObjSqlConnection);
ObjSqlDataAdapter = new SqlDataAdapter();
ObjSqlDataAdapter.SelectCommand = ObjSqlCommand;
ObjSqlDataAdapter.Fill(ObjDataSet);
foreach (DataRow ObjDataRow in ObjDataSet.Tables[0].Rows)
{
strScript += Convert.ToString(ObjDataSet.Tables[0].Rows[intCounter][0]);
intCounter++;
}
}
catch (Exception ex)
{
strScript = ex.Message.ToString();
}
finally
{
ObjSqlDataAdapter = null;
ObjSqlCommand = null;
ObjSqlConnection = null;
}
}
return strScript;
}
To create Insert script use this store procedure
IF EXISTS (SELECT * FROM dbo.sysobjects
WHERE id = OBJECT_ID(N'[dbo].[InsertGenerator]') AND OBJECTPROPERTY(id,N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[InsertGenerator]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO
CREATE PROC [dbo].[InsertGenerator]
(
#tableName varchar(100),
#KeyColumn1 varchar(100)='',
#KeyColumn2 varchar(100)=''
)
AS
-- Generating INSERT statements in SQL Server
-- to validate if record exists - supports 2 field Unique index
--Declare a cursor to retrieve column specific information for the specified table
DECLARE cursCol CURSOR FAST_FORWARD FOR
SELECT column_name,data_type FROM information_schema.columns WHERE table_name = #tableName
OPEN cursCol
DECLARE #string nvarchar(max) --for storing the first half of INSERT statement
DECLARE #stringData nvarchar(max) --for storing the data (VALUES) related statement
DECLARE #dataType nvarchar(1000) --data types returned for respective columns
DECLARE #FieldVal nvarchar(1000) -- save value for the current field
DECLARE #KeyVal nvarchar(1000) -- save value for the current field
DECLARE #KeyTest0 nvarchar(1000) -- used to test if key exists
DECLARE #KeyTest1 nvarchar(1000) -- used to test if key exists
DECLARE #KeyTest2 nvarchar(1000) -- used to test if key exists
SET #KeyTest0=''
IF #KeyColumn1<>''
SET #KeyTest0='IF not exists (Select * from '+#tableName
SET #KeyTest1=''
SET #KeyTest2=''
SET #string='INSERT '+#tableName+'('
SET #stringData=''
SET #FieldVal=''
SET #KeyVal=''
DECLARE #colName nvarchar(50)
FETCH NEXT FROM cursCol INTO #colName,#dataType
IF ##fetch_status<>0
begin
print 'Table '+#tableName+' not found, processing skipped.'
close curscol
deallocate curscol
return
END
WHILE ##FETCH_STATUS=0
BEGIN
IF #dataType in ('varchar','char','nchar','nvarchar')
BEGIN
SET #FieldVal=''''+'''+isnull('''''+'''''+'+#colName+'+'''''+''''',''NULL'')+'',''+'
SET #KeyVal='''+isnull('''''+'''''+'+#colName+'+'''''+''''',''NULL'')+'',''+'
SET #stringData=#stringData+#FieldVal
END
ELSE
if #dataType in ('text','ntext','xml') --if the datatype is text or something else
BEGIN
SET #FieldVal='''''''''+isnull(cast('+#colName+' as varchar(max)),'''')+'''''',''+'
SET #stringData=#stringData+#FieldVal
END
ELSE
IF #dataType = 'money' --because money doesn't get converted from varchar implicitly
BEGIN
SET #FieldVal='''convert(money,''''''+isnull(cast('+#colName+' as varchar(200)),''0.0000'')+''''''),''+'
SET #stringData=#stringData+#FieldVal
END
ELSE
IF #dataType='datetime'
BEGIN
SET #FieldVal='''convert(datetime,'+'''+isnull('''''+'''''+convert(varchar(200),'+#colName+',121)+'''''+''''',''NULL'')+'',121),''+'
SET #stringData=#stringData+#FieldVal
END
ELSE
IF #dataType='image'
BEGIN
SET #FieldVal='''''''''+isnull(cast(convert(varbinary,'+#colName+') as varchar(6)),''0'')+'''''',''+'
SET #stringData=#stringData+#FieldVal
END
ELSE --presuming the data type is int,bit,numeric,decimal
BEGIN
SET #FieldVal=''''+'''+isnull('''''+'''''+convert(varchar(200),'+#colName+')+'''''+''''',''NULL'')+'',''+'
SET #KeyVal='''+isnull('''''+'''''+convert(varchar(200),'+#colName+')+'''''+''''',''NULL'')+'',''+'
SET #stringData=#stringData+#FieldVal
END
--Build key test
IF #KeyColumn1=#colName
begin
SET #KeyTest1 = ' WHERE [' + #KeyColumn1 + ']='
SET #KeyTest1 = #KeyTest1+#KeyVal+']'
end
IF #KeyColumn2=#colName
begin
SET #KeyTest2 = ' AND [' + #KeyColumn2 + ']='
SET #KeyTest2 = #KeyTest2+#KeyVal+']'
end
SET #string=#string+'['+#colName+'],'
FETCH NEXT FROM cursCol INTO #colName,#dataType
END
DECLARE #Query nvarchar(max)
-- Build the test string to check if record exists
if #KeyTest0<>''
begin
if #Keycolumn1<>''
SET #KeyTest0 = #KeyTest0 + substring(#KeyTest1,0,len(#KeyTest1)-4)
if #Keycolumn2<>''
begin
SET #KeyTest0 = #KeyTest0 + ''''
SET #KeyTest0 = #KeyTest0 + substring(#KeyTest2,0,len(#KeyTest2)-4)
end
SET #KeyTest0 = #KeyTest0 + ''')'
SET #query ='SELECT '''+substring(#KeyTest0,0,len(#KeyTest0)) + ') '
end
else
SET #query ='SELECT '''+substring(#KeyTest0,0,len(#KeyTest0))
SET #query = #query + substring(#string,0,len(#string)) + ') '
SET #query = #query + 'VALUES(''+ ' + substring(#stringData,0,len(#stringData)-2)+'''+'')'' FROM '+#tableName
exec sp_executesql #query
CLOSE cursCol
DEALLOCATE cursCol
GO
and use of InsertGenerator like below
DECLARE #return_value int
EXEC #return_value = [dbo].[InsertGenerator]
#tableName = N'_PRODUCT'
SELECT 'Return Value' = #return_value

How many tables are on an instance of SQL Server

How do I find out how many tables are on my instance of SQL Server? I can get it for a single schema using select count(*) from sysobjects where type = 'U'
(from how to count number of tables/views/index in my database)
You're using the word "schema", but I think you're really asking to count tables across all "databases".
declare #t table (
DBName sysname,
NumTables int
)
insert into #t
exec sp_MSforeachdb N'select ''?'', count(*)
from [?].dbo.sysobjects
where type = ''U'''
select DBName, NumTables
from #t
where DBName not in ('distribution','master','model','msdb','tempdb')
order by DBName
select SUM(NumTables) as TotalTables
from #t
where DBName not in ('distribution','master','model','msdb','tempdb')
An option without using the hidden, undocumented sp_MSforeachdb
declare #sql nvarchar(max)
select #sql = coalesce(#sql + ' + ', '') + REPLACE('
(select count(*)
from ::DB::.sys.objects
where is_ms_shipped = 0
and type_desc = ''USER_TABLE'')', '::DB::', QUOTENAME(name))
from master.sys.databases
where owner_sid != 0x01
select #sql = 'select ' + #sql
exec (#sql) -- returns a single count of all [user] tables in the instance
>
A note on performance. It is insignificant in the greater scheme of things, but with all things interesting, someone is bound to time it. Here is a comparison of the ms_foreachdb approach passing through a temp table (it internally uses a cursor) against the string-concat method.
-- all the variables that we will use
declare #i int -- loop variable
declare #sql nvarchar(max) -- statement var used for 1st approach
declare #t table (DBName sysname, NumTables int) -- table used for 2nd approach
-- init plan cache and buffers
dbcc freeproccache dbcc dropcleanbuffers
print convert(varchar(30), getdate(), 121)
set #i = 0 while #i < 5 begin
set #sql = null
select #sql = coalesce(#sql, '') + REPLACE('
select #c = #c + count(*)
from ::DB::.sys.objects
where is_ms_shipped = 0
and type_desc = ''USER_TABLE''', '::DB::', QUOTENAME(name))
from master.sys.databases
where owner_sid != 0x01
select #sql = 'set nocount on declare #c int set #c = 0 ' + #sql + ' select #c'
exec (#sql)
-- clear plan cache and buffers after each run
dbcc freeproccache dbcc dropcleanbuffers set #i = #i + 1
end
print convert(varchar(30), getdate(), 121)
set #i = 0 while #i < 5 begin
insert into #t
exec sp_MSforeachdb N'select ''?'', count(*)
from [?].dbo.sysobjects
where type = ''U'''
select SUM(NumTables) as TotalTables
from #t
where DBName not in ('distribution','master','model','msdb','tempdb')
-- unfortunately this is required
delete from #t
-- clear plan cache and buffers after each run
dbcc freeproccache dbcc dropcleanbuffers set #i = #i + 1
end
print convert(varchar(30), getdate(), 121)
The result obtained for only 5 invocations (loop iterations) of each. YMMV
start : 2011-01-21 14:21:45.180
end of string-concat : 2011-01-21 14:21:57.497 (12.317)
end of sp_msforeachdb : 2011-01-21 14:22:13.937 (16.440)
It has to be noted that the temp table has to be emptied between each iteration of the 2nd approach, so that could contribute to the total time. It should have been insignificant though
Here is an answer that does not use undocumented functions and works in SQL Server 2005, 2008 and 2008R2. This answer can be used with minor modifications to run any statement across databases.
DECLARE #sql varchar(200), #dbname sysname, #dbid smallint;
CREATE table #alltables
(dbname sysname,
[number of tables] int);
SELECT top 1 #dbname = name, #dbid = database_id
FROM sys.databases
where database_id > 4;
WHILE (#dbname is not null)
begin
-- the statement below could contain any valid select statement
set #sql = 'use ' + #dbname + '; insert into #alltables select ''' + #dbname + ''', count(*) from sys.tables';
EXEC (#sql)
set #dbname = null;
SELECT top 1 #dbname = name, #dbid = database_id
FROM sys.databases
where database_id > #dbid;
end;
select * FROM #alltables;
SELECT sum([number of tables]) "Total Number of Tables in all user databases" from #alltables;
drop table #alltables;
Select Count(*)
From INFORMATION_SCHEMA.TABLES
Where TABLE_TYPE = 'BASE TABLE'
If what you are seeking is a way to determine how many tables exist across all databases on a given SQL Server instance, then you need to cycle through each database. One way would be:
Declare #Databases Cursor
Declare #DbName as nvarchar(64)
Declare #SQL nvarchar(max)
Declare #BaseSQL nvarchar(max)
Declare #Count int
Declare #TotalCount int
Set #Databases = Cursor Fast_Forward For
select [name]
from master..sysdatabases
where [name] Not In('master','model','msdb','tempdb')
Open #Databases
Fetch Next From #Databases Into #DbName
Set #BaseSQL = 'Select #Count = Count(*)
From DatabaseName.INFORMATION_SCHEMA.TABLES
Where TABLE_TYPE = ''BASE TABLE'''
Set #TotalCount = 0
While ##Fetch_Status = 0
Begin
Set #Count = 0
Set #SQL = Replace(#BaseSQL, 'DatabaseName', QuoteName(#DbName))
exec sp_executesql #SQL, N'#Count int OUTPUT', #Count OUTPUT
Set #TotalCount = #TotalCount + #Count
Fetch Next From #Databases Into #DbName
End
Close #Databases
Deallocate #Databases
Select #TotalCount
This solution has the advantage of not using any undocumented features such as sp_MSforeachdb however it is obviously more verbose.

Resources