I'm working on a project that focuses on truncated tables in a server. The goal of the project is to record all truncated tables in the audit table. I've worked on a code below that works fine on a single database; however, I'm not able to run that code automatically on all databases. I've tried writing:
Cursor
Using Exec ('Use ' + #DatabaseName + ';') in the stored procedure.
Exec sp_MSforeachDB and the stored procedure.
Every single time I try to run the sproc for all databases it is executed on a database where the code exists. How can I execute it on all databases.
Main query:
DBCC TRACEON (2537) -- All Log Files
;WITH TranCte as ( SELECT
[Transaction Id]
,[Transaction Name]
,[Begin Time]
,SUSER_SNAME( [Transaction SID]) AS [User]
,[Operation] FROM fn_dblog (NULL, NULL) WHERE [Transaction Name] = 'TRUNCATE TABLE'),
ObjectCte AS
(SELECT
[Lock Information]
,[Transaction Name]
,[Transaction Id]
FROM fn_dblog (NULL, NULL)
WHERE [Transaction Id] IN (SELECT
[Transaction Id]
FROM fn_dblog(NULL, NULL)
WHERE [Transaction Name] = 'TRUNCATE TABLE' )
AND [Lock Information] LIKE '%SCH_M OBJECT%'),
ConvertStringToRows ( [Lock Information], starts, pos, [Transaction ID]) AS
(SELECT
[Lock Information]
,1
,CHARINDEX( ':', [Lock Information])
,[Transaction ID]
FROM ObjectCte UNION ALL SELECT
[Lock Information]
,pos + 1
,CHARINDEX( ':', [Lock Information], pos + 1)
,[Transaction ID]
FROM ConvertStringToRows
WHERE pos > 0 ),
ConvertStringToRowsDetails AS (SELECT
*
,RowN = ROW_NUMBER () OVER (PARTITION BY [Lock Information] , [Transaction ID] ORDER BY Starts , [Transaction ID] )
,ColumnType =
CASE ROW_NUMBER () OVER (PARTITION BY [Lock Information], [Transaction ID] ORDER BY Starts , [Transaction ID] )
WHEN 3 THEN 'Database'
WHEN 4 THEN 'Object'
END
,DetailsID = SUBSTRING ([Lock Information], starts, CASE
WHEN pos > 0 THEN pos - starts
ELSE LEN ([Lock Information])
END)
FROM ConvertStringToRows )
, CTE_Report
AS (SELECT
e.*
,ObjectID = ob .DetailsID
,DatabaseID = db .DetailsID
,o. [Lock Information]
,TableName = OBJECT_NAME (ob. DetailsID, db.DetailsID )
,[Database] = DB_NAME (db. DetailsID)
FROM TranCte e
JOIN ObjectCte o
ON e .[Transaction Id] = o .[Transaction Id]
JOIN ConvertStringToRowsDetails db
ON o .[Lock Information] = db .[Lock Information]
AND db .ColumnType = 'Database'
JOIN ConvertStringToRowsDetails ob
ON o .[Lock Information] = ob .[Lock Information]
AND ob .ColumnType = 'Object' )
SELECT * FROM CTE_Report
That code will reference the system tables for the database you're currently in every time, so will only show that database's data.
If you want to execute it across databases, you'll need to either put the code into each database and run it from there using one of the methods you've already tried, or create composite views and functions that aggregate the code from a specified list of databases, and read from them.
Or you could put all that code into dynamic SQL and EXEC it against a specified database inside your sproc. But I really wouldn't do that.
EDIT - Commenter is right. Ignore my earlier prejudice. Do the dynamic SQL thing. One of the very few SO questions where it's the right answer.
Related
This is my code for a query to produce a specific report. This works, however when I added the last select statement
(Select Name
from RPT_CUSTOM_LIST_VALUES
where CUSTOM_PROPERTY_VALUE_ID = RI1.CUST_10) AS [Application]
The RI1.Cust_10 column holds multiple values delimited by commas. How can I get it so that the look up table pulls each value and provides the correct name for that value? I cannot create or modify the tables within this database.
select
RI1.incident_id as [Project Incident #],
(Select Name from RPT_CUSTOM_LIST_VALUES
where CUSTOM_PROPERTY_VALUE_ID = RI1.CUST_02) as [Business Name],
RI1.NAME as [Project Name],
RI1.INCIDENT_STATUS_NAME as [Phase],
(Select Name from RPT_CUSTOM_LIST_VALUES
where CUSTOM_PROPERTY_VALUE_ID = RI1.CUST_09) as [Key Milestone Name],
convert(nvarchar(10), RI1.CUST_26,103) as [Key Milestone Date], -- leave as date
convert(nvarchar(10), RI1.CUST_29,103) as [Target Completion Date], -- leave as date
RI1.SEVERITY_NAME as [Status Color],
RI1.CUST_01 as [Status Summary],
RI1.OWNER_NAME as [IT Owner],
(Select Name from RPT_CUSTOM_LIST_VALUES
where CUSTOM_PROPERTY_VALUE_ID = RI1.CUST_10) AS [Application]
from
RPT_INCIDENTS RI1
where
RI1.PROJECT_ID = 445
and RI1.IS_DELETED = 0
and (RI1.INCIDENT_STATUS_NAME <> '5.1-Cancelled' and RI1.INCIDENT_STATUS_NAME <> '5.2-Completed')
My output should be, however, the last column should have names not values. The values are from the Lookup table and I need a way to pull that data so that the values are now names.
Report Output
I can't test without your data, but hopefully it will work for you:
select RI1.incident_id as [Project Incident #]
, [Business Name] = s1.Name
,RI1.NAME as [Project Name]
,RI1.INCIDENT_STATUS_NAME as [Phase]
, [Key Milestone Name] = s2.Name
,convert(nvarchar(10), RI1.CUST_26,103) as [Key Milestone Date] -- leave as date
,convert(nvarchar(10), RI1.CUST_29,103) as [Target Completion Date] -- leave as date
,RI1.SEVERITY_NAME as [Status Color]
,RI1.CUST_01 as [Status Summary]
,RI1.OWNER_NAME as [IT Owner]
, [Application] = LEFT(s3.App,LEN(APP) - SIGN(LEN(s3.APP)))
From RPT_INCIDENTS AS RI1
OUTER APPLY (Select TOP 1 i.Name from RPT_CUSTOM_LIST_VALUES as i where i.CUSTOM_PROPERTY_VALUE_ID = RI1.CUST_02) as s1
OUTER APPLY (Select TOP 1 i.Name from RPT_CUSTOM_LIST_VALUES as i where i.CUSTOM_PROPERTY_VALUE_ID = RI1.CUST_09) as s2
OUTER APPLY (SELECT App = (
Select i.Name + ','
from RPT_CUSTOM_LIST_VALUES as i
where i.CUSTOM_PROPERTY_VALUE_ID = RI1.CUST_10
FOR XML PATH(''))
) as s3
where RI1.PROJECT_ID = 445
and RI1.IS_DELETED = 0
and (RI1.INCIDENT_STATUS_NAME <> '5.1-Cancelled' and RI1.INCIDENT_STATUS_NAME <> '5.2-Completed')
How to get the SQL Server Activity Monitor's output using T-SQL???
In order to get exactly same output as Activity Monitor; I have created a following script.
If you use this script, you don't need sp_who2 or activity monitor to run.
The script that I have created will display the following things:
[Session ID]
[User Process]
[Login]
[Blocked By]
[Head Blocker]
[DatabaseName]
[Task State]
[Command]
[statement_text] --It will display the statement which is being executed presently.
[command_text] ----- It will display the Stored Procedure's Name.
[Total CPU (ms)]
'Elapsed Time (in Sec)'
[Wait Time (ms)]
[Wait Type]
[Wait Resource]
[Memory Use (KB)]
[Host Name]
[Net Address]
[Workload Group]
[Application]
My Activity Monitor's Script is as follows:
/* ACTIVITY MONITOR'S OUTPUT along with statement_text and command_text */ /* Processes */
SELECT [Session ID] = s.session_id,
[User Process] = CONVERT(CHAR(1), s.is_user_process),
[Login] = s.login_name,
[Blocked By] = ISNULL(CONVERT (varchar, w.blocking_session_id), ''),
[Head Blocker] =
CASE
-- session has an active request, is blocked, but is blocking others or session is idle but has an open tran and is blocking others
WHEN r2.session_id IS NOT NULL AND (r.blocking_session_id = 0 OR r.session_id IS NULL) THEN '1'
-- session is either not blocking someone, or is blocking someone but is blocked by another party
ELSE ''
END,
[DatabaseName] = ISNULL(db_name(r.database_id), N''),
[Task State] = ISNULL(t.task_state, N''),
[Command] = ISNULL(r.command, N''),
[statement_text] = Substring(st.TEXT, (r.statement_start_offset / 2) + 1,
( ( CASE r.statement_end_offset WHEN - 1 THEN Datalength(st.TEXT)
ELSE r.statement_end_offset
END - r.statement_start_offset ) / 2 ) + 1), ----It will display the statement which is being executed presently.
[command_text] =Coalesce(Quotename(Db_name(st.dbid)) + N'.' + Quotename(Object_schema_name(st.objectid, st.dbid)) + N'.' + Quotename(Object_name(st.objectid, st.dbid)), ''), -- It will display the Stored Procedure's Name.
[Total CPU (ms)] = r.cpu_time,
r.total_elapsed_time / (1000.0) 'Elapsed Time (in Sec)',
[Wait Time (ms)] = ISNULL(w.wait_duration_ms, 0),
[Wait Type] = ISNULL(w.wait_type, N''),
[Wait Resource] = ISNULL(w.resource_description, N''),
[Total Physical I/O (MB)] = (s.reads + s.writes) * 8 / 1024,
[Memory Use (KB)] = s.memory_usage * 8192 / 1024,
--[Open Transactions Count] = ISNULL(r.open_transaction_count,0),
--[Login Time] = s.login_time,
--[Last Request Start Time] = s.last_request_start_time,
[Host Name] = ISNULL(s.host_name, N''),
[Net Address] = ISNULL(c.client_net_address, N''),
-- [Execution Context ID] = ISNULL(t.exec_context_id, 0),
-- [Request ID] = ISNULL(r.request_id, 0),
[Workload Group] = N'',
[Application] = ISNULL(s.program_name, N'')
FROM sys.dm_exec_sessions s
LEFT OUTER JOIN sys.dm_exec_connections c ON (s.session_id = c.session_id)
LEFT OUTER JOIN sys.dm_exec_requests r ON (s.session_id = r.session_id)
LEFT OUTER JOIN sys.dm_os_tasks t ON (r.session_id = t.session_id
AND r.request_id = t.request_id)
LEFT OUTER JOIN
( -- In some cases (e.g. parallel queries, also waiting for a worker), one thread can be flagged as
-- waiting for several different threads. This will cause that thread to show up in multiple rows
-- in our grid, which we don't want. Use ROW_NUMBER to select the longest wait for each thread,
-- and use it as representative of the other wait relationships this thread is involved in.
SELECT *,
ROW_NUMBER() OVER (PARTITION BY waiting_task_address
ORDER BY wait_duration_ms DESC) AS row_num
FROM sys.dm_os_waiting_tasks ) w ON (t.session_id = w.session_id)
AND w.row_num = 1
LEFT OUTER JOIN sys.dm_exec_requests r2 ON (r.session_id = r2.blocking_session_id) OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) AS st
WHERE s.session_Id > 50 -- Ignore system spids.
ORDER BY s.session_id --,[Total CPU (ms)] desc ;
Not exactly sure what you are looking for, but this should give you something similar what you see on the activity monitor (not completely but similar).
SELECT
P.spid,
RIGHT(CONVERT(VARCHAR, DATEADD(MS, DATEDIFF(MS, P.last_batch, GETDATE()), '1900-01-01'), 121), 12) AS [BATCH_DURATION],
P.program_name,
P.hostname AS HOST_NAME,
P.loginame AS LOGIN_NAME
FROM master.dbo.sysprocesses AS P
WHERE
P.spid > 50 AND
P.status NOT IN ('background', 'sleeping') AND
P.cmd NOT IN
(
'AWAITING COMMAND',
'MIRROR HANDLER',
'LAZY WRITER',
'CHECKPOINT SLEEP',
'RA MANAGER'
)
ORDER BY 2
The reason we are looking for SPID > 50 is because processes that has ID smaller than 50 belongs to internal operations. Anything greater than 50 should be for user actions.
Also, you can see all the blockings etc. on a db, you could try something like this.
SELECT
db.name AS DB_NAME,
tl.request_session_id AS REQUESTING_SESSION,
wt.blocking_session_id AS BLOCKING_SESSION,
OBJECT_NAME(p.OBJECT_ID) AS BLOCKED_OBJECT,
tl.resource_type AS RESOURCE_TYPE,
h1.TEXT AS REQUEST_QUERY,
h2.TEXT AS BLOCKING_QUERY,
tl.request_mode
FROM sys.dm_tran_locks AS tl
INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id
INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id
INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
You can possibly combine these to get what exactly you are looking for.
Hope this helps.
Is there a way to query a database either by sql query or C# to get me a list or datatable of head blockers currently locking up the database?
Use the following SQL, replacing 'MyDatabaseName' with either; the name of your database, or the value, NULL.
DECLARE
#Database NVARCHAR(MAX) = 'MyDatabaseName'
SELECT
s.login_name AS [Login]
, ISNULL(db_name(p.dbid), N'') AS [Database]
, ISNULL(t.task_state, N'') AS [Task State]
, ISNULL(r.command, N'') AS [Command]
, s.cpu_time AS [Total CPU (ms)]
, (s.reads + s.writes) * 8 / 1024 AS [Total Physical I/O (MB)]
, s.memory_usage * 8192 / 1024 AS [Memory Use (KB)]
FROM
sys.dm_exec_sessions AS s
LEFT OUTER JOIN sys.dm_exec_requests AS r ON (s.session_id = r.session_id)
LEFT OUTER JOIN sys.dm_os_tasks AS t ON (r.session_id = t.session_id AND r.request_id = t.request_id)
LEFT OUTER JOIN sys.dm_exec_requests AS r2 ON (s.session_id = r2.blocking_session_id)
LEFT OUTER JOIN sys.sysprocesses AS p ON (s.session_id = p.spid)
WHERE
r2.session_id IS NOT NULL
AND (r.blocking_session_id = 0 OR r.session_id IS NULL)
AND (#Database IS NULL OR ISNULL(db_name(p.dbid), N'') = #Database)
ORDER BY
s.cpu_time DESC
Note: A value of NULL will check all databases within that server.
I have a query that pulls accurate data when ran on SSMS, but when I create a report using SSRS with the exact same query, it misses out results that come from one of two temp tables I use.
DECLARE #from int --= #fromparameter
DECLARE #to int --= #toparameter
/*
For debug
*/
set #from = 0
set #to = 50
/*
================================================================================
Build a temp table with all accounts that have a move out date within params
================================================================================
*/
IF OBJECT_ID('tempdb.dbo.#tempProperty', 'U') is not null drop table #tempProperty
select
sa.spark_AccountNumber
,sa.spark_PropertyIdName
into
#tempProperty
from
SparkCRM_MSCRM.dbo.spark_account sa
where
sa.spark_AccountNumber IN (
select distinct
sa.spark_AccountNumber
--,sa.spark_TenantMoveinDate
--,sa.CreatedOn
--,DATEDIFF(day,sa.spark_TenantMoveinDate,sa.CreatedOn) as [Difference]
from
SparkCRM_MSCRM.dbo.spark_account sa
where
sa.spark_TenantMoveinDate BETWEEN dateadd(DAY,#from,getdate()) AND dateadd(DAY,#to,getdate())
)
/*
================================================================================
--create CTE with all accounts per property
================================================================================
*/
--;with RowRanked (AccountNumber,Name,Rowrank,MoveinDate,MoveOotDate,SProperty,PProperty)
--AS
--(
IF OBJECT_ID('tempdb.dbo.#temp', 'U') is not null drop table #temp
SELECT
sa.spark_AccountNumber [Account Number]
,sa.spark_name [Account Name]
,ROW_NUMBER() OVER(PARTITION BY sa.spark_PropertyIDName ORDER BY COALESCE (sa.spark_TenantMoveinDate, sa.spark_agreementdate) DESC) [rowRank]
,COALESCE (sa.spark_TenantMoveinDate, sa.spark_agreementdate) [Tenant Move In Date]
,sa.spark_TenantMoveoutDate [Tenant Move Out Date]
,sa.spark_PropertyIdName [Property ID]
,p.spark_name [Property Name]
into #temp
FROM
SparkCRM_MSCRM.dbo.spark_property p
LEFT JOIN
SparkCRM_MSCRM.dbo.spark_account sa
on sa.spark_PropertyId = p.spark_propertyId
WHERE
sa.spark_PropertyIdName IN (SELECT spark_PropertyIdName from #tempProperty)
--)
/*
================================================================================
build final dataset
================================================================================
*/
select distinct
sa.spark_AccountNumber [Account Number]
,sa.spark_name [Name]
,concat (
sa.spark_HouseNumber ,' ',
sa.spark_HouseName ,' ',
sa.spark_Address1 ,' ',
sa.spark_Address2 ,' ',
sa.spark_Address3 ,' ',
sa.spark_Address4 ,' ',
sa.spark_Postcode
) [Address]
,sa.spark_Email [Email]
,sa.spark_HomePhone [Landline]
,sa.spark_Mobile [Mobile Number]
,COALESCE(a3.Name,a2.Name,a1.Name) [Letting Agent Partner]
,sa.spark_tariffidName [Tariff]
,sa.spark_PPMTariffName [PPM Tariff]
,pm.Option_Label [Payment Method]
,sa.spark_Balance [Account Balance]
,sa.spark_IntendedMoveOut [Date of Likely Move Out]
,sa.spark_TenantMoveoutDate [Current Tenant Move Out Date]
,rr.[Account Number] [New Account Number]
,rr.[Tenant Move In Date] [New Account Move In Date]
,case
when pc.spark_CallDriver is not null
then 'Yes'
else
'No'
end [Arrangement to Pay]
,ds.Option_Label [Stops]
from
SparkCRM_MSCRM.dbo.spark_account sa
--inner join
-- DBS.dbo.Meter m
-- on m.cust_ref = sa.spark_AccountNumber collate DATABASE_default
-- and m.meter_status = 2
left join
SparkCRM_MSCRM.dbo.spark_property sp
on sp.spark_propertyid = sa.spark_propertyid
left join
SparkCRM_MSCRM.dbo.account a1 --branch
on sp.spark_PartnerId = a1.accountid
left join
SparkCRM_MSCRM.dbo.account a2 --brand
on a1.parentaccountid = a2.accountid
left join
SparkCRM_MSCRM.dbo.account a3 --partner
on a2.parentaccountid = a3.accountid
left join
SparkCRM_Custom.dbo.GetCRMOptions('spark_account', 'spark_paymentmethod') pm
ON pm.Option_Value = sa.spark_paymentmethod
left join
SparkCRM_Custom.dbo.GetCRMOptions('spark_account','spark_DebtorStatus') ds
on ds.Option_Value = sa.spark_DebtorStatus
left join
SparkCRM_MSCRM.dbo.PhoneCall pc
on pc.spark_Account = sa.spark_accountId
and pc.spark_CallDriver = 101
left join
#temp rr
on rr.[Property ID] = sa.spark_PropertyIdName
and rr.Rowrank = 1
where
coalesce(
sa.spark_IntendedMoveOut
,sa.spark_TenantMoveoutDate
)
BETWEEN
dateadd(DAY,#from,getdate()) AND dateadd(DAY,#to,getdate())
and
sa.spark_name not like '%occupier%'
This returns data when I run the query in SQL Server Management Studio, but copying it into SSRS Report Builder seems to remove any results from the #temp table. You'll notice that I was originally use a CTE for the second table, but I tried using a temp table instead in case it SSRS was struggling with CTEs.
Any help would be much appreciated!
You should avoid using #temp tables in Reporting Services, if for no other reason that it will cause all sorts of trouble if people try to run the report concurrently.
It would be better for you to instead create a Stored Procedure that your Report can call. This will allow you to apply indexes and another performance modifications as needed, and it will behave exactly the same as running the query stand-alone in SSMS
I have some SQL I need to get working on SQL 2005/2008. The SQL is from SQL 2000 and uses some system objects to make it work.
master.dbo.spt_provider_types
master.dbo.syscharsets
systypes
syscolumns
sysobjects
I know SQL 2005 does no longer use system tables and I can get the same information from views, but I am looking for a solution that will work for both SQL 2000 and 2005/2008. Any ideas?
select top 100 percent
TABLE_CATALOG = db_name(),
TABLE_SCHEMA = user_name(o.uid),
TABLE_NAME = o.name,
COLUMN_NAME = c.name,
ORDINAL_POSITION = convert(int,
(
select count(*)
from syscolumns sc
where sc.id = c.id
AND sc.number = c.number
AND sc.colid <= c.colid
)),
IS_COMPUTED = convert(bit, c.iscomputed)
from
syscolumns c left join syscomments m on c.cdefault = m.id and m.colid = 1,
sysobjects o,
master.dbo.spt_provider_types d,
systypes t,
master.dbo.syscharsets a_cha /* charset/1001, not sortorder.*/
where
o.name = #table_name and
permissions(o.id, c.name) <> 0
and (o.type in ('U','V','S') OR (o.type in ('TF', 'IF') and c.number = 0))
and o.id = c.id
and t.xtype = d.ss_dtype
and c.length = case when d.fixlen > 0 then d.fixlen else c.length end
and c.xusertype = t.xusertype
and a_cha.type = 1001 /* type is charset */
and a_cha.id = isnull(convert(tinyint, CollationPropertyFromID(c.collationid, 'sqlcharset')),
convert(tinyint, ServerProperty('sqlcharset'))) -- make sure there's one and only one row selected for each column
order by 2, 3, c.colorder
) tbl where IS_COMPUTED = 0
you need to read this Microsoft doc: Mapping SQL Server 2000 System Tables to SQL Server 2005 System Views
EDIT based on OP's comments
for YourTable, my query here will list the:
schema.tablename
ColumnName
TableType
DataType
Nullable status
Identity, computed column, and check constraint info
this runs on SQL Server 2005, not sure about 2000.
SELECT
sh.name+'.'+o.name AS TableName
,s.name as ColumnName
,o.type_desc AS TableType
,CASE
WHEN t.name IN ('char','varchar') THEN t.name+'('+CASE WHEN s.max_length<0 then 'MAX' ELSE CONVERT(varchar(10),s.max_length) END+')'
WHEN t.name IN ('nvarchar','nchar') THEN t.name+'('+CASE WHEN s.max_length<0 then 'MAX' ELSE CONVERT(varchar(10),s.max_length/2) END+')'
WHEN t.name IN ('numeric') THEN t.name+'('+CONVERT(varchar(10),s.precision)+','+CONVERT(varchar(10),s.scale)+')'
ELSE t.name
END AS DataType
,CASE
WHEN s.is_nullable=1 THEN 'NULL'
ELSE 'NOT NULL'
END AS Nullable
,CASE
WHEN ic.column_id IS NULL THEN ''
ELSE ' identity('+ISNULL(CONVERT(varchar(10),ic.seed_value),'')+','+ISNULL(CONVERT(varchar(10),ic.increment_value),'')+')='+ISNULL(CONVERT(varchar(10),ic.last_value),'null')
END
+CASE
WHEN sc.column_id IS NULL THEN ''
ELSE ' computed('+ISNULL(sc.definition,'')+')'
END
+CASE
WHEN cc.object_id IS NULL THEN ''
ELSE ' check('+ISNULL(cc.definition,'')+')'
END
AS MiscInfo
FROM sys.objects o
INNER JOIN sys.columns s ON o.object_id=s.object_id
INNER JOIN sys.types t ON s.system_type_id=t.system_type_id and t.is_user_defined=0
INNER JOIN sys.schemas sh on o.schema_id=sh.schema_id
LEFT OUTER JOIN sys.identity_columns ic ON s.object_id=ic.object_id AND s.column_id=ic.column_id
LEFT OUTER JOIN sys.computed_columns sc ON s.object_id=sc.object_id AND s.column_id=sc.column_id
LEFT OUTER JOIN sys.check_constraints cc ON s.object_id=cc.parent_object_id AND s.column_id=cc.parent_column_id
WHERE sh.name='dbo' --schema name
AND o.name='YourTable' --table name
--AND s.name='YourColumn'
ORDER BY s.column_id
If you trying to make the query work in SQL 2000 and SQL 2005, then it should mostly work as is. Microsoft created compatibility views for the system tables specifically so that legacy code would not break. The one problem might be the spt_provider_types table. For that you will either need to use something else like the INFORMATION_SCHEMA views or you can recreate the spt_ tables by running a script in the installs folder on the SQL 2005 system called u_tables.sql.