Sybase ASE identify columns of keys of multiple tables - database

I'm trying to identify the columns making up keys in ASE.
Sybase has the solution listed here: http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.help.ase.15.5/title.htm
I have a slightly modified version below, however it only works (just as sybase's solution) if I look up for a single table, but I want to use the 'in' keyword and look up all the tables in one shot.
Could I get some help, as to why the solution below does not work? It only generates the list of columns for 't5' table.
declare #keycnt integer
declare #objname varchar(256)
select #keycnt = keycnt, #objname = sysobjects.name from sysindexes, sysobjects
where
--sysobjects.id = object_id("t5")
--sysobjects.id = object_id("t4")
sysobjects.id in (object_id("t5"), object_id("t4"))
and sysobjects.id = sysindexes.id
and indid = 1
while #keycnt > 0
begin
select index_col(#objname, 1, #keycnt)
select #keycnt = #keycnt - 1
end
These are the tables I'm using for testing:
CREATE TABLE t4(
[value] [varchar] (500) not NULL ,
CONSTRAINT pk_g4 PRIMARY KEY CLUSTERED (
[value]
)
)
CREATE TABLE t5(
[myvalue] [varchar] (500) not NULL ,
CONSTRAINT pk_g4 PRIMARY KEY CLUSTERED (
[myvalue]
)
)

You have two solutions:
Using OR
declare #keycnt integer
declare #objname varchar(256)
select #keycnt = keycnt, #objname = sysobjects.name from sysindexes, sysobjects
where
--sysobjects.id = object_id("t5")
--sysobjects.id = object_id("t4")
(sysobjects.id = object_id("t5") OR sysobjects.id = object_id("t4"))
and sysobjects.id = sysindexes.id
and indid = 1
while #keycnt > 0
begin
select index_col(#objname, 1, #keycnt)
select #keycnt = #keycnt - 1
end
Or Using Dynamic SQL to properly use the IN.

Related

Select on a table with 2 possible structures

I'm trying to write a query that will select data from a table. due to different versions of the database, there are 2 possible structures for the source table, where the newer version has 2 more fields than the old one.
I've tried identifying the older structure and replacing the columns with NULL and also tried writing 2 separate queries with and IF statement directing to the correct one. Neither of these solutions work and in both cases it seems that the SQL engine is failing on validating these 2 columns.
Examples of my attempted solutions:
IF NOT EXISTS (SELECT *
FROM sys.objects
WHERE object_id = Object_id(N'[dbo].[Test2]')
AND type IN ( N'U' ))
BEGIN
CREATE TABLE [dbo].[test2]
(
[id] [INT] IDENTITY(1, 1) NOT NULL,
[statusid] [INT] NULL
)
END
go
DECLARE #Flag INT = 0
IF EXISTS(SELECT 1
FROM sys.columns
WHERE NAME = N'TestId'
AND object_id = Object_id(N'dbo.Test2'))
SET #Flag = 1
--Solution #1
IF #Flag = 1
SELECT id,
statusid,
testid
FROM dbo.test2
ELSE
SELECT id,
statusid
FROM dbo.test2
--Solution #2
SELECT id,
statusid,
CASE
WHEN #Flag = 1 THEN testid
ELSE NULL
END AS TestId
FROM dbo.test2
you can use Dynamic SQL and generate the query accordingly depends on value of #flag
declare #sql nvarchar(max)
select #sql = N'select id, statusid, '
+ case when #flag = 1 then 'testid' else 'NULL' end + ' as testid'
+ ' from dbo.test2'
print #sql
exec sp_executesql #sql
But it will not be that easy to code and maintain Dynamic Query if you have a complex query.

2 sub queries in insert statement values in MS SQL Server Compact Edition 4.0

Specifically I'm using SQL Server Compact 4.0, if that makes a difference.
I have 3 tables (note,userTable,verse). the user and verse table have no correlation except in this note table, so I can't do a single subquery joining the two tables.
INSERT INTO [note]
([verse_id]
,[user_id]
,[text]
,[date_created]
,[date_modified])
VALUES
( (SELECT Id FROM verse
WHERE volume_lds_url = 'ot'
AND book_lds_url = 'gen'
AND chapter_number = 8
AND verse_number = 16)
, (SELECT Id FROM userTable
WHERE username = 'canichols2')
,'test message'
,GETDATE()
,GETDATE());
GO
As far as I can tell, the statement should work.
The outer statements works fine if i hard code the Foreign Key values, and each of the subqueries work as they should and only return one column and one row each.
Error Message:There was an error parsing the query. [ Token line number = 8,Token line offset = 14,Token in error = SELECT ]
So It doesn't like the subquery in a scalar values clause, but I Can't figure out how to use a
INSERT INTO .... SELECT ....
statement with the 2 different tables.
Table Definitions
Since #Prasanna asked for it, here's the deffinitions
CREATE TABLE [userTable] (
[Id] int IDENTITY (1,1) NOT NULL
, [username] nvarchar(100) NOT NULL
, [email] nvarchar(100) NOT NULL
, [password] nvarchar(100) NULL
);
GO
ALTER TABLE [userTable] ADD CONSTRAINT [PK_user] PRIMARY KEY ([Id]);
GO
CREATE TABLE [note] (
[Id] int IDENTITY (1,1) NOT NULL
, [verse_id] int NULL
, [user_id] int NULL
, [text] nvarchar(4000) NOT NULL
, [date_created] datetime DEFAULT GETDATE() NOT NULL
, [date_modified] datetime NULL
);
GO
ALTER TABLE [note] ADD CONSTRAINT [PK_note] PRIMARY KEY ([Id]);
GO
CREATE TABLE [verse] (
[Id] int IDENTITY (1,1) NOT NULL
, [volume_id] int NULL
, [book_id] int NULL
, [chapter_id] int NULL
, [verse_id] int NULL
, [volume_title] nvarchar(100) NULL
, [book_title] nvarchar(100) NULL
, [volume_long_title] nvarchar(100) NULL
, [book_long_title] nvarchar(100) NULL
, [volume_subtitle] nvarchar(100) NULL
, [book_subtitle] nvarchar(100) NULL
, [volume_short_title] nvarchar(100) NULL
, [book_short_title] nvarchar(100) NULL
, [volume_lds_url] nvarchar(100) NULL
, [book_lds_url] nvarchar(100) NULL
, [chapter_number] int NULL
, [verse_number] int NULL
, [scripture_text] nvarchar(4000) NULL
);
GO
ALTER TABLE [verse] ADD CONSTRAINT [PK_scriptures] PRIMARY KEY ([Id]);
GO
I'm aware it's not in the 1st normal form or anything, But that's how it was given to me, and I didn't feel like dividing it up into multiple tables.
SubQuery Results
To show the results and how there's only 1 row.
SELECT Id FROM WHERE volume_lds_url = 'ot'
AND book_lds_url = 'gen'
AND chapter_number = 8
AND verse_number = 16
Id
200
And the second subquery
SELECT Id FROM userTable
WHERE username = 'canichols2'
Id
1
Attention: The target system is SQL-Server-Compact-CE-4
This smaller brother seems not to support neither sub-selects as scalar values, nor declared variables. Find details in comments...
Approach 1
As long as you can be sure, that the sub-select returns exactly one scalar value, it should be easy to transform your VALUES to a SELECT. Try this:
INSERT INTO [note]
([verse_id]
,[user_id]
,[text]
,[date_created]
,[date_modified])
SELECT
(SELECT Id FROM verse
WHERE volume_lds_url = 'ot'
AND book_lds_url = 'gen'
AND chapter_number = 8
AND verse_number = 16)
, (SELECT Id FROM userTable
WHERE username = 'canichols2')
,'test message'
,GETDATE()
,GETDATE();
Approach 2
No experience with Compact editions of SQL-Server, but you might try this:
DECLARE #id1 INT=(SELECT Id FROM verse
WHERE volume_lds_url = 'ot'
AND book_lds_url = 'gen'
AND chapter_number = 8
AND verse_number = 16);
DECLARE #id2 INT=(SELECT Id FROM userTable
WHERE username = 'canichols2');
INSERT INTO [note]
([verse_id]
,[user_id]
,[text]
,[date_created]
,[date_modified])
SELECT #id1
,#id2
,'test message'
,GETDATE()
,GETDATE();

How to get rid of timeout error when using mssql hierarchy check constrint

i am creating sql hirercky table
Here is my code;
The Constraint Function Code
alter Function Accounts.Types_Sub_Check_fn (#ID uniqueidentifier, #Sub Uniqueidentifier) returns int
begin
--declare #id uniqueidentifier = '8c7d4151-246c-476c-adf6-964ca9afdd3c' declare #sub uniqueidentifier = '47c2b6da-25fc-4921-adfa-b1f635bddde6'
declare #a int
declare #b int =(iif(#ID=#SUB,2,0))
;with cte(id, lvl) as
(
select f.sub,
1
from Accounts.Types as f
where f.id = #id
union all
select f.sub,
lvl + 1
from Accounts.Types as f
inner join cte as c
on f.id = c.id
)
select #a = (select count (*)
from cte
where id =#sub) + #b
option (maxrecursion 0)
return #a
end
go
The Table code
create Table Accounts.Types
(
ID uniqueidentifier not null CONSTRAINT DF_Accounts_Types_ID DEFAULT newid() CONSTRAINT PK_Accounts_Types_ID PRIMARY KEY NONCLUSTERED (ID) ,
Name varchar(200) not null CONSTRAINT UQ_Accounts_Types_NAME UNIQUE (NAME),
Sub uniqueidentifier CONSTRAINT FK_Accounts_Types_Sub Foreign key references Accounts.Types ,
Ctype uniqueidentifier CONSTRAINT FK_Accounts_Types_Ctype Foreign key references Accounts.Types ,
insert_time datetime not null CONSTRAINT DF_Accounts_Types_Insert_Time DEFAULT getdate() ,
insert_user uniqueidentifier CONSTRAINT DF_Accounts_Types_Insert_User DEFAULT'9EC66F53-9233-4A6C-8933-F8417D2BB5A9' ,
ts timestamp,
INDEX IX_Accounts_Types_NAME#ASC CLUSTERED (Name ASC),
Constraint Check_Accounts_Types_Sub check (Accounts.Types_Sub_Check_fn(ID,Sub)<=1)
)
go
This function will give 2 as result if trying to insert itseft as parent (in sub column)
it will give 1 if its already a child, which trying to insert as its parent
The Check constraint is created to check if the the parent (sub column) for any id should not be its child or grand child,
and itself cannot be its parent
When i try to insert a data which does not match the check constraint, it stuck, and give a timeout error,
eg:
insert into Accounts.Types (ID, Name, Sub)
values ('607936b9-6f95-4989-8ebe-87a08807f43e','LLL','607936b9-6f95-4989-8ebe-87a08807f43e')
this will give timeout
can anyone help me out, i need to get rid of time out error; get the constraint error only
Easy question - when will your recursion end when your ID and Sub are the same values and you don't limit maxrecursion or lvl? Never. It'll never end.
values ('607936b9-6f95-4989-8ebe-87a08807f43e','LLL','607936b9-6f95-4989-8ebe-87a08807f43e')
You have to remove rows where ID = Sub or add maxrecursion or add level limit or normalize your table.
alter Function Accounts.Types_Sub_Check_fn (#ID uniqueidentifier, #Sub Uniqueidentifier) returns int
begin
--declare #id uniqueidentifier = '00279c6b-df00-4144-810d-571fdb1c5109' declare #sub uniqueidentifier = 'bc887e7b-36d2-4ece-8ec1-720dc81a9de4'
declare #a int = 0
declare #b int =(iif(#ID=#SUB,2,0))
if #ID <> #sub
begin
;with cte(id, lvl) as
(
select f.Sub ,
1
from Accounts.Types as f
where f.id = #sub
union all
select iif(f.Sub = #sub, Null, f.sub),
lvl + 1
from Accounts.Types as f
inner join cte as c
on f.id = c.id
)
select #a = (select count (*)
from cte
where id =#id)
option (maxrecursion 0);
end
-- select #a + #b
return #a + #b
end
go

Need to speed up SQL Server SP that uses system metadata

Let me apologize in advance for the length of this question. I don't see how to ask it without giving all the definitions.
I've inherited a SQL Server 2005 database that includes a homegrown implementation of change tracking. Through triggers, changes to virtually every field in the database are stored in a set of three tables. In the application for this database, the user can request the history of various items, and what's returned is not just changes to the item itself, but also changes in related tables. The problem is that in some cases, it's painfully slow, and in some cases, the request eventually crashes the application. The client has also reported other users having problems when someone requests history.
The tables that store the change data are as follows:
CREATE TABLE [dbo].[tblSYSChangeHistory](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[date] [datetime] NULL,
[obj_id] [int] NULL,
[uid] [varchar](50) NULL
This table tracks the tables that have been changed. Obj_id is the value that Object_ID() returns.
CREATE TABLE [dbo].[tblSYSChangeHistory_Items](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[h_id] [bigint] NOT NULL,
[item_id] [int] NULL,
[action] [tinyint] NULL
This table tracks the items that have been changed. h_id is a foreign key to tblSYSChangeHistory. item_id is the PK of the changed item in the specified table. action indicates insert, delete or change.
CREATE TABLE [dbo].[tblSYSChangeHistory_Details](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[i_id] [bigint] NOT NULL,
[col_id] [int] NOT NULL,
[prev_val] [varchar](max) NULL,
[new_val] [varchar](max) NULL
This table tracks the individual changes. i_id is a foreign key to tblSYSChangeHistory_Items. col_id indicates which column was changed, and prev_val and new_val indicate the original and new values for that field.
There's actually a fourth table that supports this architecture. tblSYSChangeHistory_Objects maps plain English descriptions of operations to particular tables in the database.
The code to look up the history for an item is incredibly convoluted. It's one branch of a very long SP. Relevant parameters are as follows:
#action varchar(50),
#obj_id bigint = 0,
#uid varchar(50) = '',
#prev_val varchar(MAX) = '',
#new_val varchar(MAX) = '',
#start_date datetime = '',
#end_date datetime = ''
I'm storing them to local variables right away (because I was able to significantly speed up another SP by doing so):
declare #iObj_id bigint,
#cUID varchar(50),
#cPrev_val varchar(max),
#cNew_val varchar(max),
#tStart_date datetime,
#tEnd_date datetime
set #iObj_id = #obj_id
set #cUID = #uid
set #cPrev_val = #prev_val
set #cNew_val = #new_val
set #tStart_date = #start_date
set #tEnd_date = #end_date
And here's the code from that branch of the SP:
create table #r (obj_id int, item_id int, l tinyint)
create clustered index #ri on #r (obj_id, item_id)
insert into #r
select object_id(obj_name), #iObj_id, 0
from dbo.tblSYSChangeHistory_Objects
where obj_type = 'U' and descr = cast(#cPrev_val AS varchar(150))
declare #i tinyint, #cnt int
set #i = 1
while #i <= 4
begin
insert into #r
select obj_id, item_id, #i
from dbo.vSYSChangeHistoryFK a with (nolock)
where exists (select null from #r where obj_id = a.rel_obj_id and item_id = a.rel_item_id and l = #i - 1)
and not exists (select null from #r where obj_id = a.obj_id and item_id = a.item_id)
set #cnt = ##rowcount
insert into #r
select rel_obj_id, rel_item_id, #i
from dbo.vSYSChangeHistoryFK a with (nolock)
where object_name(obj_id) not in (<this is a list of particular tables in the database>)
and exists (select null from #r where obj_id = a.obj_id and item_id = a.item_id and l between #i - 1 and #i)
and not exists (select null from #r where obj_id = a.rel_obj_id and item_id = a.rel_item_id)
set #i = case #cnt + ##rowcount when 0 then 100 else #i + 1 end
end
select date, obj_name, item, [uid], [action],
pkey, item_id, id, key_obj_id into #tCH_R
from dbo.vSYSChangeHistory a with (nolock)
where exists (select null from #r where obj_id = a.obj_id and item_id = a.item_id)
and (#cUID = '' or uid = #cUID)
and (#cNew_val = '' or [action] = #cNew_val)
declare ch_item_cursor cursor for
select distinct pkey, key_obj_id, item_id
from #tCH_R
where item = '' and pkey <> ''
open ch_item_cursor
fetch next from ch_item_cursor
into #cPrev_val, #iObj_id, #iCol_id
while ##fetch_status = 0
begin
set #SQLStr = 'select #val = ' + #cPrev_val +
' from ' + object_name(#iObj_id) + ' with (nolock)' +
' where id = #id'
exec sp_executesql #SQLStr,
N'#val varchar(max) output, #id int',
#cNew_val output, #iCol_id
update #tCH_R
set item = #cNew_val
where key_obj_id = #iObj_id
and item_id = #iCol_id
fetch next from ch_item_cursor
into #cPrev_val, #iObj_id, #iCol_id
end
close ch_item_cursor
deallocate ch_item_cursor
select date, obj_name,
cast(item AS varchar(254)) AS item,
uid, [action],
cast(id AS int) AS id
from #tCH_R
order by id
return
As you can see, the code uses a view. Here's that definition:
ALTER VIEW [dbo].[vSYSChangeHistoryFK]
AS
SELECT i.obj_id, i.item_id, c1.parent_object_id AS rel_obj_id, i2.item_id AS rel_item_id
FROM dbo.vSYSChangeHistoryItemsD AS i INNER JOIN
sys.foreign_key_columns AS c1 ON c1.referenced_object_id = i.obj_id AND c1.constraint_column_id = 1 INNER JOIN
dbo.vSYSChangeHistoryItemsD AS i2 ON c1.parent_object_id = i2.obj_id INNER JOIN
dbo.tblSYSChangeHistory_Details AS d1 ON d1.i_id = i.min_id AND d1.col_id = c1.referenced_column_id INNER JOIN
dbo.tblSYSChangeHistory_Details AS d1k ON d1k.i_id = i2.min_id AND d1k.col_id = c1.parent_column_id AND ISNULL(d1.new_val,
ISNULL(d1.prev_val, '')) = ISNULL(d1k.new_val, ISNULL(d1k.prev_val, '')) --LEFT OUTER JOIN
UNION ALL
SELECT i0.obj_id, i0.item_id, c01.parent_object_id AS rel_obj_id, i02.item_id AS rel_item_id
FROM dbo.vSYSChangeHistoryItemsD AS i0 INNER JOIN
sys.foreign_key_columns AS c01 ON c01.referenced_object_id = i0.obj_id AND c01.constraint_column_id = 1 AND col_name(c01.referenced_object_id,
c01.referenced_column_id) = 'ID' INNER JOIN
dbo.vSYSChangeHistoryItemsD AS i02 ON c01.parent_object_id = i02.obj_id INNER JOIN
dbo.tblSYSChangeHistory_Details AS d01k ON i02.min_id = d01k.i_id AND d01k.col_id = c01.parent_column_id AND ISNULL(d01k.new_val,
d01k.prev_val) = CAST(i0.item_id AS varchar(max))
And finally, that view uses one more view:
ALTER VIEW [dbo].[vSYSChangeHistoryItemsD]
AS
SELECT h.obj_id, m.item_id, MIN(m.id) AS min_id
FROM dbo.tblSYSChangeHistory AS h INNER JOIN
dbo.tblSYSChangeHistory_Items AS m ON h.id = m.h_id
GROUP BY h.obj_id, m.item_id
Working with the Profiler, it appears that view vSYSChangeHistoryFK is the big culprit, and my testing suggests that the particular problem is in the join between the two copies of vSYSChangeHistoryItemsD and the foreign_key_columns table.
I'm looking for any ideas on how to give acceptable performance here. The client reports sometimes waiting as much as 15 minutes without getting results. I've tested up to nearly 10 minutes with no result in at least one case.
If there were new language elements in 2008 or later that would solve this, I think the client would be willing to upgrade.
Thanks.
Wow that's a mess. Your big gain should be in removing the cursor. I see 'where exists' - that's nice and efficient b/c as soon as it finds one match it aborts. And I see 'where not exists' - by definition that has to scan everything. Is it finding the top 4? You can do better with using ROW_NUMBER() OVER (PARTITON BY [whatever makes it unique] ORDER BY [whatever your id is]. It's hard to tell. select object_id(obj_name), #iObj_id, 0 makes it seem like only the #i=1 loop actually does anything (?)
If that is what it's doing, you could write it as
SELECT * from
(
select ROW_NUMBER() OVER (PARTITION BY obj_id ORDER BY item_id desc) as Row,
obj_id, item_id
FROM bo.vSYSChangeHistoryFK a with (nolock)
where obj_type = 'U' and descr = cast(#cPrev_val AS varchar(150))
) paged
where Row between 1 and 4
ORDER BY Row
A DBA level change that could help would be to set up a partitioning scheme based on date. Roll over to a new partition every so often. Put the old partitions on different disks. Most queries may only need to hit the recent partition, which will be say 1/5th the size that it used to be, making it much faster without changing anything else.
Not a full answer, sorry. That mess would take hours to parse

How to get a table storage information

SQL Server Edition: SQL Server 2005 w/ SP3 and 2008
Is there a built-in SQL Server stored procedures that will retrieve following information?
Or a DMV (Dynamic Management View) would be great as well.
I am interested mainly on how to find out FILEGROUP data of a table specifically.
But it'd be better if there was a sproc that will return all of following result.
By the way, is there any documents that shows one-to-one matching of how to retrieve data that SQL Server UI displays?
The system stored procedure sp_help could be a good starting point.
For example:
exec sp_help 'schema.TableName'
This will show you all kinds of goodness:
-- Script to analyze table space usage using the
-- output from the sp_spaceused stored procedure
-- Works with SQL 7.0, 2000, and 2005
set nocount on
print 'Show Size, Space Used, Unused Space, Type, and Name of all database files'
select
[FileSizeMB] =
convert(numeric(10,2),sum(round(a.size/128.,2))),
[UsedSpaceMB] =
convert(numeric(10,2),sum(round(fileproperty( a.name,'SpaceUsed')/128.,2))) ,
[UnusedSpaceMB] =
convert(numeric(10,2),sum(round((a.size-fileproperty( a.name,'SpaceUsed'))/128.,2))) ,
[Type] =
case when a.groupid is null then '' when a.groupid = 0 then 'Log' else 'Data' end,
[DBFileName] = isnull(a.name,'*** Total for all files ***')
from
sysfiles a
group by
groupid,
a.name
with rollup
having
a.groupid is null or
a.name is not null
order by
case when a.groupid is null then 99 when a.groupid = 0 then 0 else 1 end,
a.groupid,
case when a.name is null then 99 else 0 end,
a.name
create table #TABLE_SPACE_WORK
(
TABLE_NAME sysname not null ,
TABLE_ROWS numeric(18,0) not null ,
RESERVED varchar(50) not null ,
DATA varchar(50) not null ,
INDEX_SIZE varchar(50) not null ,
UNUSED varchar(50) not null ,
)
create table #TABLE_SPACE_USED
(
Seq int not null
identity(1,1) primary key clustered,
TABLE_NAME sysname not null ,
TABLE_ROWS numeric(18,0) not null ,
RESERVED varchar(50) not null ,
DATA varchar(50) not null ,
INDEX_SIZE varchar(50) not null ,
UNUSED varchar(50) not null ,
)
create table #TABLE_SPACE
(
Seq int not null
identity(1,1) primary key clustered,
TABLE_NAME SYSNAME not null ,
TABLE_ROWS int not null ,
RESERVED int not null ,
DATA int not null ,
INDEX_SIZE int not null ,
UNUSED int not null ,
USED_MB numeric(18,4) not null,
USED_GB numeric(18,4) not null,
AVERAGE_BYTES_PER_ROW numeric(18,5) null,
AVERAGE_DATA_BYTES_PER_ROW numeric(18,5) null,
AVERAGE_INDEX_BYTES_PER_ROW numeric(18,5) null,
AVERAGE_UNUSED_BYTES_PER_ROW numeric(18,5) null,
)
declare #fetch_status int
declare #proc varchar(200)
select #proc = rtrim(db_name())+'.dbo.sp_spaceused'
declare Cur_Cursor cursor local
for
select
TABLE_NAME =
rtrim(TABLE_SCHEMA)+'.'+rtrim(TABLE_NAME)
from
INFORMATION_SCHEMA.TABLES
where
TABLE_TYPE = 'BASE TABLE'
order by
1
open Cur_Cursor
declare #TABLE_NAME varchar(200)
select #fetch_status = 0
while #fetch_status = 0
begin
fetch next from Cur_Cursor
into
#TABLE_NAME
select #fetch_status = ##fetch_status
if #fetch_status <> 0
begin
continue
end
truncate table #TABLE_SPACE_WORK
insert into #TABLE_SPACE_WORK
(
TABLE_NAME,
TABLE_ROWS,
RESERVED,
DATA,
INDEX_SIZE,
UNUSED
)
exec #proc #objname =
#TABLE_NAME ,#updateusage = 'true'
-- Needed to work with SQL 7
update #TABLE_SPACE_WORK
set
TABLE_NAME = #TABLE_NAME
insert into #TABLE_SPACE_USED
(
TABLE_NAME,
TABLE_ROWS,
RESERVED,
DATA,
INDEX_SIZE,
UNUSED
)
select
TABLE_NAME,
TABLE_ROWS,
RESERVED,
DATA,
INDEX_SIZE,
UNUSED
from
#TABLE_SPACE_WORK
end --While end
close Cur_Cursor
deallocate Cur_Cursor
insert into #TABLE_SPACE
(
TABLE_NAME,
TABLE_ROWS,
RESERVED,
DATA,
INDEX_SIZE,
UNUSED,
USED_MB,
USED_GB,
AVERAGE_BYTES_PER_ROW,
AVERAGE_DATA_BYTES_PER_ROW,
AVERAGE_INDEX_BYTES_PER_ROW,
AVERAGE_UNUSED_BYTES_PER_ROW
)
select
TABLE_NAME,
TABLE_ROWS,
RESERVED,
DATA,
INDEX_SIZE,
UNUSED,
USED_MB =
round(convert(numeric(25,10),RESERVED)/
convert(numeric(25,10),1024),4),
USED_GB =
round(convert(numeric(25,10),RESERVED)/
convert(numeric(25,10),1024*1024),4),
AVERAGE_BYTES_PER_ROW =
case
when TABLE_ROWS <> 0
then round(
(1024.000000*convert(numeric(25,10),RESERVED))/
convert(numeric(25,10),TABLE_ROWS),5)
else null
end,
AVERAGE_DATA_BYTES_PER_ROW =
case
when TABLE_ROWS <> 0
then round(
(1024.000000*convert(numeric(25,10),DATA))/
convert(numeric(25,10),TABLE_ROWS),5)
else null
end,
AVERAGE_INDEX_BYTES_PER_ROW =
case
when TABLE_ROWS <> 0
then round(
(1024.000000*convert(numeric(25,10),INDEX_SIZE))/
convert(numeric(25,10),TABLE_ROWS),5)
else null
end,
AVERAGE_UNUSED_BYTES_PER_ROW =
case
when TABLE_ROWS <> 0
then round(
(1024.000000*convert(numeric(25,10),UNUSED))/
convert(numeric(25,10),TABLE_ROWS),5)
else null
end
from
(
select
TABLE_NAME,
TABLE_ROWS,
RESERVED =
convert(int,rtrim(replace(RESERVED,'KB',''))),
DATA =
convert(int,rtrim(replace(DATA,'KB',''))),
INDEX_SIZE =
convert(int,rtrim(replace(INDEX_SIZE,'KB',''))),
UNUSED =
convert(int,rtrim(replace(UNUSED,'KB','')))
from
#TABLE_SPACE_USED aa
) a
order by
TABLE_NAME
print 'Show results in descending order by size in MB'
select * from #TABLE_SPACE order by USED_MB desc
go
drop table #TABLE_SPACE_WORK
drop table #TABLE_SPACE_USED
drop table #TABLE_SPACE
Found a solution.
It seems like it takes longer to type this out than using UI to find out table FILEGROUP information.
Found through List tables in filegroups:
declare #objectid bigint
set #objectid = object_id('table_name')
exec sp_objectfilegroup #objectid
I became too lazy to type those three lines so ended up creating another stored procedure that takes table name instead.
create procedure spTableFileGroup
#TableName sysname
as
begin
if exists( select 1
from INFORMATION_SCHEMA.TABLES T
where T.TABLE_NAME = #TableName) begin
declare #objectid bigint
set #objectid = object_id(#TableName)
exec sp_objectfilegroup #objectid
end
else begin
print 'There is no table named "' + #TableName + '"'
end
end
GO
Usage
exec spTableFileGroup 'table_name'
GO
Have a look at DBCC showfilestats or sp_spaceused for filegroups.
Found a script at a blog. That lists tables and their sizes.
For a more user friendly (administrative view, you can generate reports using right mouse on the db).
The tables FILEGROUP is determined by it's clustered index. You can use this query to find the filegroup:
SELECT *
FROM
sys.tables AS tbl
INNER JOIN sys.indexes AS idx ON idx.object_id = tbl.object_id and idx.index_id < 2
LEFT OUTER JOIN sys.data_spaces AS dsidx ON dsidx.data_space_id = idx.data_space_id
In regard to your second question, I don't think there's any documentation, however, you can use SQL profiler when you view the details in SSMS. This will show you the exact queries.
This might do the trick ->
use Your_database_name
GO
SELECT FG.*, MF.* FROM sys.filegroups FG INNER JOIN sys.master_files
MF on MF.data_space_id = FG.data_space_id WHERE database_id = db_id()

Resources