Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2000 Forums
 SQL Server Development (2000)
 column and pk info from metadata tables

Author  Topic 

chacha
Starting Member

39 Posts

Posted - 2004-06-20 : 18:08:45
Hi y'all I'm new to the forums here at sqlteam.

I am writing a code generator for work. Nothing fancy, but it needs to convert sqlserver tables to c# classes which will play nice with our proprietary framework. I have almost got this down but I have run into a small problem...

when querying the metadata tables in sqlserver I currently do this...

ALTER procedure dev_get_column_info as

select
t_obj.name as [TableName],
col.name as [ColumnName],
typ.name as [Type],
col.length as [Length],
col.isnullable as [Nullable],
col.colid as [OrdinalPosition],
pkc.name as [PKColumnName]
from
sysobjects t_obj
inner join syscolumns col on col.id = t_obj.id
inner join systypes typ on typ.xtype = col.xtype
left outer join sysindexkeys k on k.id = t_obj.id
left outer join syscolumns pkc on (
pkc.id = k.id
and pkc.colid = k.colid
)
where
t_obj.xtype = 'U'
/*and t_obj.name LIKE 'bk_ac_%'*/
order by
[TableName], [OrdinalPosition]

... which gives me the list of all the column info for all our user tables as well as some basic info on primary keys. the problem I have is that some tables have composite keys, and so when I run this query I only get the name of one of the primary keys on the table. What I would really like (but don't know how to structure) is to do some sort of subselect of all the primary key fields associated with this table, and if the column name for the current column is found in the subselect query then we know that column is a primary key.

So the result of my query would be like
'tableName', 'pkID', 'int', '11', 'NULL', '1', 'IS PRI KEY'
rather than
'tableName', 'pkID', 'int', '11', 'NULL', '1', 'pkID'
which is what I get now, which gets screwed up if the table is a join table with a composite key.

Thanks for the help.

nr
SQLTeam MVY

12543 Posts

Posted - 2004-06-20 : 18:21:52
Not sure what you want. Can you give an example.

This will give the PK columns on a table - you can join to this if that's what you need.
http://www.nigelrivett.net/ColumnsInPK.html


==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

chacha
Starting Member

39 Posts

Posted - 2004-06-20 : 18:24:40
Thanks for the fast reply. This is akin to what I want, but it doesn't work...

ALTER procedure dev_get_column_info as

select
t_obj.name as [TableName],
col.name as [ColumnName],
typ.name as [Type],
col.length as [Length],
col.isnullable as [Nullable],
col.colid as [OrdinalPosition],
if col.name in (
select pkc.name
from sysindexkeys k, syscolumns pkc
where pkc.id = k.id
and pkc.colid = k.colid
and k.id = t_obj.id
)
begin
select [IsPK] = 1
end
else
begin
select [IsPK] = 0
end
from
sysobjects t_obj
inner join syscolumns col on col.id = t_obj.id
inner join systypes typ on typ.xtype = col.xtype
--left outer join sysindexkeys k on k.id = t_obj.id -- any keys
--left outer join syscolumns pkc on ( -- the column info for the key
-- pkc.id = k.id
-- and pkc.colid = k.colid
--)
where
t_obj.xtype = 'U'
/*and t_obj.name LIKE 'bk_ac_%'*/
order by
[TableName], [OrdinalPosition]
GO
Go to Top of Page

chacha
Starting Member

39 Posts

Posted - 2004-06-20 : 18:31:53
For the record, I use this to get the Primary keys


ALTER procedure dev_get_table_primary_key (
@table_name varchar(50)
) as
select o.name as [tablename], c.*
from
sysindexkeys k
inner join sysobjects o on o.id = k.id
inner join syscolumns c on c.id = o.id and c.colid = k.colid
where
o.xtype = 'U'
and o.name = RTRIM(LTRIM(@table_name))


GO
Go to Top of Page

kselvia
Aged Yak Warrior

526 Posts

Posted - 2004-06-20 : 21:53:05
Not an answer but a pointer:

set statistics profile on

sp_MShelpindex tablename
or
sp_help tablename

those procedures return the metadata. You might look at them to see how they do it. I notice the order (asc/desc) of indexes is also returned so you might need to retrieve that as well.
Go to Top of Page

chacha
Starting Member

39 Posts

Posted - 2004-06-20 : 22:15:56
as you say, not really an answer, but I will take a look. What I really need help on is the sql syntax to do something like this...

select
t_obj.name as [TableName],
col.name as [ColumnName],
typ.name as [Type],
col.length as [Length],
col.isnullable as [Nullable],
col.colid as [OrdinalPosition],
if col.name in (
select
pkc.name
from
sysindexkeys k, syscolumns pkc
where
pkc.id = k.id
and pkc.colid = k.colid
and k.id = t_obj.id
)
begin
select [IsPK] = 1
end
else
begin
select [IsPK] = 0
end
from
sysobjects t_obj
inner join syscolumns col on col.id = t_obj.id
inner join systypes typ on typ.xtype = col.xtype
where
t_obj.xtype = 'U'
order by
[TableName], [OrdinalPosition]

Do I have to replace the if/else construct with a case construct? If so, can I use an 'in (select...' construct as the test for a case? Then use the boolean result to toggle the appropriate column value?
Go to Top of Page

nr
SQLTeam MVY

12543 Posts

Posted - 2004-06-20 : 22:34:48
Showing code that doesn't giv what you want doesn't really help us to understand what you do want.
How about some sample output?

One problem might be the code you are using to get the PK - that won't just get the PK but will get other indexes and statistics too.
Have a look at sysindexes. sysindexkeys references the columns for all indexes there via indid.
I believe status 2048 is for primary keys in sysindexes but that's internal data so it's better to use the information_schema views.


If you just want to flag primary key columns then something like

select col.COLUMN_NAME, case when c.COLUMN_NAME is not null then 'yes' else 'no' end
from INFORMATION_SCHEMA.COLUMNS col
left join INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk
on col.TABLE_NAME = pk.TABLE_NAME
and CONSTRAINT_TYPE = 'PRIMARY KEY'
left join INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
on c.TABLE_NAME = pk.TABLE_NAME
and c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
and col.COLUMN_NAME = c.COLUMN_NAME
where col.TABLE_NAME = @tbl


==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

nr
SQLTeam MVY

12543 Posts

Posted - 2004-06-20 : 22:37:22
With the code you have there yes you would have to replace the if with a case.
In t-sql if is a control of flow statement and doesn't appear in queries.

==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2004-06-21 : 00:29:37
quote:
Originally posted by chacha

I am writing a code generator for work. Nothing fancy, but it needs to convert sqlserver tables to c# classes which will play nice with our proprietary framework.

The folk at http://www.ericjsmith.net/codesmith/ may have what you want, or something that you can more easily adapt than making your own. All for free too!

Kristen
Go to Top of Page

chacha
Starting Member

39 Posts

Posted - 2004-06-21 : 01:02:41
No, that won't work. Sorry, actually what we are doing is more involved than what I originally let on, I just want to focus on getting what I need out of the sql for now. Suffice it to say, there is no tool available to do what we need. I will have to be custom built. I just want to know how I can use the sysXXX tables to build a query that...

- gets the column name, type, maxlength, table name, nullable, ordinal position, whether the column is included in it's parent table's primary key definition, and if so, is it an identity field

I think my above examples are on the right track. I just don't know how to structure that subselect and the case statement in the select query. For each tuple returned I want to check whether the column name or colid is included in the table's primary key fields, so I can set a bit or flag.
Go to Top of Page

chacha
Starting Member

39 Posts

Posted - 2004-06-21 : 01:19:49
I figured it out. This does exactly what I want...


select
t_obj.name as [TableName],
col.name as [ColumnName],
typ.name as [Type],
col.length as [Length],
col.isnullable as [Nullable],
col.colid as [OrdinalPosition],
CASE
WHEN col.name IN(
select pkc.name
from sysindexkeys k, syscolumns pkc
where pkc.id = k.id
and pkc.colid = k.colid
and k.id = t_obj.id
) THEN 1
ELSE 0
END as [IsPK]
from
sysobjects t_obj
inner join syscolumns col on col.id = t_obj.id
inner join systypes typ on typ.xtype = col.xtype
--left outer join sysindexkeys k on k.id = t_obj.id -- any keys
--left outer join syscolumns pkc on ( -- the column info for the key
-- pkc.id = k.id
-- and pkc.colid = k.colid
--)
where
t_obj.xtype = 'U'
/*and t_obj.name LIKE 'bk_ac_%'*/
order by
[TableName], [OrdinalPosition]
Go to Top of Page

kselvia
Aged Yak Warrior

526 Posts

Posted - 2004-06-21 : 01:35:24
Yeah sorry, I was just about to post that very thing. We really dropped the ball and over analyzed the question instead of just answering what you asked :(
Go to Top of Page

nr
SQLTeam MVY

12543 Posts

Posted - 2004-06-21 : 07:40:12
Did you read my post about that not giving PK cols? Don't think it does anyway.

==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

chacha
Starting Member

39 Posts

Posted - 2004-06-21 : 13:59:20
quote:
Originally posted by nr

Did you read my post about that not giving PK cols? Don't think it does anyway.



Currently only primary keys are being returned and the results on a complex schema of ~100 tables is perfect. But I think you are right, we just haven't indexed tables yet, b/c there is not data in the tables at this time. But if I really want to make sure that only pk's are returned into the set I could just join sysindexes and sysobjects to sysindexkeys, restricting the join to sysobjects where xtype = 'PK', right?
Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2004-06-21 : 14:19:47
Sorry, been a bit busy and not enough time to properly understand your issue, but if its any help here's what I use to get "schema" data about a table

USE Northwind
GO

-- SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name

DECLARE @strTable varchar(256)
SELECT @strTable = 'Order Details'

SELECT C.COLUMN_NAME,
C.ORDINAL_POSITION,
C.DATA_TYPE,
C.CHARACTER_MAXIMUM_LENGTH,
C.NUMERIC_PRECISION,
C.NUMERIC_SCALE,
[PKID] = KCU.ordinal_position,
[IsIdentity] = 0,
[IsCalculated] = 0,
[IsNullable] = 0,
[DefaultValue] = CONVERT(varchar(7000), NULL)
INTO #TEMP_TABLE_02
FROM INFORMATION_SCHEMA.TABLES T
JOIN INFORMATION_SCHEMA.COLUMNS C
ON C.TABLE_CATALOG = T.TABLE_CATALOG
AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
AND C.TABLE_NAME = T.TABLE_NAME
LEFT OUTER JOIN INFORMATION_SCHEMA.table_constraints TC
ON TC.TABLE_CATALOG = T.TABLE_CATALOG
AND TC.TABLE_SCHEMA = T.TABLE_SCHEMA
AND TC.TABLE_NAME = T.TABLE_NAME
AND TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
LEFT OUTER JOIN INFORMATION_SCHEMA.key_column_usage KCU
ON KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG
AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA
AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
AND KCU.COLUMN_NAME = C.COLUMN_NAME
WHERE T.TABLE_NAME = @strTable
ORDER BY T.TABLE_NAME, C.ORDINAL_POSITION

UPDATE T1
SET [IsIdentity] = CASE WHEN C.colstat = 1 THEN 1 ELSE 0 END,
[IsCalculated] = CASE WHEN C.colstat = 4 THEN 1 ELSE 0 END,
[IsNullable] = C.isnullable,
[DefaultValue] = CONVERT(varchar(7000), COM.[text])
FROM #TEMP_TABLE_02 T1
JOIN SYSOBJECTS O
ON O.name = @strTable
AND O.Type = 'U'
JOIN dbo.syscolumns C
ON O.id = C.ID
AND C.Name = COLUMN_NAME
LEFT OUTER JOIN syscomments COM
ON COM.ID = C.cdefault

SELECT * FROM #TEMP_TABLE_02 ORDER BY ORDINAL_POSITION

GO
DROP TABLE #TEMP_TABLE_02
GO

Sorry if it is answreing the wrong question! I've got one to resolve Foreign Keys somewhere, if you need it.

Kristen
Go to Top of Page

chacha
Starting Member

39 Posts

Posted - 2004-06-21 : 16:33:53
That is great Kristen. My code was working properly but your post contains exactly where I was going with this query. I eventually needed the other three key pieces of info you supplied - to know whether the pk columns were identity or not, what the default values for the columns are, and whether the column is calculated or not.

Consider this thread resolved. Thanks.
Go to Top of Page

chacha
Starting Member

39 Posts

Posted - 2004-06-21 : 17:29:34
for the record, this is what I came up with...


select
t_obj.name as [TableName],
col.name as [ColumnName],
CASE
WHEN col.name IN(
select pkc.name
from sysindexkeys k, syscolumns pkc
where pkc.id = k.id
and pkc.colid = k.colid
and k.id = t_obj.id
) THEN 1
ELSE null
END as [IsPK],
CASE col.colstat
WHEN 1 then 1
ELSE 0
END as [IsIdentity],
typ.name as [Type],
col.length as [Length],
col.scale as [Scale],
col.prec as [Precision],
col.isnullable as [Nullable],
col.colid as [OrdinalPosition],
col.iscomputed as [IsComputed],
com.ctext as [SqlRule]
from
sysobjects t_obj
inner join syscolumns col on col.id = t_obj.id
inner join systypes typ on typ.xtype = col.xtype
left outer join syscomments com on (com.id = col.id and com.number = col.colid)
where
t_obj.xtype = 'U'
and t_obj.name LIKE 'bk_%'
order by
[TableName], [OrdinalPosition]

Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2004-06-22 : 00:46:27
quote:
Originally posted by chacha

I eventually needed the other three key pieces of info you supplied - to know whether the pk columns were identity or not, what the default values for the columns are, and whether the column is calculated or not

Sounds like you are probably doing exactly what I'm doing with it then! Glad you are now cooking on gas ...

Kristen
Go to Top of Page

nr
SQLTeam MVY

12543 Posts

Posted - 2004-06-22 : 05:21:03
quote:
Originally posted by chacha

quote:
Originally posted by nr

Did you read my post about that not giving PK cols? Don't think it does anyway.



Currently only primary keys are being returned and the results on a complex schema of ~100 tables is perfect. But I think you are right, we just haven't indexed tables yet, b/c there is not data in the tables at this time. But if I really want to make sure that only pk's are returned into the set I could just join sysindexes and sysobjects to sysindexkeys, restricting the join to sysobjects where xtype = 'PK', right?



You could do that. As it is you will also get statistics.
You could also use he sysindexes status. Note that microsoft are keen for people to use the information schema views where possible as these will be consistent across versions - and in this case it makes the query a lot simpler.

==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page
   

- Advertisement -