Как получить таблицы и первичный ключ и отобразить нуль, если в таблице нет первичного ключа?

В настоящее время я пытаюсь получить имена таблиц и первичный ключ каждой таблицы, вызвав следующий SQL на С#. Это то, что я пробовал до сих пор:

SELECT t.TABLE_NAME As 'Table Name',
       Keys.COLUMN_NAME AS 'Primary Key'
FROM INFORMATION_SCHEMA.TABLES t 
left outer join INFORMATION_SCHEMA.TABLE_CONSTRAINTS Constraints
         on  t.TABLE_NAME = Constraints.Table_name 
         and t.Table_Schema = Constraints.Table_Schema 
left outer join INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS Keys 
         ON  Constraints.TABLE_NAME = Keys.TABLE_NAME 
         and Constraints.CONSTRAINT_NAME = Keys.CONSTRAINT_NAME 
         and Constraints.CONSTRAINT_TYPE = 'PRIMARY KEY'

Проблема в том, что я хотел бы, чтобы результат содержал только «нулевую» запись, если в таблице нет первичного ключа. Пока он работает, по какой-то причине результат содержит дубликат таблицы «Зарплата», как показано ниже. Обе таблицы Salary и Employees имеют первичные ключи, а таблица «Test» — нет:

Как избавиться от 3-й записи?

EDIT: я попробовал это сейчас, и, похоже, это работает. Есть ли недостатки в этом?

SELECT T.TABLE_NAME As 'Table Name'
    ,       (
            Select K1.COLUMN_NAME
            From INFORMATION_SCHEMA.TABLE_CONSTRAINTS As C1
                Join INFORMATION_SCHEMA.KEY_COLUMN_USAGE As K1
                    On C1.TABLE_SCHEMA = K1.TABLE_SCHEMA
                        And C1.TABLE_NAME = K1.TABLE_NAME 
                        And C1.CONSTRAINT_NAME = K1.CONSTRAINT_NAME 
            Where C1.CONSTRAINT_TYPE = 'PRIMARY KEY'
                And T.TABLE_SCHEMA = C1.TABLE_SCHEMA 
                And T.TABLE_NAME = C1.TABLE_NAME 
            )As PrimaryKeyColumns
FROM INFORMATION_SCHEMA.TABLES As T 

person Dot NET    schedule 10.09.2012    source источник
comment
Есть ли у salary еще одно ограничение?   -  person Robert    schedule 10.09.2012
comment
Да, есть еще внешний ключ   -  person Dot NET    schedule 10.09.2012


Ответы (4)


Попробуйте заменить это условие Constraints.CONSTRAINT_TYPE = 'PRIMARY KEY' :

SELECT t.TABLE_NAME As 'Table Name',
       Keys.COLUMN_NAME AS 'Primary Key'
FROM INFORMATION_SCHEMA.TABLES t 
left outer join INFORMATION_SCHEMA.TABLE_CONSTRAINTS Constraints
         on  t.TABLE_NAME = Constraints.Table_name 
         and t.Table_Schema = Constraints.Table_Schema  
         and Constraints.CONSTRAINT_TYPE = 'PRIMARY KEY'       
left outer join INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS Keys 
         ON  Constraints.TABLE_NAME = Keys.TABLE_NAME 
         and Constraints.CONSTRAINT_NAME = Keys.CONSTRAINT_NAME 
person Robert    schedule 10.09.2012

Вы должны учитывать, что ваш ПК имеет несколько столбцов. Вы можете сделать это, изменив на подзапрос:

SELECT T.TABLE_NAME As 'Table Name'
    ,   Stuff(
            (
            Select ', ' + K1.COLUMN_NAME
            From INFORMATION_SCHEMA.TABLE_CONSTRAINTS As C1
                Join INFORMATION_SCHEMA.KEY_COLUMN_USAGE As K1
                    On C1.TABLE_SCHEMA = K1.TABLE_SCHEMA
                        And C1.TABLE_NAME = K1.TABLE_NAME 
                        And C1.CONSTRAINT_NAME = K1.CONSTRAINT_NAME 
            Where C1.CONSTRAINT_TYPE = 'PRIMARY KEY'
                And T.TABLE_SCHEMA = C1.TABLE_SCHEMA 
                And T.TABLE_NAME = C1.TABLE_NAME 
            For Xml Path(''), type
            ).value('.', 'nvarchar(max)')
            , 1, 2, '') As PrimaryKeyColumns
FROM INFORMATION_SCHEMA.TABLES As T 

Кстати, если вы действительно хотите использовать соединения с представлением TABLES, вам нужно вложить соединения. Проблема в том, что у вас есть два внешних соединения, а столбцы из внешних ключей генерируют строки в вашем выводе. Следует отметить, что недостатком следующего подхода по сравнению с ранее упомянутым подходом с подзапросом является то, что вы получите несколько строк, если первичный ключ состоит из нескольких столбцов. Тем не менее, вы можете вкладывать соединения в SQL Server, чтобы он сначала обрабатывал внутреннее, внутреннее соединение, а затем внешнее соединение:

SELECT T.TABLE_NAME As 'Table Name'
    , Keys.COLUMN_NAME AS 'Primary Key'
FROM INFORMATION_SCHEMA.TABLES As T
    Left Join( INFORMATION_SCHEMA.TABLE_CONSTRAINTS Constraints
        Join INFORMATION_SCHEMA.KEY_COLUMN_USAGE As Keys 
            On  Constraints.TABLE_SCHEMA = Keys.TABLE_SCHEMA 
                And Constraints.TABLE_NAME = Keys.TABLE_NAME 
                And Constraints.CONSTRAINT_NAME = Keys.CONSTRAINT_NAME 
                And Constraints.CONSTRAINT_TYPE = 'PRIMARY KEY' )
        On T.TABLE_SCHEMA = Constraints.TABLE_SCHEMA 
            And T.TABLE_NAME = Constraints.TABLE_NAME 

В этом запросе сначала обрабатывается внутреннее соединение между TABLE_CONSTRAINTS и KEY_COLUMN_USAGE, а результаты объединяются в представлении левое соединение с TABLES.

person Thomas    schedule 10.09.2012
comment
Могу я спросить, для чего нужна часть nvarchar(max)? не совсем понял запрос - person Dot NET; 10.09.2012
comment
@DotNET — For Xml Path преобразует запрос в поток Xml. Бит , type).value('.','nvarchar(max)') гарантирует, что результат правильно обрабатывает сущности Xml (например, не возвращает &amp вместо &). Наконец, функция Stuff используется для удаления первого разделителя, состоящего из двух символов: запятой и пробела. - person Thomas; 10.09.2012
comment
Случится ли что-нибудь, если я удалю эту часть? Пожалуйста, взгляните на мое редактирование вопроса, чтобы увидеть, что я пробовал. - person Dot NET; 10.09.2012
comment
@DotNET. Если вы достаточно сумасшедшие, чтобы иметь запрещенный символ Xml в имени столбца, например <, > или &, вы получите в результате объект Xml (например, &lt;, &gt; или &amp;). - person Thomas; 10.09.2012

Я вижу, вы уже приняли свой ответ, но вот чистый ответ С# на случай, если кому-то интересно:

static void Main(string[] args)
{
    Server server = new Server("serverName");
    Database db = server.Databases["DatabaseName"];

    string tableName = "TableName";

    Table table = db.Tables[tableName];

    if (table != null)
    {
        Console.WriteLine("Table:  {0}", tableName);
        if (table.Columns.Count > 0)
        {
            Console.WriteLine("  Primary Key Columns:");
            foreach (Column column in table.Columns)
            {
                if (column.InPrimaryKey)
                {
                    Console.WriteLine(string.Format("    {0}", column.Name));
                }
            }
        }
        else
        {
            Console.WriteLine("  No primary key.", tableName);
        }
    }
    Console.WriteLine("Press ENTER to exit...");
    Console.ReadLine();
}

Вам потребуется добавить ссылки на сборки Microsoft.SqlServer.Management.Sdk.Sfc и Microsoft.SqlServer.Smo и импортировать соответствующие пространства имен.

person Chris Dunaway    schedule 10.09.2012

person    schedule
comment
Это не то, о чем он просит. Он хочет имя таблицы и имя столбца в PK таблицы. Если в таблице нет PK, просто отобразите null. - person Josh; 10.09.2012