Dynamically joining tables that may not exist - sql

What's a good way to dynamically create tables, then join together all the tables that I have created?
Background
I'm trying to write a stored procedure that collects data from various parts of the database and presents it for use. For example if I wanted to send invoices to both Alice and Bob, this stored procedure would find all the information for the invoice. The trick is that Alice and Bob don't always need all the information I have. If Bob is tax exempt but trades in simoleons, I'll need to include information about currency exchange rates but leave out tax info. I want to give the stored procedure an order number and get back results formatted to the customer.
My solution so far is to create a table holding customer requirements. Then my stored procedure checks the table: if currency data is required I create and populate #Currency, or #Taxes holds tax data and so on. I check the requirement, then create the table as needed. At the end I need to Join the data into one record set.
My problem is that I have a error saying
Incorrect syntax near the keyword 'table'
AND an error saying
Must declare the table variable #Summary
on line 1 where i define #Summary These errors only show up when I use dynamic SQL to join the tables. When I comment out the dynamic SQL and copy-paste the statements into the one it should create, I have the result I want.
Code
I tried to use the following dynamic SQL in Microsoft SQL Server Management Studio
create procedure <procedure> (#OrderNumber varchar(20)) AS
DECLARE #CustomerName varchar(35) -- retrieved based on #OrderNumber
DECLARE #SQLQuery nvarchar(500)
DECLARE #ParamDef nvarchar(500)
DECLARE #SummaryTable table
(
ID varchar(20) --=#OrderNumber
, <Stuff>
)
SET #SQLQuery = 'Select * From #SummaryTable'
SET #ParamDef = '#SummaryTable table'
IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = #CustomerName)
BEGIN
--Create table
DECLARE #<TableName> table
(
ID varchar(20)
, <Stuff>
)
--Populate
Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on #OrderNumber or #CustomerName>
--Pepare Dynamic SQL
Set #SQLQuery = #SQLQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
SET #ParamDef = ', #<TableName> table'
END
<repeat for other tables>
EXECUTE sp_executesql #SQLQuery, #ParamDef, #Summary, #LineItems, #Taxes, #Currency
Question
Is there something wrong with my code? Is there a better way to do this? The other option I though of was to have IF Exists trees with the entire JOIN statement at the bottom of each branch (Since I can't seem to interrupt the JOIN clause with IF's). The problem with that is that I'll need 2^n JOIN statements to join n possible tables. n in this case might be as high has 20 or 30.

I think I see a problem in the code (maybe 3 problems -- see "questionable" 1 & 2 below) --
1) [Changed on 10/21, after OP's comments] The big problem: table parameters passed in the final "EXECUTE sp_executesql #SQLQuery..." are sometimes not declared.
1a) #Summary is actually never declared... you declared and set #SummaryTable, then use #Summary.
Just change that to #SummaryTable (I did that in (4) below), and I think that will prevent your second error message ("Must declare the table variable #Summary").
1b) All the other tables are sometimes declared: each of their DECLARE statements are within an "IF EXISTS". I suggest either (I) make the declares unconditional (outide IF EXISTS), but still conditionally INSERT... or (II) make the format of the EXECUTE command vary with what's available.
(I doubt the unused table variables need anything in them....)
In point 4 below (added 10/21), I give an example that I have NOT TESTED with my own databases (that test would take a lot more time)... so please let me know how it goes...
2) [questionable 1] Simple case of mixed case ;) Note that the line...
Set #SQLQuery = #SqlQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
... first has an uppercase "SQL", then mixed-case "Sql".
Why I said this is "questionable" -- if your server's COLLATION is case-insensitive, what you typed above would be fine.
3) [questionable 2] You have both '#TableName' and '#Table Name' (with a space). I realize that may just be how you typed it, in posting your question.
Point (4), added in update to answer -- possible code
create procedure <procedure> (#OrderNumber varchar(20)) AS
DECLARE #CustomerName varchar(35) -- retrieved based on #OrderNumber
DECLARE #SQLQuery nvarchar(500)
DECLARE #ParamDef nvarchar(500)
DECLARE #SummaryTable table
(
ID varchar(20) --=#OrderNumber
, <Stuff>
)
SET #SQLQuery = 'Select * From #SummaryTable'
SET #ParamDef = '#SummaryTable table'
--Create table variables, though they may not be populated
DECLARE #LineItems
(
ID varchar(20)
, <Stuff>
)
DECLARE #Taxes
(
ID varchar(20)
, <Stuff>
)
DECLARE #Currencytable
(
ID varchar(20)
, <Stuff>
)
IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = #CustomerName)
BEGIN
--Populate
Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on #OrderNumber or #CustomerName>
--Prepare Dynamic SQL
Set #SQLQuery = #SQLQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
SET #ParamDef = ', #<TableName> table'
END
<repeat for other tables>
EXECUTE sp_executesql #SQLQuery, #ParamDef, #SummaryTable, #LineItems, #Taxes, #Currency

The solution I've decided tp use is to create a table then use ALTER TABLE to add columns as needed.
CREATE Table #Output
(
InvoiceNumber varchar(20)
, <stuff>
)
IF EXISTS(Select <ThisSegment> FROM CustRequirements WHERE <ThisSegment> = 1 AND Customer = #CustomerName)
BEGIN
ALTER TABLE #Output ADD <ThisSegment> <dataType>
UPDATE #Output
SET <ThisSegment> = <data> from <DataSource>
WHERE <InvoiceNumber = DataSource.invoice> AND <Other conditions>
END
<Repeat as needed>

Related

How to set 1 database name containing a specific table to variable?

So, a little backstory on this one:
I have a lengthy query that I use frequently for my jobs that utilizes the sp_MSForEachDB stored proc. The query creates a temporary table, and then goes through and searches sys.tables, and if a database has a table called T_Study, it will run the query and pull these results into the temporary table.
What I am now trying to do is join in another table from another database, and this database is of a different type. These database are all distinguished by the existence T_BatchStores. All databases that have a T_BatchStores table will also have a table dbo.T_TSyntax, and this is the table on which I will need to join.
Essentially, I am trying to build a universal query that I can run on any server containing this software, and I have these few universal tables, but the names of the databases themselves will vary.
So what I want to do is, in my query that populates my temporary table, add the following:
JOIN '+#MEDDB+'.dbo.T_TSyntax and then my ON clause, etc. Keep in mind that this join occurs within my begin and end clauses and that sp_MSforEachDb will be run on this.
I want #MEDDB to be just a randomly selected name of ANY database on their SQL instance that contains a T_BatchStores table - it does not matter what the name is - I just don't want to modify the query every time I run it.
How can I use my DECLARE #MEDDB and SET/SELECT #MEDDB to work with these requirements?
Also, in some reading, I read that I should use SYSNAME as the data type, but I also know NVARCHAR would work - but I'd just like a tip on which would be ideal.
I have tried the following:
DECLARE #MEDDB SYSNAME
SET #MEDDB = (SELECT TOP 1 name FROM sys.databases WHERE EXISTS (SELECT name FROM sys.tables WHERE name = '''T_BATCHSTORES'''))
SELECT #MEDDB
But this returns 1 row with a NULL value. I'm very much a beginner to SQL, so any assistance would be greatly appreciated!
Note: I am only using Microsoft SQL Server 2008 and 2012 at the present time.
Thanks!
Okay, after heavily modifying the query so that the names aren't actually exactly the same as our actual table structure, here is a slimmed-down version of the query I'm using so that you'll see what I'm going for:
/* Add or change variables as needed and comment back in WHERE statements in Insert section as needed.
You do not need to delete any variables in this section, even if you do not need them.
Simply comment in or out relevant data in the WHERE clause in Section 4. */
/* Section 1: Declaring variables. */
DECLARE #STUID NVARCHAR (65)
DECLARE #IMUID NVARCHAR (200)
DECLARE #ACCN NVARCHAR (100)
DECLARE #MEDDB NVARCHAR (255)
/* Section 2: Assigning values to variables such as an Image file's UID. */
SET #STUID = 'enterSTUID'
SET #IMUID = 'enterIMUIDhere'
SET #ACCN = 'enterACCNhere'
SET #MEDDB = (SELECT TOP 1 name FROM sys.databases WHERE [name] LIKE '%med%'
AND [name] NOT LIKE '%audit%')
/* Section 3: Creating our temporary table to dump our results. */
IF OBJECT_ID('tempdb.dbo.#tempBatchResultsD6') IS NULL
BEGIN
CREATE TABLE #tempBatchResultsD6
(
Database_Name VARCHAR (200),
THING1 VARCHAR(100) NOT NULL,
THING2 VARCHAR(200) NOT NULL,
THING3 DATETIME NOT NULL,
TSyntaxUID VARCHAR (66) NOT NULL,
TSyntaxDesc VARCHAR (128) NOT NULL
)
END
TRUNCATE TABLE #tempBatchResultsD6
/* Section 4: Query that will be used to populate the contents of the temporary table created above.
Utilizing the stored procedure "sp_MSForEachDb," this will search every database on the SQL instance.
Here, we are limiting our results to only searching specific databases by only returning results from databases that have a "T_Studies" table. */
DECLARE #Command NVARCHAR(MAX)
SET #Command = N'USE [?];
IF EXISTS (SELECT * FROM sys.tables WHERE [name] = ''T_Studies'')
BEGIN
INSERT #tempBatchResultsD6
SELECT
DB_Name () as Database_Name,
THING1,
THING2,
THING3,
TS.TSyntaxUID,
TS.TSyntaxDesc
FROM T_Studies ST WITH (nolock)
JOIN T_Patients PT WITH (nolock) ON ST.ST_PT_FOLDERID = PT.PT_FOLDERID
JOIN T_Series SE WITH (nolock) ON ST.ST_FOLDERID = SE.SE_ST_FOLDERID
JOIN T_Images IM WITH (nolock) ON SE.SE_FOLDERID = IM.IM_SE_FOLDERID
JOIN '+#MEDDB+'.dbo.T_TSyntaxes TS WITH (nolock) on IM.IM_TSyntaxID = TS.TSyntaxUID
WHERE ST.STUID = '''+#STUID+'''
--WHERE IM.IM_UID = '''+#IMUID+'''
--WHERE ST.ST_ACCNNO = '''+#ACCN+'''
END'
EXEC sp_MSForEachDb #Command
/* Section 5: Querying our temporary table to get our results. */
SELECT
Database_Name,
THING1,
THING2,
THING3,
TSyntaxUID,
TSyntaxDesc
FROM #tempBatchResultsD6
ORDER BY Database_Name
So as you can see, this is a massive temp table that will pull from all databases that have a T_Studies table in them. It's huge in its actual form, but this is trimmed down significantly.
The problem comes in section 2, where I am using #MEDDB to choose a random database name if the name contains the word "Med" but not the word "audit." This is problematic because it assumes consistent naming across all sites - and while these names are suggested, they are never a guarantee.
To guarantee consistency, I am trying to populate the #MEDDB variable with the name of ANY database on their system that contains a T_BatchStores table instead of the WHERE [name] like portion.
Any advice with this new info would be greatly appreciated!
I am not sure whether I understand your question completely.
But if you wanna loop through all databases, you can use something like the following code. "SELECT XXX" would then be your query, by using "use ' + #DB_Name + ';" you switch to the desired database to execute the your query.
DECLARE #DB_Name varchar(100)
DECLARE #Command nvarchar(max)
DECLARE database_cursor CURSOR FOR
SELECT name
FROM MASTER.sys.sysdatabases
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #DB_Name
WHILE ##FETCH_STATUS = 0
BEGIN
set #Command =
'
use ' + #DB_Name + ';
SELECT XXX
'
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #DB_Name
END
CLOSE database_cursor
DEALLOCATE database_cursor
DECLARE #MEDDB NVARCHAR(500)
SET #MEDDB = 'SELECT TOP 1 name FROM sys.databases WHERE EXISTS (SELECT name FROM sys.tables WHERE name = ''T_BATCHSTORES'')'
EXEC (#MEDDB)
This is Dynamic SQL. Of course, I'm not getting any results, either, but I'm not in your database. Still, this allows you to be 'dynamic', and use a variable to modify things:
DECLARE #MEDDB NVARCHAR(500)
DECLARE #MEDDB2 NVARCHAR(100) = 'T_BATCHSTORES'
SET #MEDDB = 'SELECT TOP 1 name FROM sys.databases WHERE EXISTS (SELECT name FROM sys.tables WHERE name = ''' + #MEDDB2 + ''')'
EXEC (#MEDDB)
My recommendation is that you run simpler queries that show what you want, and then you build them into the dynamic method.
For instance, I'm in AdventureWorks right now, so I can do:
SELECT * FROM sys.databases
This gives me a list of all the databases that are available in here.
So maybe I expand that to
SELECT * FROM sys.databases WHERE name = 'AdventureWorks'
This gives me just one line of results. So if that's what I want, I build it into the dynamic portion:
DECLARE #MEDDB NVARCHAR(500)
DECLARE #MEDDB2 NVARCHAR(100) = 'AdventureWorks'
SET #MEDDB = 'SELECT * FROM sys.databases WHERE name =''' + #MEDDB2 + ''''
EXEC (#MEDDB)
It just all depends what you're looking for. I always find the data I want first, and then figure out how to make my query look for it.

SQL column value to table name

In my SQL database, I would like to make a general purpose soft link table. Perhaps something similar the following:
create table soft_link(
id uniqueidentifier,
name varchar(255),
LINK uniqueidentifier,
TABLE varchar(255),
primary key(id));
Say I have object "b_object" in some other table in the database. The LINK column would be the the unique identifier of b_object, and TABLE would be the table in which b_object is stored within the database.
Now I want to make a single stored procedure "sp_ResolveSoftLink". This method will take an id of a soft link, look up the LINK and TABLE columns of the soft link, and then use the TABLE and LINK to query for b_object.
The following is NOT proper SQL syntax, but hopefully it helps illustrate my question. I want to combine these two queries into a single stored procedure and return the result of the second query:
select LINK, TABLE from soft_link where id = xxxxxx
select * from TABLE where id = LINK
-- return the result of the second select query
FUNDAMENTAL QUESTION: How/can I use the varchar return from one query to form another query in the same stored procedure in SQL?
You can create a Stored procedure like this.
Here, First you have to store value of LINK and Table in variable and then use these variable to form another Dynamic Query
CREATE PROC spName
#id INT
AS
BEGIN
Declare #LINKValue uniqueidentifier, #TABLE varchar(255),#SQL varchar(max)
SELECT #LINKValue = LINK , #TABLE = TABLE
FROM soft_link
WHERE soft_link id = #id
SET #SQL='SELECT * FROM';
SET #SQL = #SQL +' ' + #TABLE
SET #SQL = #SQL +' ' + 'WHERE ID =' +' ' + #LINKValue
PRINT #SQL -- SEE Here This is what you actually Want
--EXEC (#SQL) --Then Execute this
END

Loop through Column Names in a table and do a set of operations - SQL Server 2008

Thank you for helping me out through my previous tides.. I am currently working on SQL Server 2008 for one of my project, a part of which needs to use 22 columns for a set of similar operations.
The column names only differ by the number, e.g.
C1_1,C1_2,c1_3
Is there any way to loop through the column names? I tried out the following code, but it throws out an error
DECLARE #i INT
SET #i=2
while (#i<=22)
begin
SELECT [DEF].[CONCATENATE], SUM(DEF.[C1_+#i+_PREV]) as
[C1_#i_prev]
INTO #TMP_C1_#i_CONCATENATE_PREV
FROM DEF
GROUP BY DEF.[CONCATENATE]
SELECT [ABC].[CONCATENATE], SUM(ABC.[C1_#i_CURR]) as
[c1_#i_curr]
INTO #TMP_C1_#i_CONCATENATE_CURR
FROM ABC
GROUP BY ABC.[CONCATENATE]
UPDATE #tmp_var_concatenate_c1_#i
SET [Amount] = #TMP_C1_#i_CONCATENATE_PREV.[C1_#i_PREV]
FROM #tmp_var_concatenate_c1_#i
INNER JOIN
#TMP_C1_#i_CONCATENATE_PREV ON
#tmp_var_concatenate_c1_#i.[CONCATENATE] = #TMP_C1_#i_CONCATENATE_PREV.
[CONCATENATE]
Please forgive my ignorance, if I am doing something idiotic.
Thanks
This is part of the code in which the code is burping.
alter table #tmp_var_concatenate_C1_'+cast(#i as varchar)+'
add [ColA] varchar(255),
[ColB] Varchar(255),
[ColC] Varchar(255),
[ColD] VARCHAR(50),
[ColE] MONEY,
[ColF] MONEY
Is it because of the #tables that I am using ?? but, ideally, it shouldnt be an issue whether am using a Temp table or a reg. one..
You can use dynamic sql:
DECLARE #SQL varchar(max), #i INT
SET #i=2
while (#i<=22)
begin
/* Then cover all calculations with this one: */
SET #SQL='SELECT [DEF].[CONCATENATE], SUM(DEF.[C1_'+cast(#i as varchar)+'_PREV]) as
[C1_'+cast(#i as varchar)+'_prev]
INTO #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV
FROM DEF
GROUP BY DEF.[CONCATENATE]
/* and all your code with the same trick in #i to the END */
'
--PRINT (#SQL) -- print it before use to see the result script
EXEC (#SQL)
/* Than do your iterations etc. */
set #i+=1
end
And don't forget to substitute all ' inside #SQL with ''.
Also you need to do all manipulations with temp tables inside #SQL, if you want to do final update outside the dynamic sql, just make tables real and then delete them.
[UPDATE]
As far as you faced with problem of altering temp tables, I tried to reproduce this error, but nothing happens, everything works fine. Please use this code as an example.
declare #sql varchar(max),#i int
set #i=2
while #i<=22
begin
set #sql='
select ID,Code into #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV from (select 0 as ID, ''a'' as Code) t1
alter table #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV add [Col1] varchar(255), [Col2] Varchar(255), [Col3] Money
select * from #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV'
--print (#sql)
exec (#sql)
set #i+=1
end
First, I create temp table with dynamic name. Second, add new columns. The last is successful verifying. Did you execute all creations/alters in the same #sql-batch? If no, this won't work, because this tables are available only inside this batch (that's why we used varchar(max) when declared). Please describe your actions in details, maybe there is a mistake somewhere.

How can I spot in what database is a stored procedure with name 'myStoredProcedure'?

There are bunch of databases to the SQL server I am connected.
How should I query the sysobjects in order to spot in what database a stored procedure with name 'myStoredProcedure' is located ?
The query should return the database name.
Thanks
I know you are not asking for this, but I'd really download RedGate's Sql Search add-in for SSMS and use that. It allows you to find any object (proc, table, view, column, etc) on any database easily.
And it's free!
I'd give this a try:
CREATE TABLE ##DatabaseList
(
DatabaseName varchar(50)
)
EXECUTE SP_MSForEachDB 'USE [?]; INSERT INTO ##DatabaseList SELECT DB_NAME() FROM [sys].[objects] WHERE name = "MyStoredProcedure" AND type_desc = "SQL_STORED_PROCEDURE"'
SELECT * FROM ##DatabaseList
DROP TABLE ##DatabaseList
That's using the undocumented/ unsupported system stored procedure SP_MSForEachDb and writing any hits to a global temp table, then outputting the contents to the Results window before dropping the table. If you just need to know which database (or databases - there may of course be more than one) has an appropriately named SP, this should do it. If you want to use the output elsewhere as a parameter, it may take a little more work.
By the way, I'm only learning this stuff myself over the last few months so if anyone can critique the above and suggest a better way to go at it I'm happy to receive feedback. Equally, I can answer any further questions posted here to the best of my ability.
Cheers
So out of curiosity I decided to try write this myself, especially since ADG mentioned his solution was using an unsupported, undocumented procedure. This could also be expanded to take a 2nd parameter so where it checks the type = P (stored Proc) you could probably change it to look for other things like views / tables etc.
My solution is a bit long but here goes:
CREATE PROCEDURE spFindProceduresInDatabases
(
#ProcedureName NVARCHAR(99)
)
AS
BEGIN
-- Get all the database names and put them into a table
DECLARE #Db TABLE (DatabaseName Varchar(99))
INSERT INTO #Db SELECT name FROM Sys.databases
-- Declare a table to hold our results
DECLARE #results TABLE (DatabaseName VARCHAR(99))
-- Make a Loop
-- Declare a variable to be incremented
DECLARE #count INT
SET #count = 0
-- Declare the end condition
DECLARE #endCount INT
SELECT #endCount = COUNT(*) FROM #Db
-- Loop through the databases
WHILE (#count < #endCount )
BEGIN
-- Get the database we are going to look into
DECLARE #dbWeAreChecking VARCHAR(99)
SELECT TOP 1 #dbWeAreChecking = DatabaseName FROM #Db
DELETE FROM #Db WHERE DatabaseName = #dbWeAreChecking
-- Create and execute our query
DECLARE #Query NVARCHAR(3000)
SET #Query = N'SELECT #outParam = COUNT(*) FROM '+#dbWeAreChecking+'.sys.sysobjects WHERE type = ''P'' and name = #ProcedureName'
Declare #outParam INT
print (#Query)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #IntVariable INT
SET #ParmDefinition = N'#ProcedureName VARCHAR(99),#outParam INT OUTPUT'
SET #IntVariable = 35
EXECUTE sp_executesql
#Query ,
#ParmDefinition,
#ProcedureName,
#outParam = #outParam OUTPUT
-- If we have a result insert it into the results table
If (#outParam > 0)
BEGIN
INSERT INTO #results(DatabaseName) VALUES(#dbWeAreChecking)
END
-- Increment the counter
SET #count = (#count + 1)
END
-- SELECT ALL OF THE THINGS!!!
SELECT * FROM #results
END

SQL Server Multi-Step Stored Procedure

I've got a software suite that is based off of multiple libraries where:
1 library = 1 SQL Database.
Different users can have different access to different libraries.
In addition, the databases are named in a specific manner to help identify which are "mine" and which aren't.
I'd like to create a stored procedure that takes a variable called #UserName and returns the databases that have a name starting with MYDB, where #UserName is found in a table USERS.
I'm figuring that I'll start with EXEC sp_databases, but I'm unsure how to continue.
What I need to know is:
How do I iterate the results of sp_databases to pull out just the databases that have a name matching my pattern?
How do I then check for #UserName in the [USER NAME] column of the USERS table of each database returned from #1?
I'm guessing it has something to do with temp tables and cursors, but I'm not really sure where to start.
Any help?
Thanks!
Here is some proof of concept code to show you an approach. sys.databases contains a more accessible list of databases. You'll pretty much have to use dynamic sql at some point though.
CREATE PROCEDURE MyDBs #userName VARCHAR(255)
AS
BEGIN
DECLARE #max INT
DECLARE #i INT
DECLARE #sql VARCHAR(500)
CREATE TABLE #SQL
(
rid int identity primary key clustered,
query varchar(500)
)
INSERT INTO #SQL(query)
SELECT 'SELECT * FROM ['+ name '+].USERS WHERE username = #UserName'
FROM master.sys.databases
WHERE NAME LIKE '%yourpattern%'
SELECT #max = ##rowcount, #i = 1
WHILE #i <= #max
BEGIN
SELECT #sql = query FROM #sql WHERE rid = #i
EXEC #sql
SET #i = #i + 1
END
DROP TABLE #SQL
For 1, just look at the sp_databases code, copy it and modify it to your needs. For Example (see last 2 conditions of where clause. This is the actual code of the sp_databases stored proc. You can look at it on the master db):
declare #UserName varchar(50)='someuser'
select
DATABASE_NAME = db_name(s_mf.database_id),
DATABASE_SIZE = convert(int,
case -- more than 2TB(maxint) worth of pages (by 8K each) can not fit an int...
when convert(bigint, sum(s_mf.size)) >= 268435456
then null
else sum(s_mf.size)*8 -- Convert from 8192 byte pages to Kb
end),
REMARKS = convert(varchar(254),null)
from
sys.master_files s_mf
where
s_mf.state = 0 and -- ONLINE
has_dbaccess(db_name(s_mf.database_id)) = 1 and
--db_name(s_mf.database_id) like '%'+#UserName+'%' and exists -- you may or may not want to leave this condition here. You'll figure out what condition to use
(select 1 from databasename.dbo.Users where [UserName]=#UserName)
group by s_mf.database_id
order by 1