sp_help_revlogin causing loss of characters on LOGINs with Unicode characters - sql

I want to export all users of SQL Server and I found following stored procedure from the documentation but when I Ran the Procedure, the results of some names just exported as ??????. For example:
-- Login: ??????
IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'??????')
BEGIN
CREATE LOGIN [??????] WITH PASSWORD = 0x0200B27F654A0090DFA7C3C857709164CDC91F1 HASHED, SID = 0x27926CE54FA0E64BB2300AE2E104C22C, DEFAULT_DATABASE = [master], DEFAULT_LANGUAGE = [us_english], CHECK_POLICY = OFF, CHECK_EXPIRATION = OFF
EXEC master.dbo.sp_addsrvrolemember #loginame='?????', #rolename='sysadmin'
END
All of Unicode users was shown as ?????? . the SP is as follow:
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
CREATE PROCEDURE [dbo].[sp_help_revlogin]
(
#login_name sysname = NULL
)
AS
BEGIN
DECLARE #name SYSNAME
DECLARE #type VARCHAR (1)
DECLARE #hasaccess INT
DECLARE #denylogin INT
DECLARE #is_disabled INT
DECLARE #PWD_varbinary VARBINARY (256)
DECLARE #PWD_string VARCHAR (514)
DECLARE #SID_varbinary VARBINARY (85)
DECLARE #SID_string VARCHAR (514)
DECLARE #tmpstr VARCHAR (1024)
DECLARE #is_policy_checked VARCHAR (3)
DECLARE #is_expiration_checked VARCHAR (3)
Declare #Prefix VARCHAR(255)
DECLARE #defaultdb SYSNAME
DECLARE #defaultlanguage SYSNAME
DECLARE #tmpstrRole VARCHAR (1024)
IF (#login_name IS NULL)
BEGIN
DECLARE login_curs CURSOR
FOR
SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin, p.default_language_name
FROM sys.server_principals p
LEFT JOIN sys.syslogins l ON ( l.name = p.name )
WHERE p.type IN ( 'S', 'G', 'U' )
AND p.name <> 'sa'
ORDER BY p.name
END
ELSE
DECLARE login_curs CURSOR
FOR
SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin, p.default_language_name
FROM sys.server_principals p
LEFT JOIN sys.syslogins l ON ( l.name = p.name )
WHERE p.type IN ( 'S', 'G', 'U' )
AND p.name = #login_name
ORDER BY p.name
OPEN login_curs
FETCH NEXT FROM login_curs INTO #SID_varbinary, #name, #type, #is_disabled, #defaultdb, #hasaccess, #denylogin, #defaultlanguage
IF (##fetch_status = -1)
BEGIN
PRINT 'No login(s) found.'
CLOSE login_curs
DEALLOCATE login_curs
RETURN -1
END
SET #tmpstr = '/* sp_help_revlogin script '
PRINT #tmpstr
SET #tmpstr = '** Generated ' + CONVERT (varchar, GETDATE()) + ' on ' + ##SERVERNAME + ' */'
PRINT #tmpstr
PRINT ''
WHILE (##fetch_status <> -1)
BEGIN
IF (##fetch_status <> -2)
BEGIN
PRINT ''
SET #tmpstr = '-- Login: ' + #name
PRINT #tmpstr
SET #tmpstr='IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'''+#name+''')
BEGIN'
Print #tmpstr
IF (#type IN ( 'G', 'U'))
BEGIN -- NT authenticated account/group
SET #tmpstr = 'CREATE LOGIN ' + QUOTENAME( #name ) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + #defaultdb + ']'
END
ELSE
BEGIN -- SQL Server authentication
-- obtain password and sid
SET #PWD_varbinary = CAST( LOGINPROPERTY( #name, 'PasswordHash' ) AS varbinary (256) )
EXEC sp_hexadecimal #PWD_varbinary, #PWD_string OUT
EXEC sp_hexadecimal #SID_varbinary,#SID_string OUT
-- obtain password policy state
SELECT #is_policy_checked = CASE is_policy_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END
FROM sys.sql_logins
WHERE name = #name
SELECT #is_expiration_checked = CASE is_expiration_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END
FROM sys.sql_logins
WHERE name = #name
SET #tmpstr = 'CREATE LOGIN ' + QUOTENAME( #name ) + ' WITH PASSWORD = ' + #PWD_string + ' HASHED, SID = '
+ #SID_string + ', DEFAULT_DATABASE = [' + #defaultdb + ']' + ', DEFAULT_LANGUAGE = [' + #defaultlanguage + ']'
IF ( #is_policy_checked IS NOT NULL )
BEGIN
SET #tmpstr = #tmpstr + ', CHECK_POLICY = ' + #is_policy_checked
END
IF ( #is_expiration_checked IS NOT NULL )
BEGIN
SET #tmpstr = #tmpstr + ', CHECK_EXPIRATION = ' + #is_expiration_checked
END
END
IF (#denylogin = 1)
BEGIN -- login is denied access
SET #tmpstr = #tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME( #name )
END
ELSE IF (#hasaccess = 0)
BEGIN -- login exists but does not have access
SET #tmpstr = #tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME( #name )
END
IF (#is_disabled = 1)
BEGIN -- login is disabled
SET #tmpstr = #tmpstr + '; ALTER LOGIN ' + QUOTENAME( #name ) + ' DISABLE'
END
SET #Prefix = '
EXEC master.dbo.sp_addsrvrolemember #loginame='''
SET #tmpstrRole=''
SELECT #tmpstrRole = #tmpstrRole
+ CASE WHEN sysadmin = 1 THEN #Prefix + [LoginName] + ''', #rolename=''sysadmin''' ELSE '' END
+ CASE WHEN securityadmin = 1 THEN #Prefix + [LoginName] + ''', #rolename=''securityadmin''' ELSE '' END
+ CASE WHEN serveradmin = 1 THEN #Prefix + [LoginName] + ''', #rolename=''serveradmin''' ELSE '' END
+ CASE WHEN setupadmin = 1 THEN #Prefix + [LoginName] + ''', #rolename=''setupadmin''' ELSE '' END
+ CASE WHEN processadmin = 1 THEN #Prefix + [LoginName] + ''', #rolename=''processadmin''' ELSE '' END
+ CASE WHEN diskadmin = 1 THEN #Prefix + [LoginName] + ''', #rolename=''diskadmin''' ELSE '' END
+ CASE WHEN dbcreator = 1 THEN #Prefix + [LoginName] + ''', #rolename=''dbcreator''' ELSE '' END
+ CASE WHEN bulkadmin = 1 THEN #Prefix + [LoginName] + ''', #rolename=''bulkadmin''' ELSE '' END
FROM (
SELECT CONVERT(VARCHAR(100),SUSER_SNAME(sid)) AS [LoginName],
sysadmin,
securityadmin,
serveradmin,
setupadmin,
processadmin,
diskadmin,
dbcreator,
bulkadmin
FROM sys.syslogins
WHERE ( sysadmin<>0
OR securityadmin<>0
OR serveradmin<>0
OR setupadmin <>0
OR processadmin <>0
OR diskadmin<>0
OR dbcreator<>0
OR bulkadmin<>0
)
AND name=#name
) L
PRINT #tmpstr
PRINT #tmpstrRole
PRINT 'END'
END
FETCH NEXT FROM login_curs INTO #SID_varbinary, #name, #type, #is_disabled, #defaultdb, #hasaccess, #denylogin, #defaultlanguage
END
CLOSE login_curs
DEALLOCATE login_curs
RETURN 0
END
GO
The number of users registered in the system as Unicode is large and it takes a long time to transfer them manually. So I need a solution that I can use to display Unicode users correctly.

I'll preface this by saying that it's much better to do this via some tool, rather than cobbling together something in T-SQL. You can use SSMS or similar.
Be that as it may:
The script you have posted has multiple issues.
The primary issue you were facing: use of varchar instead of nvarchar, means that other languages get clobbered.
Use of a cursor unnecessarily. This can be done in a single query using string aggregation.
The binary-to-hex procedure is complete overkill, as this can be done with a simple CONVERT. Even if it were necessary, it could have been done with an inline TVF.
Some missing QUOTENAME usages, to quote the names correctly.
It only adds fixed server roles, rather than any user-defined roles
It only adds CONNECT permissions, rather than any other permissions.
Usage of the deprecated sys.syslogins, which was replaced in 2005.
The following script is a full rewrite.
It returns all SQL logins and Windows logins based on AD users or AD groups (but not any other logins such as certificate based ones).
It adds all server-level permissions.
It adds all server roles, including user-defined.
CREATE OR ALTER PROCEDURE [dbo].[sp_help_revlogin]
#login_name sysname = NULL
AS
SELECT
'
/* sp_help_revlogin script
** Generated ' + CONVERT(nvarchar(30), GETDATE()) + ' on ' + QUOTENAME(##SERVERNAME) + ' */
GO
' + STRING_AGG(CONCAT(
'
IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N',
QUOTENAME(p.name, ''''),
')
BEGIN
CREATE LOGIN ',
QUOTENAME(p.name),
CASE WHEN p.type IN ('G', 'U') THEN ' FROM WINDOWS' END,
' WITH',
CASE WHEN p.type = 'S' THEN CONCAT(
' PASSWORD = ',
CONVERT(nvarchar(max), ISNULL(l.password_hash, 0x0), 1),
' HASHED, SID = ',
CONVERT(nvarchar(max), p.sid, 1),
CASE WHEN l.is_policy_checked = 0 THEN ', CHECK_POLICY = OFF' END,
CASE WHEN l.is_expiration_checked = 1 THEN ', CHECK_EXPIRATION = ON' END,
','
) END,
' DEFAULT_DATABASE = ',
QUOTENAME(p.default_database_name),
', DEFAULT_LANGUAGE = ',
QUOTENAME(p.default_language_name),
';
END;
GO
',
CASE WHEN p.is_disabled = 1 THEN
'ALTER LOGIN ' + QUOTENAME(p.name) + ' DISABLE;
GO
'
END,
r.roles + '
GO
',
sp.perms + '
GO
'
), N'
') WITHIN GROUP (ORDER BY p.name)
FROM sys.server_principals p
LEFT JOIN sys.sql_logins l ON l.principal_id = p.principal_id
CROSS APPLY (
SELECT STRING_AGG(
'EXEC sp_addsrvrolemember #loginame = ' + p2.name + ', #rolename = ' + QUOTENAME(r.name, '''') + ';'
, '
GO
'
)
FROM sys.server_role_members rm
JOIN sys.server_principals r ON r.principal_id = rm.role_principal_id
CROSS APPLY (VALUES( QUOTENAME(p.name, '''') )) p2(name) -- error 8124
WHERE rm.member_principal_id = p.principal_id
) r(roles)
CROSS APPLY (
SELECT STRING_AGG(CONCAT(
CASE WHEN sp.state = 'W' THEN N'GRANT' ELSE sp.state_desc END,
' ',
sp.permission_name COLLATE DATABASE_DEFAULT,
' TO ',
p2.name,
CASE WHEN sp.state = 'W' THEN N' WITH GRANT OPTION' END,
';'
), N'
GO
'
)
FROM sys.server_permissions sp
CROSS APPLY (VALUES( QUOTENAME(p.name, '''') )) p2(name) -- error 8124
WHERE sp.grantee_principal_id = p.principal_id
AND sp.class = 100
) sp(perms)
WHERE p.type IN ('S', 'G', 'U')
AND p.name <> 'sa'
AND (p.name = #login_name OR #login_name IS NULL);
GO
db<>fiddle

Related

How to compare data from multiple databases

I'm trying to compare some data from different multiple databases, as I have illustrate my current case, I have there databases, database 1 is the main, and time to time database 2 and database 3 are updated from database 1. I have some difficulties to get the final result which return the data from database 1 and two columns column show the availability in database 2 as Yes or No, and the same with second extra column that will indicate the data availability on the database 3 with Yes or NO.
SELECT *
FROM (
Select ID as db1_ID,
First_name as db1_First_name,
Last_name as db1_Last_name,
Email as db1_Email,
Password as db1_Password,
Request_Id as db1_Request_Id,
User_Id as db1_User_Id,
Request_name as db1_Request_name
from User
inner join User_request
on User_request.User_Id = user.ID
) AS DB1_VIEW
LEFT OUTER JOIN
(
Select ID as db2_ID,
First_name as db2_First_name,
Last_name as db2_Last_name,
Email as db2_Email,
Password as db2_Password,
Request_Id as db2_Request_Id,
User_Id as db2_User_Id,
Request_name as db2_Request_name
from User
inner join User_request
on User_request.User_Id = user.ID
) AS DB2_VIEW
ON db2_ID = db1_ID
LEFT OUTER JOIN
(
Select ID as db3_ID,
First_name as db3_First_name,
Last_name as db3_Last_name,
Email as db3_Email,
Password as db3_Password,
Request_Id as db3_Request_Id,
User_Id as db3_User_Id,
Request_name as db3_Request_name
from User
inner join User_request
on User_request.User_Id = user.ID
) AS DB3_VIEW
ON db3_ID = db1_ID
ID First_name Last_name Email Password Request_Id User_Id Request_name
1 Oliver Jake OJake#domain.com 123 1 1 Request1
2 Mathew Harry MHarry#domain.com 123 1 2 Request1
3 Jacob Reece JReece#domain.com 123 1 3
Request1
4 Charlie Damian CDamian#domain.com 123 1 4 Request1
Use this as your first select statement:
SELECT DB1_VIEW.*
,CASE WHEN DB2_VIEW.db2_ID IS NOT NULL THEN 'Y' ELSE 'N' END AS Available_db2
,CASE WHEN DB3_VIEW.db3_ID IS NOT NULL THEN 'Y' ELSE 'N' END AS Available_db3
You can remove all the details apart from the ID fields in the db2_view and db3_view subqueries.
You can use the below query before execute you should use replace [SourceDB] to your source database and [TargertDB] to your target database. Insert the table name into #mdtables to include for comparison.
USE [SourceDB]
IF Object_id('tempdb..#mdTables') IS NOT NULL
DROP TABLE #mdtables;
CREATE TABLE #mdtables
(
id INT IDENTITY(1, 1) NOT NULL,
schemaname NVARCHAR(128),
tablename NVARCHAR(128)
);
INSERT INTO #mdtables
(schemaname,
tablename)
VALUES ('dbo',
'user');
DECLARE #mdTableLim INT =0,
#mdTableRowId INT =0
SELECT #mdTableLim = Count(*)
FROM #mdtables;
SET #mdTableRowId = 1;
WHILE #mdTableRowId <= #mdTableLim
BEGIN
DECLARE #SDBName VARCHAR(50) = '[SourceDB]',
#TDBName VARCHAR(50) = '[TargertDB]',
#tableName VARCHAR(100) = ''
DECLARE #WhereF VARCHAR(max) ='',
#joincondition VARCHAR(max) ='',
#or VARCHAR(10) ='',
#select VARCHAR(max) = '',
#comma VARCHAR(1)='',
#query VARCHAR(max) ='',
#and VARCHAR(5)='',
#where1 VARCHAR(1000) ='',
#wOR VARCHAR(5)=''
SELECT #tableName = tablename
FROM #mdtables
WHERE id = #mdTableRowId;
SELECT #joincondition += Isnull(#and + ( CASE
WHEN cu.column_name IS NULL
THEN
NULL
ELSE ' src.[' + cu.column_name
+
'] = ' +
'trgt.['
+ c.column_name + ']'
END ), ''),
#WhereF += Isnull (#or + ( CASE
WHEN cu.column_name IS NOT NULL THEN
NULL
ELSE Isnull ( ' src.[' +
TC.column_name
+
'] ',
' isnull( src.[' +
C.column_name +
'],1) ' )
+ Isnull( '<> trgt.[' +
TC.column_name
+ ']',
' = isnull (src.['
+
C.column_name + '],1) ')
END ), ''),
#or = ( CASE
WHEN cu.column_name IS NOT NULL THEN ''
ELSE ' OR '
END ),
#and = ( CASE
WHEN cu.column_name IS NULL THEN ''
ELSE ' AND '
END ),
#select += #comma + ' src.[' + c.column_name + '] '
+ Isnull (' , trgt.[' + TC.column_name + ']', ''),
#comma = ',',
#where1 += Isnull(( #wOR + ( CASE
WHEN cu.column_name IS NULL THEN
NULL
ELSE ' trgt.[' + cu.column_name +
'] is null '
END ) ), ''),
#wOR = ( CASE
WHEN cu.column_name IS NULL THEN ''
ELSE ' OR '
END )
FROM information_schema.columns C
LEFT JOIN information_schema.key_column_usage CU
ON C.column_name = cu.column_name
AND constraint_name LIKE 'PK_%'
AND c.table_name = cu.table_name
LEFT JOIN [TargertDB].information_schema.columns TC
ON C.column_name = TC.column_name
AND c.table_name = TC.table_name
WHERE c.table_name = #tableName
--AND columnproperty(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 0
AND c.column_name NOT IN ( 'LST_CHG_TMS', 'LST_CHG_TMS',
'LST_CHG_USR_ID'
,
'LST_CHG_USR_ID' )
AND c.data_type NOT IN ( 'image' )
ORDER BY cu.column_name
SET #query = 'select ' + #select + ' from ' + #SDBName + '.dbo.'
+ #tableName + ' as src left join ' + #TDBName
+ '.dbo.' + #tableName + ' as trgt on '
+ #joincondition + ' where (' + #where1 + ')'
+ Isnull ('and '+ NULLIF (#WhereF, ''), '')
DECLARE #qu1 VARCHAR(max) =
' declare #cnt int =0 select #cnt =count (1) from '
+ #SDBName + '.dbo.' + #tableName
+ ' as src left join ' + #TDBName + '.dbo.'
+ #tableName + ' as trgt on ' + #joincondition
+ ' where (' + #where1 + ')'
+ Isnull (' OR '+ NULLIF (#WhereF, ''), '')
+ ' if (#cnt>0) begin select '''
+ #tableName + ''' as [ ],#cnt ' +-- #query + ' end '
BEGIN try
EXECUTE ( #qu1)
END try
BEGIN catch
PRINT #qu1;
END catch
SET #mdTableRowId = #mdTableRowId + 1
END
This might not need CTE's or sub-queries.
A few joins might do it.
SELECT
Usr1.ID AS db1_User_Id,
Usr1.First_name AS db1_First_name,
Usr1.Last_name AS db1_Last_name,
Usr1.Email AS db1_Email,
Usr1.Password AS db1_Password,
MAX(UsrReq1.Request_Id) AS db1_Request_Id,
MAX(UsrReq1.Request_name) AS db1_Request_name,
CASE WHEN COUNT(UsrReq2.User_Id) > 0 THEN 'Y' ELSE 'N' END AS Available_Db2,
CASE WHEN COUNT(UsrReq3.User_Id) > 0 THEN 'Y' ELSE 'N' END AS Available_Db3
FROM [Database1].[User] AS Usr1
LEFT JOIN [Database1].[User_request] AS UsrReq1 ON UsrReq1.User_Id = Usr1.ID
LEFT JOIN [Database2].[User] AS Usr2 ON Usr2.ID = Usr1.ID
LEFT JOIN [Database2].[User_request] AS UsrReq2 ON UsrReq2.User_Id = Usr2.ID
LEFT JOIN [Database3].[User] AS Usr3 ON Usr3.ID = Usr1.ID
LEFT JOIN [Database3].[User_request] AS UsrReq3 ON UsrReq3.User_Id = Usr3.ID
GROUP BY
Usr1.ID,
Usr1.First_name,
Usr1.Last_name,
Usr1.Email,
Usr1.Password;

Generate insert statement which has same columns of a table in SQL Server

I have wide table with 100 columns.
I need a SP which takes 100 parameters and then does the insert.
I know how to do this manually. But having the table definition and knowing that SP parameters will have exact same name of the table columns, can you think of a better/faster way to generate this stored procedure?
I use SQL to write it for you. Check it out and let me know if it needs any tweaks or if you have any questions.
IF OBJECT_ID('yourTable') IS NOT NULL
DROP TABLE yourTable;
CREATE TABLE yourTable
(
col1 INT,
col2 VARCHAR(100),
col3 NUMERIC(18,2)
)
DECLARE #InputParams VARCHAR(MAX),
#InsertColumns VARCHAR(MAX),
#InsertParams VARCHAR(MAX);
WITH CTE_columns
AS
(
SELECT COLUMN_NAME,
UPPER(DATA_TYPE) data_type,
'(' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10)) + ')' max_length,
CASE
WHEN DATA_TYPE IN ('Numeric','Decimal') THEN CONCAT('(',NUMERIC_PRECISION,',',NUMERIC_SCALE,')')
END prec_scale
--#InsertColumns = COALESCE(#InsertColumns + ',','') + COLUMN_NAME,
FROM INFORMATION_SCHEMA.COLUMNS A
WHERE TABLE_NAME = 'yourTable'
)
SELECT #InputParams = COALESCE(#InputParams + ',','') + CONCAT('#',column_name,' ',data_type,max_length,prec_scale),
#InsertColumns = COALESCE(#InsertColumns + ',','') + COLUMN_NAME,
#InsertParams = COALESCE(#InsertParams + ',','') + '#'+ COLUMN_NAME
FROM CTE_columns
SELECT
'CREATE PROCEDURE dbo.yourProc ' + #InputParams +
' AS
INSERT INTO yourTable(' + #InsertColumns + ')
VALUES (' + #InsertParams + ');
GO'
Results(Formatting isn't great, but it works):
CREATE PROCEDURE dbo.yourProc #col1 INT,#col2 VARCHAR(100),#col3 NUMERIC(18,2) AS
INSERT INTO yourTable(col1,col2,col3)
VALUES (#col1,#col2,#col3);
GO
For this type of scenario, I like to leverage table types.
First, create the table type:
CREATE TYPE [YourType] AS TABLE (columns...)
In C#, populate a DataTable "template" by running this query (using a DataAdapter):
DECLARE #tt AS [YourType]
SELECT * FROM #tt
Then add a row to the table and cycle through the columns to add the necessary values.
Then pass the datatable as a parameter into your procedure:
CREATE PROCEDURE [YourProc]
#tt [YourType] READONLY
AS
BEGIN
--do stuff
END
An added benefit of this approach is that you can pass multiple records into the procedure with a single call.
You can probably quickly modify this for your needs:
Declare #tableName nvarchar(100) = 'aspnet_Membership'
Declare #objectName nvarchar(100) = 'Membership'
DECLARE #newLineChar AS CHAR(2) = CHAR(13) + CHAR(10)
Declare #columnName nvarchar(50)
Declare #dataType nvarchar(50)
Declare #characterLength int
Declare #isNullableString varchar(3)
Declare #ordinalPosition int
Declare #firstSortOrder smallint
Declare #variableTableCreateCode nvarchar(max) = ''
Declare #variableTableDataTypeCode nvarchar(128)
Declare #variableTableSelectCode001 nvarchar(max) = ''
Declare #variableTableSelectCode002 nvarchar(max) = ''
Declare #varableTableInsertIntoCode nvarchar(max) = ''
Declare tbl_ColumnCursor Cursor For
Select colSchema.COLUMN_NAME, colSchema.DATA_TYPE, colSchema.CHARACTER_MAXIMUM_LENGTH, colSchema.IS_NULLABLE , colSchema.ORDINAL_POSITION , FirstSortOrder = CASE WHEN PrimaryKeyDerived.CONSTRAINT_TYPE IS NOT NULL THEN 1 ELSE 2 END
from INFORMATION_SCHEMA.COLUMNS colSchema
LEFT JOIN
(
SELECT
INFORMATION_SCHEMA.COLUMNS.TABLE_NAME
,INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME
,INFORMATION_SCHEMA.TABLE_CONSTRAINTS.CONSTRAINT_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
LEFT OUTER JOIN information_schema.KEY_COLUMN_USAGE
ON
INFORMATION_SCHEMA.COLUMNS.TABLE_NAME=information_schema.KEY_COLUMN_USAGE.TABLE_NAME
AND INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME=information_schema.KEY_COLUMN_USAGE.COLUMN_NAME
LEFT OUTER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS
ON
information_schema.KEY_COLUMN_USAGE.TABLE_NAME=information_schema.TABLE_CONSTRAINTS.TABLE_NAME
AND information_schema.KEY_COLUMN_USAGE.CONSTRAINT_NAME=information_schema.TABLE_CONSTRAINTS.CONSTRAINT_NAME
WHERE CONSTRAINT_TYPE = 'PRIMARY KEY'
) as PrimaryKeyDerived on colSchema.TABLE_NAME = PrimaryKeyDerived.TABLE_NAME and colSchema.COLUMN_NAME = PrimaryKeyDerived.COLUMN_NAME
where colSchema.TABLE_NAME = #tableName
Order By FirstSortOrder , colSchema.ORDINAL_POSITION
Set #variableTableCreateCode = 'declare #' + #objectName + 'Holder table ( ' + #objectName + 'SurrogateKey int , '
Set #variableTableSelectCode001 = 'INSERT INTO #' + #objectName + 'Holder ( '
Set #variableTableSelectCode002 = ' SELECT '
Set #varableTableInsertIntoCode = 'INSERT INTO '+#tableName+' ( '
Open tbl_ColumnCursor
Fetch Next From tbl_ColumnCursor Into #ColumnName, #dataType, #characterLength, #isNullableString, #ordinalPosition , #firstSortOrder
While ##FETCH_STATUS = 0
Begin
print '/#ColumnName/'
print #ColumnName
print ''
print '/##dataType/'
print #dataType
print ''
print '/###isNullableString/'
print #isNullableString
print ''
Select #variableTableDataTypeCode =
case
when #dataType like '%char%' and #characterLength = -1 Then
#dataType+'(max)'
when #dataType like '%char%' and #characterLength = 1 Then
#dataType+'('+convert(varchar(32), #characterLength)+')'
When (#dataType like '%char%') Or (#dataType = 'xml') Or (#dataType like '%text%') Then
#dataType+'('+convert(varchar(32), #characterLength)+')'
Else #dataType
End
Set #variableTableCreateCode = #variableTableCreateCode + ' [' + #columnName + '] ' + #variableTableDataTypeCode +','
select #variableTableSelectCode002 = #variableTableSelectCode002 + 'T.MyEntity.value(''#' + #columnName + ''', '''+#variableTableDataTypeCode+''') AS ' + #columnName + ','
Fetch Next From tbl_ColumnCursor Into #ColumnName, #dataType, #characterLength , #isNullableString , #ordinalPosition , #firstSortOrder
End
Close tbl_ColumnCursor
Deallocate tbl_ColumnCursor
if(DATALENGTH(#variableTableCreateCode) > 0)
BEGIN
select #variableTableCreateCode = LEFT(#variableTableCreateCode, LEN(#variableTableCreateCode) -1)
END
if(DATALENGTH(#variableTableSelectCode002) > 0)
BEGIN
select #variableTableSelectCode002 = LEFT(#variableTableSelectCode002, LEN(#variableTableSelectCode002) -1)
END
Set #variableTableCreateCode = #variableTableCreateCode + #newLineChar + ' ) '
select #variableTableCreateCode
select #variableTableSelectCode001 = #variableTableSelectCode001 + ( SELECT
MyColumns = STUFF
(
(
SELECT ', [' + r.COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS AS r
WHERE r.TABLE_NAME = tabs.TABLE_NAME
ORDER BY r.ORDINAL_POSITION
FOR XML PATH(''), TYPE
).value('.[1]','nvarchar(max)'),
1,1,''
)
FROM INFORMATION_SCHEMA.TABLES AS tabs
/* Optional WHERE Clause */
WHERE
tabs.TABLE_NAME = #tableName
)
select #variableTableSelectCode001 = #variableTableSelectCode001 + ' ) '+#newLineChar
select #variableTableSelectCode002 = #variableTableSelectCode002 + ' FROM #xmldata.nodes(''RootElement/' + #tableName + 'Elements/' + #tableName + 'Element'') AS T(MyEntity); '
select #variableTableSelectCode001 + #variableTableSelectCode002
select #varableTableInsertIntoCode = #varableTableInsertIntoCode + ( SELECT
MyColumns = STUFF
(
(
SELECT ', [' + r.COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS AS r
WHERE r.TABLE_NAME = tabs.TABLE_NAME
ORDER BY r.ORDINAL_POSITION
FOR XML PATH(''), TYPE
).value('.[1]','nvarchar(max)'),
1,1,''
)
FROM INFORMATION_SCHEMA.TABLES AS tabs
WHERE
tabs.TABLE_NAME = #tableName
)
select #varableTableInsertIntoCode = #varableTableInsertIntoCode + ' ) SELECT '
select #varableTableInsertIntoCode = #varableTableInsertIntoCode + ( SELECT
MyColumns = STUFF
(
(
SELECT ', holder.[' + r.COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS AS r
WHERE r.TABLE_NAME = tabs.TABLE_NAME
ORDER BY r.ORDINAL_POSITION
FOR XML PATH(''), TYPE
).value('.[1]','nvarchar(max)'),
1,1,''
)
FROM INFORMATION_SCHEMA.TABLES AS tabs
WHERE
tabs.TABLE_NAME = #tableName
)
select #varableTableInsertIntoCode = #varableTableInsertIntoCode + ' from #' + #objectName + 'Holder holder where not exists ( select null from ' +#tableName+ ' realTable where realTable.Name = holder.Name and realTable.IsDeleted = holder.IsDeleted ) '
select #varableTableInsertIntoCode
SELECT
tabs.TABLE_NAME,
MyColumns = STUFF
(
(
SELECT ', alias.[' + r.COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS AS r
WHERE r.TABLE_NAME = tabs.TABLE_NAME
ORDER BY r.ORDINAL_POSITION
FOR XML PATH(''), TYPE
).value('.[1]','nvarchar(max)'),
1,1,''
)
FROM INFORMATION_SCHEMA.TABLES AS tabs
/* Optional WHERE Clause */
WHERE
tabs.TABLE_NAME = #tableName
ORDER BY tabs.TABLE_NAME;

How can I use single quote inside sql command? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I escape a single quote in sqlserver?
I got a script below that drop everything on the database from this link. It does error when I execute on this line.
SET #statement = '
IF(#type = 'F') or (#type = 'C') or (#type = 'D') or (#type='F') or (#type='K')
The reason is because the single quote. I want to know how can I fix this error?
/*** drop (pretty much) everything before rebuilding the database ***/
DECLARE
OBJECTS CURSOR FOR SELECT
so.name,
so.type,
so.type_desc,
p.name AS parentName
FROM
sys.objects AS so
LEFT JOIN sys.objects AS p ON so.parent_object_id = p.object_id
WHERE
so.schema_id = 1
ORDER BY
CASE
WHEN so.type = 'F' THEN
0
WHEN so.type = 'TR' THEN
1
WHEN so.type = 'U' THEN
2
WHEN so.type = 'F' THEN
3
ELSE
4
END OPEN OBJECTS DECLARE
#name AS nvarchar (MAX) DECLARE
#type AS nvarchar (2) DECLARE
#type_desc AS nvarchar DECLARE
#parentName AS nvarchar (MAX) DECLARE
#statement AS nvarchar (MAX) FETCH NEXT
FROM
OBJECTS INTO #name,
#type,
#type_desc,
#parentName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #statement = ' IF(#type = ' F ')
BEGIN
PRINT ' DROPING FK : ' + #name + ' OF type ' + #type + ' (' + #type_desc + ') '
SET #statement = ' ALTER TABLE ' + #parentName + ' DROP CONSTRAINT ' + #name
EXECUTE(#statement)
END
ELSE IF (#type = ' TR ')
BEGIN
PRINT ' DROPING TRIGGER : ' + #name + ' OF type ' + #type + ' (' + #type_desc + ') '
SET #statement = ' DROP TRIGGER ' + #name
EXECUTE(#statement)
END
ELSE IF (#type = ' U ')
BEGIN
PRINT ' DROPING TABLE : ' + #name + ' OF type ' + #type + ' (' + #type_desc + ') '
SET #statement = ' DROP TABLE ' + #name
EXECUTE(#statement)
END
ELSE IF (#type = ' FN ')
BEGIN
PRINT ' DROPING FUNCTION : ' + #name + ' OF type ' + #type + ' (' + #type_desc + ') '
SET #statement = ' DROP FUNCTION ' + #name
EXECUTE(#statement)
END
ELSE
PRINT ' Didn 't drop object ' + #name + ' of type ' + #type + ' (' + #type_desc + ')' FETCH NEXT
FROM
OBJECTS INTO #name,
#type,
#type_desc,
#parentName
END CLOSE OBJECTS DEALLOCATE OBJECTS
if you want to use single quote inside a prepared statement, escape it with another single quote, example,
SET #statement = 'world''s view';
SET #statement2 = 'world''s view';
from your example above
SET #statement = '
IF(#type = ''F'') or (#type = ''C'') or
(#type = ''D'') or (#type=''F'') or
(#type=''K'')'
-- the strings are all red.
Single quote is used to represent a string literal in SQL.
If you need to explicitly insert a single quote , you should use double single quotes ('')
It should be like this:
SET #statement = 'IF(#type = ''F'') or (#type = ''C'') or (#type = ''D'') or (#type=''F'') or (#type=''K'')'
Raj

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

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;

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