T-SQL copy Logins, Users, roles, permissions etc - sql

We have implemented log shipping as a database disaster recovery solution and want to know if there is a way I can use T-SQL to script all the logins, users, roles permissions etc to the master database on the secondary server so that the T-SQL can be sheduled to run as an SQL Job?
My aim is that in the event of a D/R situation we can simply restore the transaction logs for each database to the secondary server and not have to worry about orphaned users etc.
Thanks for you help!

There is a login copy script here designed to copy logins to another server for disaster recovery purposes:
http://www.sqlsoldier.com/wp/sqlserver/transferring-logins-to-a-database-mirror
Use master;
Go
If Exists (Select 1 From INFORMATION_SCHEMA.ROUTINES
Where ROUTINE_NAME = 'dba_CopyLogins'
And ROUTINE_SCHEMA = 'dbo')
Drop Procedure dbo.dba_CopyLogins
Go
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
Create Procedure dbo.dba_CopyLogins
#PartnerServer sysname,
#Debug bit = 0
As
Declare #MaxID int,
#CurrID int,
#SQL nvarchar(max),
#LoginName sysname,
#IsDisabled int,
#Type char(1),
#SID varbinary(85),
#SIDString nvarchar(100),
#PasswordHash varbinary(256),
#PasswordHashString nvarchar(300),
#RoleName sysname,
#Machine sysname,
#PermState nvarchar(60),
#PermName sysname,
#Class tinyint,
#MajorID int,
#ErrNumber int,
#ErrSeverity int,
#ErrState int,
#ErrProcedure sysname,
#ErrLine int,
#ErrMsg nvarchar(2048)
Declare #Logins Table (LoginID int identity(1, 1) not null primary key,
[Name] sysname not null,
[SID] varbinary(85) not null,
IsDisabled int not null,
[Type] char(1) not null,
PasswordHash varbinary(256) null)
Declare #Roles Table (RoleID int identity(1, 1) not null primary key,
RoleName sysname not null,
LoginName sysname not null)
Declare #Perms Table (PermID int identity(1, 1) not null primary key,
LoginName sysname not null,
PermState nvarchar(60) not null,
PermName sysname not null,
Class tinyint not null,
ClassDesc nvarchar(60) not null,
MajorID int not null,
SubLoginName sysname null,
SubEndPointName sysname null)
Set NoCount On;
If CharIndex('\', #PartnerServer) > 0
Begin
Set #Machine = LEFT(#PartnerServer, CharIndex('\', #PartnerServer) - 1);
End
Else
Begin
Set #Machine = #PartnerServer;
End
-- Get all Windows logins from principal server
Set #SQL = 'Select P.name, P.sid, P.is_disabled, P.type, L.password_hash' + CHAR(10) +
'From ' + QUOTENAME(#PartnerServer) + '.master.sys.server_principals P' + CHAR(10) +
'Left Join ' + QUOTENAME(#PartnerServer) + '.master.sys.sql_logins L On L.principal_id = P.principal_id' + CHAR(10) +
'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
'And P.name <> ''sa''' + CHAR(10) +
'And P.name Not Like ''##%''' + CHAR(10) +
'And CharIndex(''' + #Machine + '\'', P.name) = 0;';
Insert Into #Logins (Name, SID, IsDisabled, Type, PasswordHash)
Exec sp_executesql #SQL;
-- Get all roles from principal server
Set #SQL = 'Select RoleP.name, LoginP.name' + CHAR(10) +
'From ' + QUOTENAME(#PartnerServer) + '.master.sys.server_role_members RM' + CHAR(10) +
'Inner Join ' + QUOTENAME(#PartnerServer) + '.master.sys.server_principals RoleP' +
CHAR(10) + char(9) + 'On RoleP.principal_id = RM.role_principal_id' + CHAR(10) +
'Inner Join ' + QUOTENAME(#PartnerServer) + '.master.sys.server_principals LoginP' +
CHAR(10) + char(9) + 'On LoginP.principal_id = RM.member_principal_id' + CHAR(10) +
'Where LoginP.type In (''U'', ''G'', ''S'')' + CHAR(10) +
'And LoginP.name <> ''sa''' + CHAR(10) +
'And LoginP.name Not Like ''##%''' + CHAR(10) +
'And RoleP.type = ''R''' + CHAR(10) +
'And CharIndex(''' + #Machine + '\'', LoginP.name) = 0;';
Insert Into #Roles (RoleName, LoginName)
Exec sp_executesql #SQL;
-- Get all explicitly granted permissions
Set #SQL = 'Select P.name Collate database_default,' + CHAR(10) +
' SP.state_desc, SP.permission_name, SP.class, SP.class_desc, SP.major_id,' + CHAR(10) +
' SubP.name Collate database_default,' + CHAR(10) +
' SubEP.name Collate database_default' + CHAR(10) +
'From ' + QUOTENAME(#PartnerServer) + '.master.sys.server_principals P' + CHAR(10) +
'Inner Join ' + QUOTENAME(#PartnerServer) + '.master.sys.server_permissions SP' + CHAR(10) +
CHAR(9) + 'On SP.grantee_principal_id = P.principal_id' + CHAR(10) +
'Left Join ' + QUOTENAME(#PartnerServer) + '.master.sys.server_principals SubP' + CHAR(10) +
CHAR(9) + 'On SubP.principal_id = SP.major_id And SP.class = 101' + CHAR(10) +
'Left Join ' + QUOTENAME(#PartnerServer) + '.master.sys.endpoints SubEP' + CHAR(10) +
CHAR(9) + 'On SubEP.endpoint_id = SP.major_id And SP.class = 105' + CHAR(10) +
'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
'And P.name <> ''sa''' + CHAR(10) +
'And P.name Not Like ''##%''' + CHAR(10) +
'And CharIndex(''' + #Machine + '\'', P.name) = 0;'
Insert Into #Perms (LoginName, PermState, PermName, Class, ClassDesc, MajorID, SubLoginName, SubEndPointName)
Exec sp_executesql #SQL;
Select #MaxID = Max(LoginID), #CurrID = 1
From #Logins;
While #CurrID <= #MaxID
Begin
Select #LoginName = Name,
#IsDisabled = IsDisabled,
#Type = [Type],
#SID = [SID],
#PasswordHash = PasswordHash
From #Logins
Where LoginID = #CurrID;
If Not Exists (Select 1 From sys.server_principals
Where name = #LoginName)
Begin
Set #SQL = 'Create Login ' + quotename(#LoginName)
If #Type In ('U', 'G')
Begin
Set #SQL = #SQL + ' From Windows;'
End
Else
Begin
Set #PasswordHashString = '0x' +
Cast('' As XML).value('xs:hexBinary(sql:variable("#PasswordHash"))', 'nvarchar(300)');
Set #SQL = #SQL + ' With Password = ' + #PasswordHashString + ' HASHED, ';
Set #SIDString = '0x' +
Cast('' As XML).value('xs:hexBinary(sql:variable("#SID"))', 'nvarchar(100)');
Set #SQL = #SQL + 'SID = ' + #SIDString + ';';
End
If #Debug = 0
Begin
Begin Try
Exec sp_executesql #SQL;
End Try
Begin Catch
Set #ErrNumber = ERROR_NUMBER();
Set #ErrSeverity = ERROR_SEVERITY();
Set #ErrState = ERROR_STATE();
Set #ErrProcedure = ERROR_PROCEDURE();
Set #ErrLine = ERROR_LINE();
Set #ErrMsg = ERROR_MESSAGE();
RaisError(#ErrMsg, 1, 1);
End Catch
End
Else
Begin
Print #SQL;
End
If #IsDisabled = 1
Begin
Set #SQL = 'Alter Login ' + quotename(#LoginName) + ' Disable;'
If #Debug = 0
Begin
Begin Try
Exec sp_executesql #SQL;
End Try
Begin Catch
Set #ErrNumber = ERROR_NUMBER();
Set #ErrSeverity = ERROR_SEVERITY();
Set #ErrState = ERROR_STATE();
Set #ErrProcedure = ERROR_PROCEDURE();
Set #ErrLine = ERROR_LINE();
Set #ErrMsg = ERROR_MESSAGE();
RaisError(#ErrMsg, 1, 1);
End Catch
End
Else
Begin
Print #SQL;
End
End
End
Set #CurrID = #CurrID + 1;
End
Select #MaxID = Max(RoleID), #CurrID = 1
From #Roles;
While #CurrID <= #MaxID
Begin
Select #LoginName = LoginName,
#RoleName = RoleName
From #Roles
Where RoleID = #CurrID;
If Not Exists (Select 1 From sys.server_role_members RM
Inner Join sys.server_principals RoleP
On RoleP.principal_id = RM.role_principal_id
Inner Join sys.server_principals LoginP
On LoginP.principal_id = RM.member_principal_id
Where LoginP.type In ('U', 'G', 'S')
And RoleP.type = 'R'
And RoleP.name = #RoleName
And LoginP.name = #LoginName)
Begin
If #Debug = 0
Begin
Exec sp_addsrvrolemember #rolename = #RoleName,
#loginame = #LoginName;
End
Else
Begin
Print 'Exec sp_addsrvrolemember #rolename = ''' + #RoleName + ''',';
Print ' #loginame = ''' + #LoginName + ''';';
End
End
Set #CurrID = #CurrID + 1;
End
Select #MaxID = Max(PermID), #CurrID = 1
From #Perms;
While #CurrID <= #MaxID
Begin
Select #PermState = PermState,
#PermName = PermName,
#Class = Class,
#LoginName = LoginName,
#MajorID = MajorID,
#SQL = PermState + space(1) + PermName + SPACE(1) +
Case Class When 101 Then 'On Login::' + QUOTENAME(SubLoginName)
When 105 Then 'On ' + ClassDesc + '::' + QUOTENAME(SubEndPointName)
Else '' End +
' To ' + QUOTENAME(LoginName) + ';'
From #Perms
Where PermID = #CurrID;
If Not Exists (Select 1 From sys.server_principals P
Inner Join sys.server_permissions SP On SP.grantee_principal_id = P.principal_id
Where SP.state_desc = #PermState
And SP.permission_name = #PermName
And SP.class = #Class
And P.name = #LoginName
And SP.major_id = #MajorID)
Begin
If #Debug = 0
Begin
Begin Try
Exec sp_executesql #SQL;
End Try
Begin Catch
Set #ErrNumber = ERROR_NUMBER();
Set #ErrSeverity = ERROR_SEVERITY();
Set #ErrState = ERROR_STATE();
Set #ErrProcedure = ERROR_PROCEDURE();
Set #ErrLine = ERROR_LINE();
Set #ErrMsg = ERROR_MESSAGE();
RaisError(#ErrMsg, 1, 1);
End Catch
End
Else
Begin
Print #SQL;
End
End
Set #CurrID = #CurrID + 1;
End
Set NoCount Off;

Related

Search a string in whole mysql database

I wrote this query for searching a string in whole database . Earlier it was working properly now it doesn't fetch full result at all . I don't get what is wrong with this query . Please help
QUERY
/*
- Search through tables to find specific text
- Written by Luis Chiriff (with help from SQL Server Central)
- luis.chiriff#gmail.com # 24/11/2008 # 11:54
*/
-- Variable Declaration
Declare #StringToFind VARCHAR(200), #Schema sysname, #Table sysname, #FullTable int, #NewMinID
int, #NewMaxID int,
#SQLCommand VARCHAR(8000), #BaseSQLCommand varchar(8000), #Where VARCHAR(8000), #CountCheck
varchar(8000) , #FieldTypes varchar(8000),
#cursor VARCHAR(8000), #columnName sysname, #SCn int, #SCm int
Declare #TableList table (Id int identity(1,1) not null, tablename varchar(250))
Declare #SQLCmds table (id int identity(1,1) not null, sqlcmd varchar(8000))
Declare #DataFoundInTables table (id int identity(1,1) not null, sqlcmd varchar(8000))
-- Settings
SET #StringToFind = 'abcdef'
SET NOCOUNT ON
SET #StringToFind = '%'+#StringToFind+'%'
-- Gathering Info
if ((select count(*) from sysobjects where name = 'tempcount') > 0)
drop table tempcount
create table tempcount (rowsfound int)
insert into tempcount select 0
-- This section here is to accomodate the user defined datatypes, if they have
-- a SQL Collation then they are assumed to have text in them.
SET #FieldTypes = ''
select #FieldTypes = #FieldTypes + '''' + rtrim(ltrim(name))+''',' from systypes where collation
is not null or xtype = 36
select #FieldTypes = left(#FieldTypes,(len(#FieldTypes)-1))
insert into #TableList (tablename)
select name from sysobjects
where xtype = 'U' and name not like 'dtproperties'
order by name
-- Start Processing Table List
select #NewMinID = min(id), #NewMaxID = max(id) from #TableList
while(#NewMinID <= #NewMaxID)
Begin
SELECT #Table = tablename, #Schema='dbo', #Where = '' from #TableList where id = #NewMinID
SET #SQLCommand = 'SELECT * FROM ' + #Table + ' WHERE'
-- removed ' + #Schema + '.
SET #cursor = 'DECLARE col_cursor CURSOR FOR SELECT COLUMN_NAME
FROM [' + DB_NAME() + '].INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = ''' + #Schema + '''
AND TABLE_NAME = ''' + #Table + '''
AND DATA_TYPE IN ('+#FieldTypes+')'
--Original Check, however the above implements user defined data types --AND DATA_TYPE IN
(''char'',''nchar'',''ntext'',''nvarchar'',''text'',''varchar'')'
EXEC (#cursor)
SET #FullTable = 0
DELETE FROM #SQLCmds
OPEN col_cursor
FETCH NEXT FROM col_cursor INTO #columnName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Where = #Where + ' [' + #columnName + '] LIKE ''' + #StringToFind + ''''
SET #Where = #Where + ' OR'
--PRINT #Table + '|'+ cast(len(isnull(#Where,''))+len(isnull(#SQLCommand,'')) as varchar(10))+'|'+#Where
if (len(isnull(#Where,''))+len(isnull(#SQLCommand,'')) > 3600)
Begin
SELECT #Where = substring(#Where,1,len(#Where)-3)
insert into #SQLCmds (sqlcmd) select #Where
SET #Where = ''
End
FETCH NEXT FROM col_cursor INTO #columnName
END
CLOSE col_cursor
DEALLOCATE col_cursor
if (#Where <> '')
Begin
SELECT #Where = substring(#Where,1,len(#Where)-3)
insert into #SQLCmds (sqlcmd)
select #Where --select #Table,count(*) from #SQLCmds
End
SET #BaseSQLCommand = #SQLCommand
select #SCn = min(id), #SCm = max(id) from #SQLCmds
while(#SCn <= #SCm)
Begin
select #Where = sqlcmd from #SQLCmds where ID = #SCn
if (#Where <> '')
Begin
SET #SQLCommand = #BaseSQLCommand + #Where
SELECT #CountCheck = 'update tempcount set rowsfound = (select count(*) '+ substring(#SQLCommand,10,len(#SQLCommand)) + ')'
EXEC (#CountCheck)
if ((select rowsfound from tempcount) > 0)
Begin
PRINT '--- ['+cast(#NewMinID as varchar(15))+'/'+cast(#NewMaxID as varchar(15))+'] '+#Table + ' ----------------------------------[FOUND!]'
--PRINT '--- [FOUND USING:] ' +#SQLCommand
insert into #DataFoundInTables (sqlcmd) select #SQLCommand
EXEC (#SQLCommand)
update tempcount set rowsfound = 0
End
else
Begin
PRINT '--- ['+cast(#NewMinID as varchar(15))+'/'+cast(#NewMaxID as varchar(15))+'] '+#Table
End
End
SET #SCn = #SCn + 1
End
set #NewMinID = #NewMinID + 1
end
if ((select count(*) from sysobjects where name = 'tempcount') > 0)
drop table tempcount
/*
This will now return all the sql commands you need to use
*/
select #NewMinID = min(id), #NewMaxID = max(id) from #DataFoundInTables
if (#NewMaxID > 0)
Begin
PRINT ' '
PRINT ' '
PRINT '-----------------------------------------'
PRINT '----------- TABLES WITH DATA ------------'
PRINT '-----------------------------------------'
PRINT ' '
PRINT 'We found ' + cast(#NewMaxID as varchar(10)) + ' table(s) with the string '+#StringToFind
PRINT ' '
while(#NewMinID <= #NewMaxID)
Begin
select #SQLCommand = sqlcmd from #DataFoundInTables where ID = #NewMinID
PRINT #SQLCommand
SET #NewMinID = #NewMinID + 1
End
PRINT ' '
PRINT '-----------------------------------------'
End
This query was working fine one month ago but now it doesn't fetch the results . Can anyone tell me what is wrong in this query .

Generate script with INSERT SELECT statements from T-SQL

I have two databases in SQL Server 2014 Express, since a few days ago I am unifying both databases in such a way that there is only one left.
When I have this new database I must pass it to Azure (I don't have any experience using this technology), however, I am doing tests on my local server, so far I have created the whole scheme but I must fill out all the tables to perform tests with my application.
There are 313 tables of which many have more than 200,000 records, my question is, what is the best way to populate the database?
Because at this moment I want to test my local machine, I could fill in the tables through Tasks> Generate Script> Advanced Options (Include only data) but this information will change the day when the migration to Azure is done, therefore I must Do the same process.
So, is it possible to create an INSERT SELECT script so that it does not include records one by one and is as dynamic as possible? For example, you would have to generate an INSERT INTO similar to this:
SET IDENTITY_INSERT [SchemaX].[TableA] ON ;
INSERT INTO [SchemaX].[TableA]
(
[id]
,[fieldA]
,[fieldB]
,[fieldC])
SELECT
[id]
,[fieldA]
,[fieldB]
,[fieldC]
FROM [server].[dbname].[SchemaX].[TableA]
SET IDENTITY_INSERT [SchemaX].[TableA] OFF ;
Some tables have IDENTITY enabled so you would have to recognize which tables are like this and use SET IDENTITY_INSERT when inserting. This way you would have to link the production server and insert the information into the local server.
If there are suggestions or recommendations about another way you are welcome
Has been answered before ...
/****** Object: StoredProcedure [dbo].[procUtils_GenerateInsertProc] Script Date: 03/20/2010 13:06:13 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[procUtils_GenerateInsertProc]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[procUtils_GenerateInsertProc]
GO
/****** Object: StoredProcedure [dbo].[procUtils_GenerateInsertProc] Script Date: 03/20/2010 13:06:13 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--exec procUtils_GenerateInsertProc 'Whatever'
--exec sp_HelpText procUtils_GenerateInsertProc
CREATE PROCEDURE [dbo].[procUtils_GenerateInsertProc]
#TableName [varchar](50)
WITH EXECUTE AS CALLER
AS
BEGIN -- proc start
SET NOCOUNT ON;
BEGIN TRY --begin try
--FIRST SEARCH THE TABLE WHICH HAD A "Feature" in its name
--SELECT NAME FROM SYS.TABLES WHERE NAME LIKE '%Feature%'
--SELECT column_name from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='Feature' --SELECT * from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='Task'
--Decalre a variable to remember the position of the current delimiter
DECLARE #ProcName varchar(1000)
set #ProcName = '[dbo].[procGen_' + #TableName + '_Insert]'
DECLARE #CurrentDelimiterPositionVar INT
DECLARE #PkColumnName varchar(200)
--Decalre a variable to remember the number of rows in the table
DECLARE #Count INT
DECLARE #ColumnName varchar(300);
DECLARE #DataType varchar(50)
DECLARE #IsNullable bit
DECLARE #MaxLength INT
DECLARE #IsComputed BIT
set #IsComputed = 0
DECLARE #IsPrimaryKey BIT
set #IsPrimaryKey = 0
DECLARE #CODESTR VARCHAR(max)
--PRINT DROP PROCEDURE
set #CODESTR = ' '
--Declare the Table variable
DECLARE #ColumnNames TABLE
(
Number INT IDENTITY(1,1), --Auto incrementing Identity column
TableName varchar(300) , --the name of the table
ColumnName VARCHAR(300) , --The string value ,
DataType varchar(50) , --the datatype
IsNullable bit , --should we add =null in front
MaxLength INT , --VARCHAR(LENGHTi)
IsComputed bit , --whether or not this table is computed
IsPrimaryKey bit --whether or not this table is computed
)
--Populate the TABLE variable using some logic
-- SELECT * from INFORMATION_SCHEMA.COLUMNS
INSERT INTO #ColumnNames
(
TableName ,
ColumnName ,
DataType ,
IsNullable ,
MaxLength ,
IsComputed ,
IsPrimaryKey )
SELECT
TableName ,
ColumnName ,
DataType ,
IsNullable ,
MaxLength ,
IsComputed ,
IsPrimaryKey
from viewMeta_TableColumns
--debug where TableName = 'Whatever'
where TableName = #TableName
--SELECT column_name , Data_type , IsNullable , MaxLength
--from INFORMATION_SCHEMA.COLUMNS
--where TABLE_NAME=#TableName
--Initialize the looper variable
SET #CurrentDelimiterPositionVar = 1
--Determine the number of rows in the Table
SELECT #Count=max(Number) from #ColumnNames
--A variable to hold the currently selected value from the table
set #CODESTR = #CODESTR + 'IF OBJECT_ID(''' + #ProcName + ''') IS NOT NULL
BEGIN
DROP PROC ' + #ProcName + '
END
GO'
set #CODESTR = #CODESTR + '
/****** Object: StoredProcedure ' + #ProcName + '*/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE ' + #ProcName + '
#CurUserSessionId [int] ,
#CurPageTypeId [int] ,
#MsgOut [varchar](200) OUTPUT ,
#DebugMsgOut [varchar](200) OUTPUT,
#Ret [int] OUTPUT ,' + CHAR(13)
--#COLUMN_NAME [DATA_TYPE] (MAX_LENGTH) =NULL ,
WHILE #CurrentDelimiterPositionVar <= #Count --1st loop
BEGIN
--Load current value from the Table
SELECT #ColumnName = ColumnName FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar
SELECT #DataType = DataType FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar
SELECT #MaxLength = MaxLength FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar
set #IsNullable = ( select IsNullable FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar )
set #IsPrimaryKey = ( select IsPrimaryKey FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar )
if ( #DataType = 'timestamp' or #IsComputed = 1)
begin
set #CurrentDelimiterPositionVar = #CurrentDelimiterPositionVar + 1
continue
end
set #CODESTR = #CODESTR + '#' + #ColumnName + ' ['+ #DataType + '] '
--ADD THE (200)
IF #MaxLength IS NOT NULL
BEGIN --IF #MaxLength IS NOT NULL
--xml
if #DataType <> 'xml' and #DataType <> 'sql_variant' and
#DataType <> 'text' and #DataType <> 'ntext' and #DataType <> 'image' and
#DataType <> 'hierarchyid' and #DataType <> 'bit' and #DataType <> 'varbinary' and
#DataType <> 'int' and #DataType <> 'uniqueidentifier' and #DataType <> 'tinyint' and
#DataType <> 'timestamp' and #DataType <> 'uniqueidentifier' and #DataType <> 'smallint' and
#DataType <> 'bigint' and #DataType <> 'smallmoney' and #DataType <> 'money' and
#DataType <> 'real' and #DataType <> 'smalldatetime' and #DataType <> 'datetime'
begin --those with()
if #MaxLength <> -1
SET #CODESTR = #CODESTR + '(' + CONVERT(VARCHAR , #MaxLength ) + ')'
else
SET #CODESTR = #CODESTR + '(max)'
end --those with(200)
else
begin
SET #CODESTR = #CODESTR --DO NOTHING
end
END --IF #MaxLength IS NOT NULL
IF #IsNullable = 1
SET #CODESTR = + #CODESTR + ' = NULL '
if #IsPrimaryKey = 1
SET #CODESTR = #CODESTR + ' OUTPUT '
if #CurrentDelimiterPositionVar <> #Count
SET #CODESTR = #CODESTR + ','
--DEBUGGING
--set #CODESTR = #CODESTR + '#ColumnName - ' + #ColumnName
--set #CODESTR = #CODESTR + '#DataType - ' + #DataType
--set #CODESTR = #CODESTR + '#IsNullable - ' + #IsNullable
--set #CODESTR = #CODESTR + '#MaxLength - ' + CONVERT ( VARCHAR , #MaxLength )
set #CODESTR = #CODESTR + CHAR(13)
SET #CurrentDelimiterPositionVar = #CurrentDelimiterPositionVar + 1
END
SET #CODESTR = #CODESTR + '
WITH EXECUTE AS CALLER
AS
BEGIN -- proc start
SET NOCOUNT ON;
BEGIN TRY --begin try
--
set #Ret = 1 --assume false from the beginning
declare #MsgKey [nvarchar](max)
declare #MsgTxt [nvarchar](max)
exec procUtils_GetMsgTxtByKeyAndUserSessionId
#UserSessionId =2 ,
#MsgKey = ''MsgOkTheAddingOfItemIsOk'' ,
#MsgTxt = ''''
set #MsgOut = replace (#MsgTxt , ''{0}'' , ''' + #TableName + ''' )
declare #thisProcName varchar(300)
set #thisProcName= ( SELECT OBJECT_NAME(##PROCID))
'
SET #CurrentDelimiterPositionVar = 1 --START LOOP AGAIN
set #CODESTR = #CODESTR + '
--Action !!!
INSERT INTO [dbo].[' + #TableName + ']( ' + CHAR(13)
--Loop through until all row processing is done
WHILE #CurrentDelimiterPositionVar <= #Count --2nd loop
BEGIN
--Load current value from the Table
SELECT #ColumnName = ColumnName FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar
SELECT #DataType = DataType FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar
SELECT #MaxLength = MaxLength FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar
set #IsNullable = ( select IsNullable FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar )
set #IsPrimaryKey = ( select IsPrimaryKey FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar )
if #IsPrimaryKey = 1
begin -- the primary key
set #PkColumnName = #ColumnName
end --the primary key
if ( #DataType = 'timestamp' or #IsComputed = 1 or #IsPrimaryKey = 1 )
begin
set #CurrentDelimiterPositionVar = #CurrentDelimiterPositionVar + 1
continue
end
--select
if #CurrentDelimiterPositionVar <= #Count
BEGIN
set #CODESTR = #CODESTR + '[' + #ColumnName + ']' --null the codestring var
if #CurrentDelimiterPositionVar <> #Count
set #CODESTR = #CODESTR + ', --type of ' + #DataType + CHAR(13) --WITH COMMA
ELSE
set #CODESTR = #CODESTR + ' --type of ' + #DataType + CHAR(13) --NO COMMA
END -- IF SHOULD PRINT COLUMN
SET #CurrentDelimiterPositionVar = #CurrentDelimiterPositionVar + 1;
END --eof while 2
set #CODESTR = #CODESTR + ') VALUES ( '
--AND START ALL OVER AGAIN
SET #CurrentDelimiterPositionVar = 1
--Loop through until all row processing is done
WHILE #CurrentDelimiterPositionVar <= #Count --WHILE 3
BEGIN
--Load current value from the Table
SELECT #ColumnName = ColumnName FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar
SELECT #DataType = DataType FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar
SELECT #MaxLength = MaxLength FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar
set #IsNullable = ( select IsNullable FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar )
set #IsPrimaryKey = ( select IsPrimaryKey FROM #ColumnNames
WHERE Number = #CurrentDelimiterPositionVar )
if ( #DataType = 'timestamp' or #IsComputed = 1 or #IsPrimaryKey = 1)
begin
set #CurrentDelimiterPositionVar = #CurrentDelimiterPositionVar + 1
continue
end
set #CODESTR = #CODESTR + '#' + #ColumnName
if #CurrentDelimiterPositionVar <= #Count
BEGIN
IF #CurrentDelimiterPositionVar <> #Count
set #CODESTR = #CODESTR + ' , --type of ' + #DataType --all others with comma
else
set #CODESTR = #CODESTR + ' --type of ' + #DataType --the last one without comma
END -- IF SHOULD NOT PRINT COLUMN
--increase the counter
set #CODESTR = #CODESTR + CHAR(13)
SET #CurrentDelimiterPositionVar = #CurrentDelimiterPositionVar + 1;
END
set nocount off
SET #CODESTR = #CODESTR + ')
SET #' + #pkColumnName + ' = ##IDENTITY
set #Ret = ##ERROR
set #DebugMsgOut = ''TODO:REMOVE INSERT OK ''
END TRY
BEGIN CATCH
EXEC #ret = [dbo].[procUtils_GetMsgTxtByKeyAndUserSessionId]
#UserSessionId = 2,
#MsgKey = N''ErrorMsgMenuRetrievalFailed'',
#MsgTxt = #MsgOut OUTPUT
set #ret = 1
set #msgOut = #MsgTxt
set #debugMsgOut = '' Error number: '' + CAST(ERROR_NUMBER() AS varchar(100)) +
''Error message: '' + ERROR_MESSAGE() + ''Error severity: '' +
CAST(ERROR_SEVERITY() AS varchar(10)) +
''Error state: '' + CAST(ERROR_STATE() AS varchar(100)) +
''XACT_STATE: '' + CAST(XACT_STATE() AS varchar(100))
-- record the error in the database
set #debugMsgOut = #debugMsgOut + #msgOut
INSERT INTO [dbo].[LogStore] ( [Date],[Thread],[Level],[Logger],[Message])
values ( getdate() , N''8'', N''DEBUG'', #thisProcName , #debugMsgOut )
END CATCH
END --procedure end
GO
'
print #codestr
END TRY --end try
BEGIN CATCH
print ' Error number: ' + CAST(ERROR_NUMBER() AS varchar(100)) +
'Error message: ' + ERROR_MESSAGE() + 'Error severity: ' +
CAST(ERROR_SEVERITY() AS varchar(1000)) +
'Error state: ' + CAST(ERROR_STATE() AS varchar(100)) +
'XACT_STATE: ' + CAST(XACT_STATE() AS varchar(100))
END CATCH
END --procedure end
--USE [Gaf]
--GO
--SELECT NAME FROM SYS.tables where name like '%Msg%'
--EXEC [dbo].[procUtils_GenerateInsertProc] #TableName = N'Whatever'
GO

SQL Server - Get a list of datatypes, max length, if its an identity and if its a primary key without object_ID function

I need a query that gets the datatype, max length, if its an identity and if its a primary key from a specific table_name without the use of the object_id function. So far what I have right now (and also found this somewhere on stackoverflow):
SELECT col.COLUMN_NAME AS ColumnName
, col.DATA_TYPE AS DataType
, col.CHARACTER_MAXIMUM_LENGTH AS MaxLength
, COLUMNPROPERTY(OBJECT_ID('[' + col.TABLE_SCHEMA + '].[' + col.TABLE_NAME + ']'), col.COLUMN_NAME, 'IsIdentity')AS IS_IDENTITY
, CAST(ISNULL(pk.is_primary_key, 0)AS bit)AS IsPrimaryKey
FROM INFORMATION_SCHEMA.COLUMNS AS col
LEFT JOIN(SELECT SCHEMA_NAME(o.schema_id)AS TABLE_SCHEMA
, o.name AS TABLE_NAME
, c.name AS COLUMN_NAME
, i.is_primary_key
FROM sys.indexes AS i JOIN sys.index_columns AS ic ON i.object_id = ic.object_id
AND i.index_id = ic.index_id
JOIN sys.objects AS o ON i.object_id = o.object_id
LEFT JOIN sys.columns AS c ON ic.object_id = c.object_id
AND c.column_id = ic.column_id
WHERE i.is_primary_key = 1)AS pk ON col.TABLE_NAME = pk.TABLE_NAME
AND col.TABLE_SCHEMA = pk.TABLE_SCHEMA
AND col.COLUMN_NAME = pk.COLUMN_NAME
WHERE col.TABLE_NAME = 'tbl_users'
ORDER BY col.TABLE_NAME, col.ORDINAL_POSITION;
Im using this code for a cursor and when I try to get the value of IS_IDENTITY, its always empty or something. I feel like dynamic sql and cursor don't like the OBJECT_ID function. When I run this query without the cursor and stuff, it works perfectly fine.
FULL CODE:
ALTER Procedure [dbo].[sp_generateUpserts]
#databaseName nvarchar(MAX)
AS
BEGIN
SET NOCOUNT ON
DECLARE #tranState BIT
IF ##TRANCOUNT = 0
BEGIN
SET #tranState = 1
BEGIN TRANSACTION tranState
END
BEGIN TRY
Declare #TABLE_NAME varchar(100)
Declare #COLUMN_NAME varchar(100)
Declare #COLUMN_NAME_WITH_SPACE varchar(100)
Declare #DATA_TYPE varchar(100)
Declare #CHARACTER_MAXIMUM_LENGTH INT
Declare #IS_PK INT
Declare #IS_IDENTITY INT
DECLARE #statement nvarchar(MAX)
SET #statement = N'USE [' + #databaseName + '];
DECLARE cursorUpsert CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
ORDER BY TABLE_NAME'
EXECUTE sp_executesql #statement
DECLARE #use_db nvarchar(max)
DECLARE #generateSpStatement varchar(MAX)
DECLARE #spParameters varchar(MAX) = ''
DECLARE #whereColumns varchar(MAX) = ''
DECLARE #updateStatement varchar(MAX) = ''
DECLARE #insertStatement varchar(MAX) = ''
DECLARE #valueStatement varchar(MAX) = ''
OPEN cursorUpsert
FETCH NEXT FROM cursorUpsert
INTO #TABLE_NAME
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #statementColumns nvarchar(MAX)
SET #statementColumns = N'USE [' + #databaseName + '];
DECLARE cursorUpsertColumns CURSOR FOR
SELECT col.COLUMN_NAME
, col.DATA_TYPE
, col.CHARACTER_MAXIMUM_LENGTH
, COLUMNPROPERTY(OBJECT_ID(QUOTENAME(col.TABLE_SCHEMA) + ''.'' + QUOTENAME(col.TABLE_NAME)), col.COLUMN_NAME, ''IsIdentity'')AS IS_IDENTITY
, CAST(ISNULL(pk.is_primary_key, 0) AS bit) AS IS_PK
FROM INFORMATION_SCHEMA.COLUMNS AS col
LEFT JOIN(SELECT SCHEMA_NAME(o.schema_id)AS TABLE_SCHEMA
, o.name AS TABLE_NAME
, c.name AS COLUMN_NAME
, i.is_primary_key
FROM sys.indexes AS i JOIN sys.index_columns AS ic ON i.object_id = ic.object_id
AND i.index_id = ic.index_id
JOIN sys.objects AS o ON i.object_id = o.object_id
LEFT JOIN sys.columns AS c ON ic.object_id = c.object_id
AND c.column_id = ic.column_id
WHERE i.is_primary_key = 1)AS pk ON col.TABLE_NAME = pk.TABLE_NAME
AND col.TABLE_SCHEMA = pk.TABLE_SCHEMA
AND col.COLUMN_NAME = pk.COLUMN_NAME
WHERE col.TABLE_NAME = ''' + #TABLE_NAME + '''
ORDER BY col.TABLE_NAME, col.ORDINAL_POSITION;'
EXECUTE sp_executesql #statementColumns
OPEN cursorUpsertColumns
FETCH NEXT FROM cursorUpsertColumns
INTO #COLUMN_NAME, #DATA_TYPE, #CHARACTER_MAXIMUM_LENGTH, #IS_IDENTITY, #IS_PK
WHILE ##FETCH_STATUS = 0
BEGIN
-- Parameters for the SP
IF #COLUMN_NAME LIKE '% %'
BEGIN
SET #COLUMN_NAME_WITH_SPACE = #COLUMN_NAME
SET #COLUMN_NAME_WITH_SPACE = REPLACE(#COLUMN_NAME_WITH_SPACE,' ','_')
SET #spParameters = #spParameters + CHAR(13) + '#' + #COLUMN_NAME_WITH_SPACE + ' ' + #DATA_TYPE
END
ELSE
BEGIN
SET #spParameters = #spParameters + CHAR(13) + '#' + #COLUMN_NAME + ' ' + #DATA_TYPE
END
IF #DATA_TYPE IN ('varchar', 'nvarchar', 'char', 'nchar')
BEGIN
IF #CHARACTER_MAXIMUM_LENGTH = '-1'
BEGIN
SET #spParameters = #spParameters + '(MAX)'
END
ELSE
BEGIN
SET #spParameters = #spParameters + '(' + CAST(#CHARACTER_MAXIMUM_LENGTH As Varchar(10)) + ')'
END
END
-- Add a comma after each parameter
SET #spParameters = #spParameters + ', '
IF #COLUMN_NAME IN ('top')
BEGIN
IF #IS_IDENTITY != 1
BEGIN
print('YES IDENTITY')
END
-- Add where parameters: ColumnName=#ColumnName AND
SET #whereColumns = #whereColumns + CHAR(32) + '[' + #COLUMN_NAME + ']=#' + #COLUMN_NAME + ' AND'
-- Add update parameters: column1 = value1, etc.
IF #IS_IDENTITY != 1 OR #IS_PK != 1
BEGIN
SET #updateStatement = #updateStatement + CHAR(32) + '[' + #COLUMN_NAME + ']=#' + #COLUMN_NAME + ','
END
-- Add insert columns
SET #insertStatement = #insertStatement + CHAR(32) + '[' + #COLUMN_NAME + '],'
-- Add values
SET #valueStatement = #valueStatement + CHAR(32) + '#' + #COLUMN_NAME + ','
END
ELSE IF #COLUMN_NAME LIKE '% %'
BEGIN
IF #IS_IDENTITY != 1
BEGIN
print('YES IDENTITY')
END
-- Add where parameters: ColumnName=#ColumnName AND
SET #whereColumns = #whereColumns + CHAR(32) + '[' + #COLUMN_NAME + ']=#' + #COLUMN_NAME_WITH_SPACE + ' AND'
-- Add update parameters: column1 = value1, etc.
IF #IS_IDENTITY != 1 OR #IS_PK != 1
BEGIN
SET #updateStatement = #updateStatement + CHAR(32) + '[' + #COLUMN_NAME + ']=#' + #COLUMN_NAME_WITH_SPACE + ','
END
-- Add insert columns
SET #insertStatement = #insertStatement + CHAR(32) + '['+ #COLUMN_NAME + '],'
-- Add values
SET #valueStatement = #valueStatement + CHAR(32) + '#' + #COLUMN_NAME_WITH_SPACE + ','
END
ELSE
BEGIN
IF #IS_IDENTITY != 1
BEGIN
print('YES IDENTITY')
END
-- Add where parameters: ColumnName=#ColumnName AND
SET #whereColumns = #whereColumns + CHAR(32) + #COLUMN_NAME + '=#' + #COLUMN_NAME + ' AND'
-- Add update parameters: column1 = value1, etc.
IF #IS_IDENTITY != 1 OR #IS_PK != 1
BEGIN
SET #updateStatement = #updateStatement + CHAR(32) + #COLUMN_NAME + '=#' + #COLUMN_NAME + ','
END
-- Add insert columns
SET #insertStatement = #insertStatement + CHAR(32) + #COLUMN_NAME + ','
-- Add values
SET #valueStatement = #valueStatement + CHAR(32) + '#' + #COLUMN_NAME + ','
END
FETCH NEXT FROM cursorUpsertColumns
INTO #COLUMN_NAME, #DATA_TYPE, #CHARACTER_MAXIMUM_LENGTH, #IS_IDENTITY, #IS_PK
if ##FETCH_STATUS!=0
begin
-- Last row, remove things
-- Remove the last AND word
SET #whereColumns = left (#whereColumns, len(#whereColumns) -3)
-- Remove the last comma from the parameter
SET #spParameters = left (#spParameters, len(#spParameters) -1)
-- Remove the last comma from the updateStatement
SET #updateStatement = left (#updateStatement, len(#updateStatement) -1)
-- Remove the last comma from the insertStatement
SET #insertStatement = left (#insertStatement, len(#insertStatement) -1)
-- Remove the last comma from the valueStatement
SET #valueStatement = left (#valueStatement, len(#valueStatement) -1)
end
END;
CLOSE cursorUpsertColumns;
DEALLOCATE cursorUpsertColumns;
--- End Cursor Columns
-- Generate the SP
SET #generateSpStatement = 'CREATE Procedure [dbo].[sp_' + #TABLE_NAME + '_upsert]' + #spParameters
SET #generateSpStatement = #generateSpStatement + CHAR(13) + 'AS BEGIN' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'SET NOCOUNT ON' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'DECLARE #tranState BIT' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'IF ##TRANCOUNT = 0' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'BEGIN' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + CHAR(9) +'SET #tranState = 1' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + CHAR(9) +'set transaction isolation level serializable' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + CHAR(9) +'BEGIN TRANSACTION tranState' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'END' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(13) + 'BEGIN TRY' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'IF EXISTS(SELECT 1 FROM ' + #TABLE_NAME + ' WITH (updlock) WHERE' + #whereColumns + ')' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + CHAR(9) + 'UPDATE ' + #TABLE_NAME + ' SET' + #updateStatement + ' WHERE ' + #whereColumns + ';' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'ELSE' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + CHAR(9) + 'INSERT INTO ' + #TABLE_NAME + ' ('+ #insertStatement + ') VALUES (' + #valueStatement + ');' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'IF #tranState = 1 AND XACT_STATE() = 1' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + CHAR(9) + 'COMMIT TRANSACTION tranState' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + 'END TRY' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(13) + 'BEGIN CATCH' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'DECLARE #Error_Message VARCHAR(5000)' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'DECLARE #Error_Severity INT' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'DECLARE #Error_State INT' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'SELECT #Error_Message = ERROR_MESSAGE()' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'SELECT #Error_Severity = ERROR_SEVERITY()' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'SELECT #Error_State = ERROR_STATE()' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'IF #tranState = 1 AND XACT_STATE() <> 0' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + CHAR(9) +'ROLLBACK TRANSACTION' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(9) + 'RAISERROR (#Error_Message, #Error_Severity, #Error_State)' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + 'END CATCH' + CHAR(13)
SET #generateSpStatement = #generateSpStatement + CHAR(13)
SET #generateSpStatement = #generateSpStatement + 'END' + CHAR(13)
--print(#generateSpStatement)
-- Reset Variables
SET #generateSpStatement = ''
SET #spParameters = ''
SET #whereColumns = ''
SET #updateStatement = ''
SET #insertStatement = ''
SET #valueStatement = ''
FETCH NEXT FROM cursorUpsert
INTO #TABLE_NAME
END;
CLOSE cursorUpsert;
DEALLOCATE cursorUpsert;
IF #tranState = 1
AND XACT_STATE() = 1
COMMIT TRANSACTION tranState
END TRY
BEGIN CATCH
DECLARE #Error_Message VARCHAR(5000)
DECLARE #Error_Severity INT
DECLARE #Error_State INT
SELECT #Error_Message = ERROR_MESSAGE()
SELECT #Error_Severity = ERROR_SEVERITY()
SELECT #Error_State = ERROR_STATE()
IF #tranState = 1 AND XACT_STATE() <> 0
ROLLBACK TRANSACTION
RAISERROR (#Error_Message, #Error_Severity, #Error_State)
END CATCH
END
I feel like dynamic sql and cursor don't like the OBJECT_ID function.
It has nothing to do with OBJECT_ID but most likely incorrect double qouting when using with dynamic string:
COLUMNPROPERTY(OBJECT_ID(''['' + col.TABLE_SCHEMA + ''].['' + col.TABLE_NAME + '']''), col.COLUMN_NAME, ''IsIdentity'')AS IS_IDENTITY
Anyway you should avoid manually adding [ and use QUOTENAME instead:
COLUMNPROPERTY(OBJECT_ID(QUOTENAME(col.TABLE_SCHEMA) + ''.'' + QUOTENAME(col.TABLE_NAME)), col.COLUMN_NAME, ''IsIdentity'')AS IS_IDENTITY
It's a common case and it would be really nice if here-strings/text quoting were supported.
Another slightly simpler way of getting this information would be something like..
Declare #Schema SYSNAME = 'dbo'
, #Table SYSNAME= 'Orders'
SELECT
name Column_Name
, system_type_name Data_Type
, max_length Max_Length
, is_identity_column Is_Identity_Column
, ISNULL(c.PK_Column,0) Is_Primary_Key_Column
FROM sys.dm_exec_describe_first_result_set (N'SELECT * FROM '+ #Schema +'.' + #Table, null, 0) r
OUTER APPLY (
SELECT 1 PK_Column
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE s
WHERE OBJECTPROPERTY(OBJECT_ID(s.CONSTRAINT_SCHEMA + '.' + QUOTENAME(s.CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
AND s.TABLE_NAME = #Table
AND s.TABLE_SCHEMA = #Schema
AND r.name COLLATE DATABASE_DEFAULT = s.COLUMN_NAME
) c(PK_Column)
You can put this code inside a function and simply call this function cross applying it with sys.tables catalog view.
The dynamic management view sys.dm_exec_describe_first_result_set has lots of other useful information too.
Lets suppose you create a function like this..
CREATE FUNCTION dbo.fn_get_Column_Info (
#Schema SYSNAME
, #Table SYSNAME)
RETURNS TABLE
AS
RETURN
(
SELECT
name Column_Name
, system_type_name Data_Type
, max_length Max_Length
, is_identity_column Is_Identity_Column
, ISNULL(c.PK_Column,0) Is_Primary_Key_Column
FROM sys.dm_exec_describe_first_result_set (N'SELECT * FROM '+ #Schema +'.' + #Table, null, 0) r
OUTER APPLY (
SELECT 1 PK_Column
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE s
WHERE OBJECTPROPERTY(OBJECT_ID(s.CONSTRAINT_SCHEMA + '.' + QUOTENAME(s.CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
AND s.TABLE_NAME = #Table
AND s.TABLE_SCHEMA = #Schema
AND r.name COLLATE DATABASE_DEFAULT = s.COLUMN_NAME
) c(PK_Column)
);
GO
Then your query to get all the information you need would be as simple as..
SELECT s.name , t.name, f.*
FROM sys.schemas s
INNER JOIN sys.Tables t ON s.schema_id = t.schema_id
CROSS APPLY dbo.fn_get_Column_Info(s.name , t.name) f;
No need for any cursor or dynamic SQL, unless you want to do this for all the databases on a server. But even if you had to do that for all databases it would be a much simpler cursor.

Looping in a dynamic sql query

I've nearly got this dynamic query working:
DECLARE
#ord int = 1,
#wght int = 0,
#bit int,
#colname varchar(max),
#col2 varchar(max),
#sstr varchar(max),
#fstr varchar(max),
#wstr varchar(max),
#sum varchar(max) = ' 0',
#colist varchar(max) = '',
#frlist varchar(max) = '',
#whlist varchar(max) = '',
#sql varchar(max)
WHILE #wght < 2
BEGIN
SET #wght = #wght + 1
WHILE #ord < 3
BEGIN
SET #ord = #ord + 1
SELECT #colname = BR FROM dbo.[Y2 Ordinal] WHERE ORDINAL_POSITION= #ord
SET #sstr = '(A.' + #colname + '*B' + CAST(#wght AS varchar(max))+'.' + #colname +')'
SET #sum = #sum + '+' + #sstr
END
SELECT #w = [ID NO] FROM dbo.[Weightings] WHERE [ID NO]= #wght
SET #fstr = ' dbo.[Weightings] AS B' + CAST(#wght AS varchar(max))
SELECT #col2 = [Batch ID] FROM dbo.[Weightings] WHERE [ID NO]= #wght
SET #wstr = 'B' + CAST(#wght AS varchar(max)) + '.[Portfolio ID] = ' + '''' + #col2 + ''''
SET #colist = #colist + ',' +(#sum)+' AS ' +''''+(#col2)+''''
SET #frlist = #frlist + ',' +(#fstr)
SET #whlist = #whlist + ' AND ' +(#wstr)
END
SET #sql = '(SELECT A.Sim' + #colist+ ' FROM dbo.[Y2 Net of Fees] As A' + #frlist +' WHERE A.[Simulation] IS NOT NULL ' + #whlist + ')ORDER BY Simulation'
PRINT(#sql);
The output sql statement (reformatted here) is:
(SELECT A.Sim,
0+(A.[Apples]*B1.[Apples])+(A.[Oranges]*B1.[Oranges]) AS 'Batch A',
0+(A.[Apples]*B1.[Apples])+(A.[Oranges]*B1.[Oranges]) AS 'Batch B'
FROM dbo.[Fruit] As A,
dbo.[Weightings] AS B1,
dbo.[Weightings] AS B2
WHERE A.[Simulation] IS NOT NULL
AND B1.[Batch ID] = 'Batch A'
AND B2.[Batch ID] = 'Batch B')
ORDER BY Simulation
My desired output is:
SELECT A.Sim,
0+(A.[Apples]*B1.[Apples])+(A.[Oranges]*B1.[Oranges]) AS 'Batch A',
0+(A.[Apples]*B2.[Apples])+(A.[Oranges]*B2.[Oranges]) AS 'Batch B'
So my problem is the #wght remaining static in the second while loop, I was thinking the when it increased in the first loop it would feed into the second. Any suggestions?
USE APPLICATION
GO
SET NOCOUNT ON
IF OBJECT_ID('tempdb.dbo.#Y2_Ordinal') IS NOT NULL
BEGIN
DROP TABLE #Y2_Ordinal
END
IF OBJECT_ID('tempdb.dbo.#Weightings') IS NOT NULL
BEGIN
DROP TABLE #Weightings
END
GO
CREATE TABLE #Y2_Ordinal(
COLUMN_NAME VARCHAR(100)
,ORDINAL_POSITION INT
);
CREATE TABLE #Weightings(
[Batch ID] INT
,[ID NO] INT
);
GO
INSERT INTO #Y2_Ordinal
(COLUMN_NAME
,ORDINAL_POSITION)
VALUES('Apples'
,'1')
INSERT INTO #Y2_Ordinal
(COLUMN_NAME
,ORDINAL_POSITION)
VALUES('Oranges'
,'2')
--SELECT * FROM #Y2_Ordinal
--INSERT INTO #Y2_Ordinal
-- (COLUMN_NAME
-- ,ORDINAL_POSITION)
-- VALUES('Grapes'
-- ,'3')
INSERT INTO #Weightings VALUES('1','1')
INSERT INTO #Weightings VALUES('2','2')
INSERT INTO #Weightings VALUES('3','3')
DECLARE
#ord int = 1,
#wght int = 0,
#bit int,
#colname varchar(max),
#col2 varchar(max),
#sstr varchar(max),
#fstr varchar(max),
#wstr varchar(max),
#sum varchar(max) = '',
#colist varchar(max) = '',
#frlist varchar(max) = '',
#whlist varchar(max) = '',
#sql varchar(1000),
#w VARCHAR(max)
WHILE #wght < 2
BEGIN
SET #wght = #wght + 1
--PRINT(#wght)
SET #ord = 1
Set #sum = ''
WHILE #ord < 3
BEGIN
--PRINT('ord' + CAST(#ord AS VARCHAR(100)))
SELECT #colname = COLUMN_NAME FROM #Y2_Ordinal WHERE ORDINAL_POSITION= #ord
--PRINT(#colname)
SET #sstr = '(A.' + #colname + '*B' + CAST(#wght AS varchar(max))+'.' + #colname +')'
--PRINT(#sstr)
SET #sum = #sum + '+' + #sstr
--PRINT(#sum)
SET #ord = #ord + 1
END
SELECT #w = [ID NO] FROM #Weightings WHERE [ID NO]= #wght
SET #fstr = ' dbo.[Weightings] AS B' + CAST(#wght AS varchar(max))
SELECT #col2 = [Batch ID] FROM #Weightings WHERE [ID NO]= #wght
--PRINT ('col2' + #col2)
SET #wstr = 'B' + CAST(#wght AS varchar(max)) + '.[Portfolio ID] = ' + '''' + #col2 + ''''
SET #colist = #colist + ', 0' +(#sum)+' AS ' +''''+(#col2)+''''
--PRINT ('col_list' + #colist)
SET #frlist = #frlist + ',' +(#fstr)
SET #whlist = #whlist + ' AND ' +(#wstr)
END
--PRINT(#colist)
--PRINT(#frlist)
--PRINT(#whlist)
SET #sql = '(SELECT A.Sim' + #colist+ ' FROM dbo.[Y2 Net of Fees] As A'
+ #frlist +' WHERE A.[Simulation] IS NOT NULL '
+ #whlist + ')ORDER BY Simulation'
PRINT #SQL

Generate Delete Statement From Foreign Key Relationships in SQL 2008?

Is it possible via script/tool to generate a delete statement based on the tables fk relations.
i.e. I have the table: DelMe(ID) and there are 30 tables with fk references to its ID that I need to delete first, is there some tool/script that I can run that will generate the 30 delete statements based on the FK relations for me ?
(btw I know about cascade delete on the relations, I can't use it in this existing db)
I'm using Microsoft SQL Server 2008
Here is a script for cascading delete by Aasim Abdullah, works for me on MS SQL Server 2008:
IF OBJECT_ID('dbo.udfGetFullQualName') IS NOT NULL
DROP FUNCTION dbo.udfGetFullQualName;
GO
CREATE FUNCTION dbo.udfGetFullQualName
(#ObjectId INT)
RETURNS VARCHAR (300)
AS
BEGIN
DECLARE #schema_id AS BIGINT;
SELECT #schema_id = schema_id
FROM sys.tables
WHERE object_id = #ObjectId;
RETURN '[' + SCHEMA_NAME(#schema_id) + '].[' + OBJECT_NAME(#ObjectId) + ']';
END
GO
--============ Supporting Function dbo.udfGetOnJoinClause
IF OBJECT_ID('dbo.udfGetOnJoinClause') IS NOT NULL
DROP FUNCTION dbo.udfGetOnJoinClause;
GO
CREATE FUNCTION dbo.udfGetOnJoinClause
(#fkNameId INT)
RETURNS VARCHAR (1000)
AS
BEGIN
DECLARE #OnClauseTemplate AS VARCHAR (1000);
SET #OnClauseTemplate = '[<#pTable>].[<#pCol>] = [<#cTable>].[<#cCol>] AND ';
DECLARE #str AS VARCHAR (1000);
SET #str = '';
SELECT #str = #str + REPLACE(REPLACE(REPLACE(REPLACE(#OnClauseTemplate, '<#pTable>', OBJECT_NAME(rkeyid)), '<#pCol>', COL_NAME(rkeyid, rkey)), '<#cTable>', OBJECT_NAME(fkeyid)), '<#cCol>', COL_NAME(fkeyid, fkey))
FROM dbo.sysforeignkeys AS fk
WHERE fk.constid = #fkNameId; --OBJECT_ID('FK_ProductArrearsMe_ProductArrears')
RETURN LEFT(#str, LEN(#str) - LEN(' AND '));
END
GO
--=========== CASECADE DELETE STORED PROCEDURE dbo.uspCascadeDelete
IF OBJECT_ID('dbo.uspCascadeDelete') IS NOT NULL
DROP PROCEDURE dbo.uspCascadeDelete;
GO
CREATE PROCEDURE dbo.uspCascadeDelete
#ParentTableId VARCHAR (300), #WhereClause VARCHAR (2000), #ExecuteDelete CHAR (1)='N', --'N' IF YOU NEED DELETE SCRIPT
#FromClause VARCHAR (8000)='', #Level INT=0 -- TABLE NAME OR OBJECT (TABLE) ID (Production.Location) WHERE CLAUSE (Location.LocationID = 7) 'Y' IF WANT TO DELETE DIRECTLY FROM SP, IF LEVEL 0, THEN KEEP DEFAULT
AS -- writen by Daniel Crowther 16 Dec 2004 - handles composite primary keys
SET NOCOUNT ON;
/* Set up debug */
DECLARE #DebugMsg AS VARCHAR (4000),
#DebugIndent AS VARCHAR (50);
SET #DebugIndent = REPLICATE('---', ##NESTLEVEL) + '> ';
IF ISNUMERIC(#ParentTableId) = 0
BEGIN -- assume owner is dbo and calculate id
IF CHARINDEX('.', #ParentTableId) = 0
SET #ParentTableId = OBJECT_ID('[dbo].[' + #ParentTableId + ']');
ELSE
SET #ParentTableId = OBJECT_ID(#ParentTableId);
END
IF #Level = 0
BEGIN
PRINT #DebugIndent + ' **************************************************************************';
PRINT #DebugIndent + ' *** Cascade delete ALL data from ' + dbo.udfGetFullQualName(#ParentTableId);
IF #ExecuteDelete = 'Y'
PRINT #DebugIndent + ' *** #ExecuteDelete = Y *** deleting data...';
ELSE
PRINT #DebugIndent + ' *** Cut and paste output into another window and execute ***';
END
DECLARE #CRLF AS CHAR (2);
SET #CRLF = CHAR(13) + CHAR(10);
DECLARE #strSQL AS VARCHAR (4000);
IF #Level = 0
SET #strSQL = 'SET NOCOUNT ON' + #CRLF;
ELSE
SET #strSQL = '';
SET #strSQL = #strSQL + 'PRINT ''' + #DebugIndent + dbo.udfGetFullQualName(#ParentTableId) + ' Level=' + CAST (##NESTLEVEL AS VARCHAR) + '''';
IF #ExecuteDelete = 'Y'
EXECUTE (#strSQL);
ELSE
PRINT #strSQL;
DECLARE curs_children CURSOR LOCAL FORWARD_ONLY
FOR SELECT DISTINCT constid AS fkNameId, -- constraint name
fkeyid AS cTableId
FROM dbo.sysforeignkeys AS fk
WHERE fk.rkeyid <> fk.fkeyid -- WE DO NOT HANDLE self referencing tables!!!
AND fk.rkeyid = #ParentTableId;
OPEN curs_children;
DECLARE #fkNameId AS INT,
#cTableId AS INT,
#cColId AS INT,
#pTableId AS INT,
#pColId AS INT;
FETCH NEXT FROM curs_children INTO #fkNameId, #cTableId; --, #cColId, #pTableId, #pColId
DECLARE #strFromClause AS VARCHAR (1000);
DECLARE #nLevel AS INT;
IF #Level = 0
BEGIN
SET #FromClause = 'FROM ' + dbo.udfGetFullQualName(#ParentTableId);
END
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #strFromClause = #FromClause + #CRLF + ' INNER JOIN ' + dbo.udfGetFullQualName(#cTableId) + #CRLF + ' ON ' + dbo.udfGetOnJoinClause(#fkNameId);
SET #nLevel = #Level + 1;
EXECUTE dbo.uspCascadeDelete #ParentTableId = #cTableId, #WhereClause = #WhereClause, #ExecuteDelete = #ExecuteDelete, #FromClause = #strFromClause, #Level = #nLevel;
SET #strSQL = 'DELETE FROM ' + dbo.udfGetFullQualName(#cTableId) + #CRLF + #strFromClause + #CRLF + 'WHERE ' + #WhereClause + #CRLF;
SET #strSQL = #strSQL + 'PRINT ''---' + #DebugIndent + 'DELETE FROM ' + dbo.udfGetFullQualName(#cTableId) + ' Rows Deleted: '' + CAST(##ROWCOUNT AS VARCHAR)' + #CRLF + #CRLF;
IF #ExecuteDelete = 'Y'
EXECUTE (#strSQL);
ELSE
PRINT #strSQL;
FETCH NEXT FROM curs_children INTO #fkNameId, #cTableId;
--, #cColId, #pTableId, #pColId
END
IF #Level = 0
BEGIN
SET #strSQL = #CRLF + 'PRINT ''' + #DebugIndent + dbo.udfGetFullQualName(#ParentTableId) + ' Level=' + CAST (##NESTLEVEL AS VARCHAR) + ' TOP LEVEL PARENT TABLE''' + #CRLF;
SET #strSQL = #strSQL + 'DELETE FROM ' + dbo.udfGetFullQualName(#ParentTableId) + ' WHERE ' + #WhereClause + #CRLF;
SET #strSQL = #strSQL + 'PRINT ''' + #DebugIndent + 'DELETE FROM ' + dbo.udfGetFullQualName(#ParentTableId) + ' Rows Deleted: '' + CAST(##ROWCOUNT AS VARCHAR)' + #CRLF;
IF #ExecuteDelete = 'Y'
EXECUTE (#strSQL);
ELSE
PRINT #strSQL;
END
CLOSE curs_children;
DEALLOCATE curs_children;
Usage example 1
Note the use of the fully qualified column name in the example. It's subtle but you must specify the table name for the generated SQL to execute properly.
EXEC uspCascadeDelete
#ParentTableId = 'Production.Location',
#WhereClause = 'Location.LocationID = 2'
Usage example 2
EXEC uspCascadeDelete
#ParentTableId = 'dbo.brand',
#WhereClause = 'brand.brand_name <> ''Apple'''
Usage example 3
exec uspCascadeDelete
#ParentTableId = 'dbo.product_type',
#WhereClause = 'product_type.product_type_id NOT IN
(SELECT bpt.product_type_id FROM dbo.brand_product_type bpt)'
DELETE statements generated for use in SP with parameter, and as ON DELETE triggers:
(this variant supports single column FKs only)
SELECT 'DELETE '+detail.name+' WHERE '+dcolumn.name+' = #'+mcolumn.name AS stmt,
'DELETE ' + detail.name + ' FROM ' + detail.name + ' INNER JOIN deleted ON ' +
detail.name + '.' + dcolumn.name + ' = deleted.' + mcolumn.name AS trg
FROM sys.columns AS mcolumn
INNER JOIN sys.foreign_key_columns ON mcolumn.object_id =
sys.foreign_key_columns.referenced_object_id
AND mcolumn.column_id = sys.foreign_key_columns.referenced_column_id
INNER JOIN sys.tables AS master ON mcolumn.object_id = master.object_id
INNER JOIN sys.columns AS dcolumn
ON sys.foreign_key_columns.parent_object_id = dcolumn.object_id
AND sys.foreign_key_columns.parent_column_id = dcolumn.column_id
INNER JOIN sys.tables AS detail ON dcolumn.object_id = detail.object_id
WHERE (master.name = N'MyTableName')
I'm pretty sure I posted code here on Stack Overflow which does this automatically using INFORMATION_SCHEMA to generate dynamic SQL, but I can't find it. Let me see if I can regenerate it.
You might need to check this out a bit, I couldn't find my original code, so I modified some code I had which builds flattend views for star-schemas automatically.
DECLARE #COLUMN_NAME AS sysname
DECLARE #TABLE_NAME AS sysname
DECLARE #IDValue AS int
SET #COLUMN_NAME = '<Your COLUMN_NAME here>'
SET #TABLE_NAME = '<Your TABLE_NAME here>'
SET #IDValue = 123456789
DECLARE #sql AS varchar(max) ;
WITH RELATED_COLUMNS
AS (
SELECT QUOTENAME(c.TABLE_SCHEMA) + '.'
+ QUOTENAME(c.TABLE_NAME) AS [OBJECT_NAME]
,c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS AS c WITH (NOLOCK)
INNER JOIN INFORMATION_SCHEMA.TABLES AS t WITH (NOLOCK)
ON c.TABLE_CATALOG = t.TABLE_CATALOG
AND c.TABLE_SCHEMA = t.TABLE_SCHEMA
AND c.TABLE_NAME = t.TABLE_NAME
AND t.TABLE_TYPE = 'BASE TABLE'
INNER JOIN (
SELECT rc.CONSTRAINT_CATALOG
,rc.CONSTRAINT_SCHEMA
,lkc.TABLE_NAME
,lkc.COLUMN_NAME
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
WITH (NOLOCK)
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE lkc
WITH (NOLOCK)
ON lkc.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
AND lkc.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
AND lkc.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
WITH (NOLOCK)
ON rc.CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG
AND rc.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
AND rc.UNIQUE_CONSTRAINT_NAME = tc.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE rkc
WITH (NOLOCK)
ON rkc.CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG
AND rkc.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
AND rkc.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
WHERE rkc.COLUMN_NAME = #COLUMN_NAME
AND rkc.TABLE_NAME = #TABLE_NAME
) AS j
ON j.CONSTRAINT_CATALOG = c.TABLE_CATALOG
AND j.CONSTRAINT_SCHEMA = c.TABLE_SCHEMA
AND j.TABLE_NAME = c.TABLE_NAME
AND j.COLUMN_NAME = c.COLUMN_NAME
)
SELECT #sql = COALESCE(#sql, '') + 'DELETE FROM ' + [OBJECT_NAME]
+ ' WHERE ' + [COLUMN_NAME] + ' = ' + CONVERT(varchar, #IDValue)
+ CHAR(13) + CHAR(10)
FROM RELATED_COLUMNS
PRINT #sql
Another technique is to use a code generator to create the Sql. I'm pretty sure the MyGeneration (no connection) has existing templates to do this. Using that tool and the right template you can create a sql script that deletes the relevant stuff with no pain.
Unfortunately, I think cascading is the tool you're asking for. I understand not being able to use it, but that fact that it exists as a built-in part of the db has pretty much killed the need for an alternative.
You can create all fk columns with a same name like 'row_id'
Then write the code below:
create procedure dbo.deleteRow
#row_id int
as
begin
set nocount on
begin transaction delete_row
declare #mainTableName varchar(50) = 'MyMainTableName'
begin try
declare #OBJECT_ID_mainTable int
select #OBJECT_ID_mainTable = OBJECT_ID from sys.tables where name = #mainTableName
create table #ids ( object_id int , table_name varchar (50) , referenced_object_id int , r_index int )
--1) select all tables are has fk to main table
insert into #ids select t.object id , t.name , fk.referenced object id ,
row_number () over ( order by
--how many tables are depends on me
(select COUNT ( * ) from sys . foreign_key_columns a fk where a_fk.referenced_object_id = fk.parent_object_id ) desc ) r_index
from sys.foreign_key_columns fk
join sys.tables t on t.object_id- fk.parent_object_id
where fk.referenced_object_id = #OBJECT_ID_mainTable
declare #i int = ( select max ( r_index ) from #ids )
declare #sqlBuilder nvarchar ( max )
--2) delete from fk tables in dependet order
while #i > 0
begin
--all fk column are called 'row_id'
set #sqlBuilder = concat ('delete from dbo.[' + ( select table_name from #ids where r_index = #i ) + ']' +
'where row_id = ', #row_id )
exec(#sqlBuilder)
set #i=#i-1
end
--3) delete from main table
delete from <MyMainTableName> where id = #row_id
commit transaction delete_row
end try
begin catch
rollback transaction delete_row
throw
end catch
end