views:

132

answers:

4

Given a database, how can I retrieve a list of Tables along with the primary key of each table?

Thanks.

Edit: This is for a code generation tool I'm making, I need to somehow run the SQL script using C# only.

+1  A: 

Here's a start (SQL 2005 and up):

SELECT ta.name TableName, ind.name IndexName
 from sys.tables ta
  left outer join sys.indexes ind
   on ind.object_id = ta.object_id
    and ind.is_primary_key = 1

Tables without primary keys have IndexName set to Null.

But this merely lists the name of the primary key. Do you need a list of the column(s) in the key?

-- ADDED ----------------

Here's a part of a utility I once wrote. This will list all tables, and columns in order of all existing primary keys:

SELECT ta.name TableName, ind.name IndexName, indcol.key_ordinal IndexColumnOrder, col.name ColumnName
 from sys.tables ta
  left outer join sys.indexes ind
   on ind.object_id = ta.object_id
    and ind.is_primary_key = 1
  left outer join sys.index_columns indcol
   on indcol.object_id = ta.object_id
    and indcol.index_id = ind.index_id
  left outer join sys.columns col
   on col.object_id = ta.object_id
    and col.column_id = indcol.column_id
 order by
   ta.Name
  ,indcol.key_ordinal

Hacking that, the following will list all (and only) tables with primary keys of only one column:

SELECT ta.name TableName, max(col.name) ColumnName
 from sys.tables ta
  inner join sys.indexes ind
   on ind.object_id = ta.object_id
    and ind.is_primary_key = 1
  inner join sys.index_columns indcol
   on indcol.object_id = ta.object_id
    and indcol.index_id = ind.index_id
  inner join sys.columns col
   on col.object_id = ta.object_id
    and col.column_id = indcol.column_id
 group by ta.name
 having count(*) = 1
 order by ta.Name

As for transforming this to C# (not my forte), you should be able to either make this a stored procedure, or just build and submit the query from within your code.

Philip Kelley
Imagine the Person table has the ID columns as primary key. I need the exact name of that column. I also need to somehow invoke this inside of C# code. How can I do this?
Serg
Can you reference the PK columns by ordinal position (0,1) instead of name (ID, type)?
Beth
+1  A: 

Here's one answer:

select 
    t.Table_Name,
    tc.Constraint_Name,
    ccu.Column_Name
from
    information_schema.tables t
    left join information_schema.table_constraints tc
        on tc.Table_Catalog = t.Table_Catalog
        and tc.Table_Name = t.Table_Name
        and tc.Constraint_Type = 'PRIMARY KEY'
    left join information_schema.constraint_column_usage ccu
        on ccu.Table_Catalog = tc.Table_Catalog
        and ccu.Table_Name = tc.Table_Name
        and ccu.Constraint_Schema = tc.Constraint_Schema
        and ccu.Constraint_Name = tc.Constraint_Name
order by
    t.Table_Name,
    tc.Constraint_Name,
    ccu.Column_Name

This will list tables, their primary key constraints, and the columns in those constraints. Note that if a primary key has multiple columns, there will be multiple entries for that column. Also note that this query returns views, too.

Paul Williams
I need C#, not TSQL.
Serg
The user is not running TSQL commands; your C# code is. What is your objection to creating a SQL connection and using this as the SQL command in C#? THere is no SQL injection possibility here.
Paul Williams
A: 

You use the SQLCommand object to execute t-sql against a database..

Documented here with many examples in c# and other languages: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx

Or do you mean directly by using the CLR built into SQL server?

Adrian
I can't have the end user running TSQL commands, I need to use pure C#.
Serg
Do you understand the relationship between SQL server and the dotnet framework? SQLCommand is fundamental - just about ALL the interactions between ANY dotnet application/framwork and SQL server happen through that object...Please explain how you imagine an application to interact with a database!!!!!!I've decided already not to buy your 'product'
Adrian
It's very possible. Executing a custom gathering thing is not doable, however there is a MS created library that gets database information already. You can get just about any non-data information you want about a database without writing any T-SQL yourself.
Rangoric
I'm not 100% on this, obviously...but isn't SMO a layer(s) over the top of SQLCommand anyway - in much the same way that SQL Manager generates scripts from dialogs and runs them. Having said that you are probably better of using SMO anyway as MS should update it if the schemas change
Adrian
@Rangoric You can avoid writing the T-SQL yourself, but there's still T-SQL being sent to the server via a SqlCommand. You could also use LINQ to query the master database if you wished, but that doesn't help either if we can't allow the end user to run anything that uses SqlCommand.
stevemegson
+5  A: 

This uses no T-SQL by itself. It generates it on its own but will not be as efficient as just using the exact, short T-SQL that will get the same information.

using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

public class LoadStuff
{
    string mDatabaseConnectionString = "Something";
    ...
    public void LoadDatabase(string tDatabaseName)
    {
        using (var vSqlConnection = new SqlConnection(mDatabaseConnectionString))
        {
            var vConnection = new ServerConnection(vSqlConnection);
            var vServer = new Server(vConnection);
            var vDatabase = vServer.Databases[tDatabaseName];
            var vTables = vDatabase.Tables;
        }
    }
}

Each of the Objects in the collection "vTables" will be a definition for a Table in that database called vDatabaseName.

That table will have a collection of columns, and the primary keys can be found be looping through the "Columns" property of each table. Any columns in the primary key will have its property "InPrimaryKey" marked true.

Also you must get all information out of the various objects BEFORE the end of the using block. I went about making mini classes that had just the information I needed once the whole process was done, but you can probably just spit out the code from the code generation instead.

Rangoric
That depends on what you mean by "uses no T-SQL". There's no T-SQL in the code, but it's probably running a lot more T-SQL than the other answers which get everything we need in one query.
stevemegson
SMO would generate and run the required T-SQL code for you, and the app developer doesn't have to worry about the actual code, managing database version changes, SQL injection, etc. Once you toss the loops to extract the data you need from this, you end up with the same results... and if the *requirements* are "no T-SQL in the C# code", then this is what you need.
Philip Kelley
Yeah I was taking it as he couldn't use T SQL to so this. Or at a minimum he must use as much c# as possible to do it. It uses t SQL but the person using this code snippet is using only using code, no command strings or anything.
Rangoric
This is exactly, what I was looking for. I don't know why so many overzealous developers had their panties in a bunch because of my requirement. I REALIZE that everything runs TSQL (in this question) but the point was not bothering the end user with the details. Thanks!
Serg
@Rangoric - just curious as to what the 'v' prefix on your variables represents?
Piers Myers
@Piers I didn't use it throughout (except v which is very much out of habit) but I use the t Prefix for parameters, v for local variables and m for member variables. Also since it was mostly a Cut/Paste I kept them for this post.
Rangoric