Query all Databases a User has Access to as Administrator - sql-server

I'm looking to query all databases mapped to a user, similar to Security > Logins > Properties > User Mapping.
This may be done in SQL 2005 if possible
For example, something similar to:
SELECT name
FROM sys.databases
WHERE HAS_DBACCESS(name) = 1
But perform the query from an administrative user, as opposed to running the above query as the user itself.
How would something like this be performed?
Thank you.

Well this might be a start, probably not the nice output you'd hope for (and it produces two resultsets):
EXEC sp_helplogins N'floob';
But it does work on SQL Server 2000. If you want to try and replicate some of the functionality in the procedure, you can see how it's checking for permissions, basically a cursor through every database. On SQL Server 2000:
EXEC sp_helptext N'sp_helplogins';
On 2005+ I much prefer the output of OBJECT_DEFINITION():
SELECT OBJECT_DEFINITION(OBJECT_ID(N'sys.sp_helplogins'));
So you could write your own cursor based on similar logic, and make the output prettier...
Here is a quick (and not complete) example, doesn't cover much but an idea to get started if the above is not sufficient:
DECLARE #login NVARCHAR(255);
SET #login = N'foobarblat';
-- above would be an input parameter to a procedure, I presume
CREATE TABLE #dbs(name SYSNAME);
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'INSERT #dbs SELECT ''' + name + ''' FROM '
+ QUOTENAME(name) + '.sys.database_principals AS u
INNER JOIN sys.server_principals AS l
ON u.sid = l.sid
WHERE l.name = #login;'
FROM sys.databases
WHERE state_desc = 'ONLINE'
AND user_access_desc = 'MULTI_USER';
EXEC sp_executesql #sql, N'#login SYSNAME', #login;
SELECT name FROM #dbs;
DROP TABLE #dbs;
As I said, this is not complete. Won't know if the user has been denied connect, is member of deny reader/writer roles, won't show the alias if the user name in the db doesn't match the login, etc. You can dig into more details from sp_helplogins depending on what you want to show.

The EXECUTE AS functionality was added in the 2005 release, so I don't think you can run that in 2000. You could probably mimick it by putting the relevant code in a job and setting the job owner to an admin user, but it would process with the job not inline.

Related

How to fail SQL query when using partial three part name like dbname..tablename

I need a way to fail my query when there is partial three part name is involved like
select * from dbName..TableName in SQL Server
Thank you.
This answer is a little in jest, but it will give you the desired effect. But, like I said in my comment. This feels like an XY Problem.
Firstly, create a new schema in your database. I am intentionally giving is a silly name:
USE YourDatabase;
GO
CREATE SCHEMA Always_define_your_schema;
GO
Then change all the USERs in the database to have their default schema be this new ("stupid") schema:
ALTER USER YourUser WITH DEFAULT_SCHEMA = Always_define_your_schema;
If you want to do this to every USER with a script, you could do something like this:
DECLARE #SQL nvarchar(MAX);
SET #SQL = STUFF((SELECT NCHAR(13) + NCHAR(10) +
N'ALTER USER ' + QUOTENAME(U.[name]) + N' WITH DEFAULT_SCHEMA = Always_define_your_schema;'
FROM sys.database_principals U
WHERE U.type IN ('U','S')
AND U.authentication_type > 0
AND U.[name] != 'dbo' --You will need to likely include more here
FOR XML PATH(''),TYPE).value('.','nvarchar(MAX)'),1,2,N'');
PRINT #SQL; --Your debugging friend
--EXEC sys.sp_executesql #SQL; --Uncomment to run
But, like I said, this is more of a "jest" answer; if you really implement this then expect to break things...

How to get database name automatically into script to use in stored procedure execution?

I need to TRIM databases as per requirement. So, I'm using below script and giving database names manually. All I need is to automate the script to get database names automatically. Can anyone please suggest how to get the database name automatically.
Use [Sales_backup_2015_05_31_230001_7137975]
Exec [spMaint_TrimTestDB] 1
Go
for Eg:
instead of giving manually Sales_backup_2015_05_31_230001_7137975 I need to get db name automatically
Thanks.
There is a function DB_NAME() that would return the name of the current database if no parameters are passed. Check this.
I guess dynamic SQL might help you to run SP in different databases:
DECLARE #sql nvarchar(max)
SELECT #sql = (
SELECT N'Use '+QUOTENAME([name]) +' Exec [spMaint_TrimTestDB] 1;'
FROM sys.databases
WHERE database_id >= 5 AND [name] like 'Sales_backup%'
FOR XML PATH('')
)
EXEC sp_executesql #sql
This script will create and execute dynamic statement like:
Use [sales_backup_2015] Exec [spMaint_TrimTestDB] 1;
Use [sales_backup_2016] Exec [spMaint_TrimTestDB] 1;
etc...

Remove user name from table name

I restored a database after a server failure and now I'm running into a problem where the table names show as database_user_name.table_name. So when I query something like:
select * from contacts
it doesn't work because it expects it be fully qualified, as in:
select * from user1000.contacts
The problem with this is that I have hundreds of stored procedures that reference the tables with their name, so none of the queries work.
Is there a way to tell SQL Server 2005 to drop the username from the table without changing the user as the owner?
try this advice from the manual:
To change the schema of a table or view by using SQL Server Management Studio, in Object Explorer, right-click the table or view and then click Design. Press F4 to open the Properties window. In the Schema box, select a new schema.
If you are sure none of the tables exist in the dbo schema as well, then you can say:
ALTER SCHEMA dbo TRANSFER user1000.contacts;
To generate a set of scripts for all of the tables in that schema, you can say:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'
ALTER SCHEMA dbo TRANSFER user1000.' + QUOTENAME(name) + ';'
FROM sys.tables
WHERE SCHEMA_NAME([schema_id]) = N'user1000';
PRINT #sql;
--EXEC sp_executesql #sql;
(Once you're happy with the PRINT output - acknowledging that it will be truncated at 8K even though the variable really contains the whole script - uncomment the EXEC and run it again. This does not check for potential conflicts.)
But the real fix is to fix your code. You should never say select * from contacts - both the * and the missing schema prefix can be problematic for various reasons.

Is is possible to find an equivalent Login in a 2nd database via a single query?

A database-level Login can be associated with zero to one server-instance-level Logins. And if it exists, that in turn can be associated with zero to one database-level Logins on another database.
Is it possible to retrieve the matching Login using a single query?
Given the answer to this question, I suspect it's not. But it's worth asking.
Assuming you have a local database user named foo, you can use this query to find out if there is a related user in database [splunge]:
SELECT [local].[name], [remote].[name]
FROM sys.database_principals AS [local]
INNER JOIN [splunge].sys.database_principals AS [remote]
ON [local].[sid] = [remote].[sid]
WHERE [local].[name] = 'foo';
If you don't know which other database(s) the related login may be found in, then no, there isn't a simple way without constructing a query like the answer in the other question you pointed to. If you are going to use sp_msForEachDB, please use caution:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server
One way to do this a little easier would be:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql = #sql + '
UNION ALL SELECT ''' + QUOTENAME(name)
+ ''', name COLLATE SQL_Latin1_General_CP1_CI_AS
FROM ' + QUOTENAME(name) + '.sys.database_principals
WHERE sid IN (SELECT sid FROM x)'
FROM sys.databases
WHERE database_id > 4; -- assume ignore system dbs
SET #sql = ';WITH x AS (SELECT sid FROM sys.database_principals
WHERE name = ''foo'')' + STUFF(#sql, 1, 12, '') + ';';
PRINT #sql;
--EXEC sp_executesql #sql;
This doesn't meet your requirement of "a single query" but maybe you could explain why that is a requirement.

Change the roles of multiple security accounts

I have many security accounts on the sql database and i want to remove/add roles to them based on a simple string comparison.
Basically i want to list all
accounts
Filter out accounts that DON'T start
with "MyDomain\"
Remove role A.
Add role B.
What i found out by now is that i use sp_helprolemember to list all the accounts and sp_addrolemember and sp_droprolemember. My problem is that i dont know how to "get" the output from sp_helprolemember and work with it.
My first attemt at a soltuion based of feedback.
DROP TABLE [dbo].[XTemp]
create table XTemp(DbRole sysname,MemberName sysname,MemberSID varbinary(85) )
insert XTemp exec sp_helprolemember
select * from XTemp
I made a permanent table to make it simpler to test and debug.
SELECT [DbRole]
,[MemberName]
,[MemberSID]
FROM [ARTICLE].[dbo].[XTemp]
WHERE MemberName like Domain\%'
exec sp_addrolemember 'OldRole MemberName
Assuming that you're using SQL 2005 or later, and executing sp_helprolemember without parameters, this is the query that sp_helprolemember runs (extracted using sp_helptext):
select DbRole = g.name, MemberName = u.name, MemberSID = u.sid
from sys.database_principals u, sys.database_principals g, sys.database_role_members m
where g.principal_id = m.role_principal_id
and u.principal_id = m.member_principal_id
order by 1, 2
This should enable you to collect the information you need into a temp table.
If you'd rather stick to documented behaviour, you can store the output of the SP into a temp table:
create table #t
(DbRole sysname,
MemberName sysname,
MemberSID varbinary(85)
)
insert #t
exec sp_helprolemember
select * from #t
EDIT
There are two ways to use this data to amend your system. One is using a cursor:
DECLARE #memberName sysname
DECLARE curMember CURSOR fast_forward FOR
SELECT MemberName
FROM #t
WHERE MemberName LIKE 'Domain\%'
OPEN curMember
FETCH NEXT FROM curMember INTO #memberName
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC sp_addrolemember 'OldRole', #memberName
FETCH NEXT FROM curMember INTO #memberName
END
CLOSE curMember
DEALLOCATE curMember
The other is using dynamic SQL:
DECLARE #sql NVARCHAR(MAX),
SELECT #sql = 'EXEC sp_addrolemember ''OldRole'', ''' + MemberName + ''''
FROM #t
WHERE MemberName LIKE 'Domain\%'
EXEC sp_executesql #stmt = #sql
As you can see the dynamic SQL version is more compact but requires more effort to maintain.
Remember that after you execute either statement, the data you extracted from sp_helprolemember into a table is no longer up to date, and should probably be refreshed.
You can use Excel to generate SQL queries - I know it sounds lame but it is very simple and powerful. It is especially well-suited for tasks that have to be performed once or only from time to time.
Copy results from Management Studio to Excel.
Remove rows and columns than you don't need.
Use a formula in column B (e.g. ="EXEC sp_dropsrvrolemember '"&A1&"', 'sysadmin'") to generate queries for values stored in column A (the formula can of course reference more than one column with input data and generate really complicated queries).
Copy generated queries from Excel to Management Studio.

Resources