How to collate sysname in sql server - sql-server

I am trying to run a query to drop server logins coming from windows and recreate them. I run this query to drop logins and it works fine
USE master
SELECT
'IF EXISTS (SELECT * FROM sys.server_principals WHERE name = ' +
'''' + [name] + '''' + ')' +
' BEGIN DROP LOGIN ' + QUOTENAME([name]) + ' END; ' +
'CREATE LOGIN ' + QUOTENAME([name]) + ' FROM WINDOWS; '
FROM sys.server_principals
WHERE type_desc IN ('WINDOWS_GROUP' , 'WINDOWS_LOGIN')
But the following query
USE master
SELECT
'ALTER LOGIN ' + QUOTENAME([name]) + ' WITH DEFAULT_DATABASE = ' +
spr.default_database_name
+ 'GRANT ' + spe.permission_name + ' to ' + QUOTENAME([name])
+ CASE WHEN spr.default_database_name IS NOT NULL
THEN ' WITH DEFAULT DATABASE = ' + spr.default_database_name
ELSE
''
END
FROM sys.server_principals spr INNER JOIN
sys.server_permissions spe ON spr.principal_id = spe.grantee_principal_id
WHERE spr.type_desc IN ('WINDOWS_GROUP', 'WINDOWS_LOGIN')
is returning this error
Cannot resolve collation conflict for column 1 in SELECT statement.
So when I investigated the issue it is coming from the sysname column default_database_name
The query ran when I used spr.default_database_name COLLATE Latin1_General_CI_AI
My three parts question is: Did I get the problem because of a row or because the column data type is sysname?. Is COLLATE Latin1_General_CI_AI the correct syntax for this scenario?.
when I run this query
SELECT name, description
FROM sys.fn_helpcollations()
The results are showing many collations for different languages. So how did I get this error for a completely English database.
Edit:
I ran these two queries to check the collation property for database and server
SELECT DATABASEPROPERTYEX(N'master', N'Collation')
SELECT SERVERPROPERTY(N'Collation')
and the result in both of them was
SQL_Latin1_General_CP1_CI_AS

It may help to bring the default collate for database and server instance, changing the query as shown below:
SELECT
'ALTER LOGIN ' + QUOTENAME([name]) + ' WITH DEFAULT_DATABASE = ' +
spr.default_database_name COLLATE database_default
+ 'GRANT ' + spe.permission_name + ' to ' + QUOTENAME([name])
+ CASE WHEN spr.default_database_name IS NOT NULL
THEN ' WITH DEFAULT DATABASE = ' + spr.default_database_name COLLATE database_default
ELSE
''
END
FROM sys.server_principals spr INNER JOIN
sys.server_permissions spe ON spr.principal_id = spe.grantee_principal_id
WHERE spr.type_desc IN ('WINDOWS_GROUP', 'WINDOWS_LOGIN')

It looks like someone at MS has fixed the collation on sys.server_permissions.state_desc as Latin1_General_CI_AS_KS_WS
I've tried on a number of instances (SQL 2016) and found the same problem. Putting "collate database_default" works.

Solution for your question: Specify collation for two columns where you CONCAT
USE master
GO
SELECT
'ALTER LOGIN ' + QUOTENAME([name]) COLLATE SQL_Latin1_General_CP1_CI_AS +
' WITH DEFAULT_DATABASE = ' +
spr.default_database_name COLLATE SQL_Latin1_General_CP1_CI_AS
+ 'GRANT ' + spe.permission_name + ' to ' + QUOTENAME([name])
+ CASE WHEN spr.default_database_name IS NOT NULL
THEN ' WITH DEFAULT DATABASE = ' + spr.default_database_name
ELSE
''
END
FROM sys.server_principals spr INNER JOIN
sys.server_permissions spe ON spr.principal_id = spe.grantee_principal_id
WHERE spr.type_desc IN ('WINDOWS_GROUP', 'WINDOWS_LOGIN')

Related

How can I revoke all the permissions a role has in sql server?

I have a role, it has some select perms on various tables. I would like to remove all the select permissions that the role has across all tables. Eg,
revoke all from my_role_name;
But this doesn't seem to work. How can I do this?
I just had the need to do this.
You can use something along the lines of the below
DECLARE #RevokeScript NVARCHAR(MAX);
DECLARE #PrincipalName SYSNAME = 'my_role_name'
SELECT #RevokeScript = STRING_AGG(CAST('REVOKE ' + permission_name
+ CASE class_desc
WHEN 'OBJECT_OR_COLUMN' THEN ' ON ' + QUOTENAME(OBJECT_SCHEMA_NAME(major_id)) + '.' + QUOTENAME(OBJECT_NAME(major_id))
WHEN 'SCHEMA' THEN ' ON SCHEMA::' + SCHEMA_NAME(major_id)
WHEN 'DATABASE' THEN ''
END
+ ' TO ' + QUOTENAME(#PrincipalName) COLLATE SQL_Latin1_General_CP1_CI_AS AS NVARCHAR(MAX)), ';')
FROM sys.database_permissions AS pe
WHERE pe.grantee_principal_id = DATABASE_PRINCIPAL_ID (#PrincipalName);
PRINT #RevokeScript
EXEC (#RevokeScript)

Duplicate Sql Server Table Definition Using Generated Create Statement

I need to copy several tables definition from one database to other database. I have found a way to generate create statement for every individual table that I need. However I cannot execute every 'create statement' that is generated. I expect that following script will recreate all table that match criteria in NEWDB database. However it didn't. It always recreate only one table instead of all tables that I need. I am not sql expert and I believe I am missing something here.
declare #sql varchar(8000)
select #sql='create table [NEWDB].[' + OBJECT_SCHEMA_NAME(so.id) + '].[' + so.name + '] (' + o.list + ')' + CASE WHEN tc.Constraint_Name IS NULL THEN '' ELSE 'ALTER TABLE ' + so.Name + ' ADD CONSTRAINT ' + tc.Constraint_Name + ' PRIMARY KEY ' + ' (' + LEFT(j.List, Len(j.List)-1) + ')' END
from sysobjects so
cross apply
(SELECT
' ['+column_name+'] ' +
data_type + case data_type
when 'sql_variant' then ''
when 'text' then ''
when 'ntext' then ''
when 'xml' then ''
when 'decimal' then '(' + cast(numeric_precision as varchar) + ', ' + cast(numeric_scale as varchar) + ')'
else coalesce('('+case when character_maximum_length = -1 then 'MAX' else cast(character_maximum_length as varchar) end +')','') end + ' ' +
case when exists (
select id from syscolumns
where object_name(id)=so.name
and name=column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(so.name) as varchar) + ',' +
cast(ident_incr(so.name) as varchar) + ')'
else ''
end + ' ' +
(case when IS_NULLABLE = 'No' then 'NOT ' else '' end ) + 'NULL ' +
case when information_schema.columns.COLUMN_DEFAULT IS NOT NULL THEN 'DEFAULT '+ information_schema.columns.COLUMN_DEFAULT ELSE '' END + ', '
from information_schema.columns where table_name = so.name
order by ordinal_position
FOR XML PATH('')) o (list)
left join
information_schema.table_constraints tc
on tc.Table_name = so.Name
AND tc.Constraint_Type = 'PRIMARY KEY'
cross apply
(select '[' + Column_Name + '], '
FROM information_schema.key_column_usage kcu
WHERE kcu.Constraint_Name = tc.Constraint_Name
ORDER BY
ORDINAL_POSITION
FOR XML PATH('')) j (list)
where xtype = 'U'
AND name NOT IN ('dtproperties')
-- criteria
AND name like 'AUD_%'
order by OBJECT_SCHEMA_NAME(so.id),so.name
-- this execute one table only regardless how many table in actual result set
exec (#sql)
SSMS supports scripting all schema (and optionally data as INSERTS) in 2k8:
Right click on a database and select Tasks->Generate Scripts...
Select All tables OR Pick the tables you require, Click Next.
....
See answer here: to "Script all data from SQL Server database"
That's a lot easier than trying to roll your own.

SQL Server equivalent to DBMS_METADATA.GET_DDL

I was wondering if there is an equivalent in SQL Server 2008 to Oracle's DBMS_METADATA.GET_DDL Function? You can pass this function a table name and it will return the ddl for that table so that you can use it to build a script for a schema.
I know I can go into SSMS and use that, but I would prefer to have a t-sql script that would generate the ddl for me.
Thanks,
S
I using this query for generate query but this work for 1 table :
declare #vsSQL varchar(8000)
declare #vsTableName varchar(50)
select #vsTableName = 'Customers'
select #vsSQL = 'CREATE TABLE ' + #vsTableName + char(10) + '(' + char(10)
select #vsSQL = #vsSQL + ' ' + sc.Name + ' ' +
st.Name +
case when st.Name in ('varchar','varchar','char','nchar') then '(' + cast(sc.Length as varchar) + ') ' else ' ' end +
case when sc.IsNullable = 1 then 'NULL' else 'NOT NULL' end + ',' + char(10)
from sysobjects so
join syscolumns sc on sc.id = so.id
join systypes st on st.xusertype = sc.xusertype
where so.name = #vsTableName
order by
sc.ColID
select substring(#vsSQL,1,len(#vsSQL) - 2) + char(10) + ')'
If you are looking for a TSQL solution, it is quite verbose, as [this example]¹ shows.
A shorter alternative would be using the SMO library (example)
¹ Link for this example deleted. The way Internet Archive Wayback Machine displayed an error saying that they could not display the content. And following the link to the original went someplace malicious. (Grave errors, instruction to call some number, etc.)

SQL Server 2005 Users & Roles

All,
I am trying to script out a database in SQL Server Management Studio 2005.
In my database users belong to different roles.
Unfortunately I can't find how to script out the relationship between users and roles.
Thanks,
M
The role membership is stored in sys.database_role_members:
select u.name as UserName,
r.Name as RoleName
from sys.database_principals u
join sys.database_role_members m on u.principal_id = m.member_principal_id
join sys.database_principals r on m.role_principal_id = p.principal_id;
User may still get additional privileges by their server fixed roles membership, which is stored in sys.server_role_memebers and needs to be joined with sys.server_principals.
Here's some code we use to first check if a user is already mapped to a role, and if not, to do the mapping. You should be able to take the TSQL at the top that uses database_prinicipals and database_role_members and use that to extract out the relationships you have in your databaase.
SELECT #sql = ' IF EXISTS (SELECT * FROM ' + #DatabaseName + '.sys.database_principals a
JOIN ' + #DatabaseName + '.sys.database_role_members b ON a.principal_id = b.role_principal_id
JOIN ' + #DatabaseName + '.sys.database_principals c ON b.member_principal_id = c.principal_id
WHERE a.Type = ''R'' AND a.Name = ''' + #CurrentDbRole + '''
AND c.type IN ( ''U'', ''S'') AND c.name = ''' + #MappedUser + ''')'
+ ' BEGIN
PRINT '''';
PRINT N''The [' + #MappedUser + '] user is already a member of the ['
+ #CurrentDbRole + '] role in the [' + #DatabaseName + '] database. Skipping Role Member creation.'';
PRINT '''';
END
ELSE
BEGIN
PRINT '''';
PRINT N''Adding the [' + #MappedUser + '] database user as member of the [' + #CurrentDbRole
+ '] role in the [' + #DatabaseName + '] database... '';
PRINT '''';
USE ' + #DatabaseName +';
EXECUTE sp_addrolemember [' + #CurrentDbRole + '], [' + #MappedUser + '];
PRINT '''';
PRINT ''Completed adding the user to the role.'';
PRINT '''';
END; ';

SQL Server - script to update database columns from varchar to nvarchar if not already nvarchar

I am in a situation where I must update an existing database structure from varchar to nvarchar using a script. Since this script is run everytime a configuration application is run, I would rather determine if a column has already been changed to nvarchar and not perform an alter on the table. The databases which I must support are SQL Server 2000, 2005 and 2008.
You can run the following script which will give you a set of ALTER commands:
SELECT 'ALTER TABLE ' + isnull(schema_name(syo.id), 'dbo') + '.' + syo.name
+ ' ALTER COLUMN ' + syc.name + ' NVARCHAR(' + case syc.length when -1 then 'MAX'
ELSE convert(nvarchar(10),syc.length) end + ');'
FROM sysobjects syo
JOIN syscolumns syc ON
syc.id = syo.id
JOIN systypes syt ON
syt.xtype = syc.xtype
WHERE
syt.name = 'varchar'
and syo.xtype='U'
There are, however, a couple of quick caveats for you.
This will only do tables. You'll want to scan all of your sprocs and functions to make sure they are changed to NVARCHAR as well.
If you have a VARCHAR > 4000 you will need to modify it to be NVARCHAR(MAX)
But those should be easily doable with this template.
If you want this to run automagically you can set it in a WHILE clause.
The issue with Josef's answer is that it would change NOT NULL fields to NULL after executing the queries. The following manipulation fixes it:
SELECT cmd = 'alter table [' + c.table_schema + '].[' + c.table_name
+ '] alter column [' + c.column_name + '] nvarchar('
+CASE WHEN CHARACTER_MAXIMUM_LENGTH<=4000
THEN CAST(CHARACTER_MAXIMUM_LENGTH as varchar(10)) ELSE 'max' END+')'
+ CASE WHEN IS_NULLABLE='NO' THEN ' NOT NULL' ELSE '' END,*
FROM information_schema.columns c
WHERE c.data_type='varchar'
ORDER BY CHARACTER_MAXIMUM_LENGTH desc
Credits to Igor's answer
The following query should get you what you need:
IF EXISTS
(SELECT *
FROM sysobjects syo
JOIN syscolumns syc ON
syc.id = syo.id
JOIN systypes syt ON
syt.xtype = syc.xtype
WHERE
syt.name = 'nvarchar' AND
syo.name = 'MY TABLE NAME' AND
syc.name = 'MY COLUMN NAME')
BEGIN
ALTER ...
END
Fixed the space issue and added schema
SELECT 'ALTER TABLE [' + isnull(schema_name(syo.object_id), sysc.name) + '].[' + syo.name
+ '] ALTER COLUMN ' + syc.name + ' NVARCHAR(' + case syc.max_length when -1 then 'MAX'
ELSE convert(nvarchar(10),syc.max_length) end + ');'
FROM sys.objects syo
JOIN sys.columns syc ON
syc.object_id= syo.object_id
JOIN sys.types syt ON
syt.system_type_id = syc.system_type_id
JOIN sys.schemas sysc ON
syo.schema_id=sysc.schema_id
WHERE
syt.name = 'varchar'
and syo.type='U'
Further updated to fix MAX being replaced with -1.
SELECT cmd = 'ALTER TABLE [' + c.table_schema + '].[' + c.table_name
+ '] ALTER COLUMN [' + c.column_name + '] NVARCHAR('
+CASE WHEN CHARACTER_MAXIMUM_LENGTH<=4000 THEN
CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN
'MAX' ELSE CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10)) END ELSE 'MAX' END+')'
+ CASE WHEN IS_NULLABLE='NO' THEN ' NOT NULL' ELSE '' END,*
FROM information_schema.columns c
WHERE c.data_type='VARCHAR'
ORDER BY CHARACTER_MAXIMUM_LENGTH DESC
Credit to Nezam's Answer
And another one to manage default values:
SELECT cmd =
CASE WHEN name IS NOT NULL THEN
'ALTER TABLE ' + c.table_name + ' DROP CONSTRAINT ' + d.name + '; ' +
'ALTER TABLE [' + c.table_schema + '].[' + c.table_name + '] ALTER COLUMN [' + c.column_name + '] ' +
'NVARCHAR(' +
CASE WHEN CHARACTER_MAXIMUM_LENGTH <= 4000 THEN
CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN
'MAX'
ELSE
CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10))
END
ELSE
'MAX'
END
+ ')' +
CASE WHEN IS_NULLABLE='NO' THEN ' NOT NULL' ELSE '' END + '; ' +
'ALTER TABLE '+ c.table_name + ' ADD CONSTRAINT ' + d.name +' DEFAULT '+ c.column_default + ' FOR ' + c.column_name + ';'
ELSE
'ALTER TABLE [' + c.table_schema + '].[' + c.table_name + '] ALTER COLUMN [' + c.column_name + '] ' +
'NVARCHAR(' +
CASE WHEN CHARACTER_MAXIMUM_LENGTH<=4000 THEN
CASE WHEN CHARACTER_MAXIMUM_LENGTH = -1 THEN
'MAX'
ELSE
CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10))
END
ELSE
'MAX'
END
+ ')' +
CASE WHEN IS_NULLABLE='NO' THEN ' NOT NULL' ELSE '' END
END,d.name, c.*
FROM information_schema.columns c
LEFT OUTER JOIN sys.default_constraints d ON d.parent_object_id = object_id(c.table_name)
AND d.parent_column_id = columnproperty(object_id(c.table_name), c.column_name, 'ColumnId')
WHERE c.data_type='VARCHAR'
ORDER BY CHARACTER_MAXIMUM_LENGTH DESC

Resources