2016-03-15 11 views
11

Potrzebowałem napisać zapytanie, w którym mogę uzyskać informacje o wszystkich kolumnach (z typem danych), a także wiedzieć, które z nich to PK/FK. W przypadku FK potrzebne są dodatkowe informacje, takie jak z drugiej tabeli. Mam kwerendę, która działa, ale wygląda trochę overkill.Zapytanie SQL o informacje dotyczące kolumny misc

Czy można to zrobić lepiej? Nie lubię sub-query dołącza w nim. To musi być zapytanie, nie można tego zrobić przez SP.

Mój przykład jest przeciwko Northwind (z pewnymi dodatkowymi FK relacji, że byłem testowania)

SELECT 
    t.name AS TableName, 
    t.object_id AS TableObjectId, 
    tCols.column_name AS ColumnName, 
    tCols.data_type AS ColumnDataType, 
    ISNULL(tCols.numeric_scale, 0) AS ColumnDecimalPlaces, 
    CASE tConstraints.CONSTRAINT_TYPE 
     WHEN 'PRIMARY KEY' 
      THEN '1' 
      ELSE '0' 
    END AS ISPK, 
    CASE tConstraints.CONSTRAINT_TYPE 
     WHEN 'FOREIGN KEY' 
      THEN '1' 
      ELSE '0' 
    END AS ISFK, 
    tConstraints.CONSTRAINT_TYPE, 
    tConstraints.CONSTRAINT_NAME, 
    fkInfo.FK_name, 
    fkInfo.PK_column, 
    fkInfo.PK_table, 
    fkInfo.PK_name 
FROM sys.objects t 
LEFT JOIN information_schema.columns tCols ON tCols.TABLE_NAME = t.name 
LEFT JOIN (
    SELECT 
     tc.CONSTRAINT_NAME, 
     tc.TABLE_NAME, 
     tc.CONSTRAINT_TYPE, 
     kcu.COLUMN_NAME 
    FROM information_schema.table_constraints tc 
    INNER JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name 
) AS tConstraints 
    ON t.name = tConstraints.TABLE_NAME 
    AND tCols.column_name = tConstraints.COLUMN_NAME 
LEFT JOIN (
    SELECT 
     o1.name AS FK_table, 
     c1.name AS FK_column, 
     fk.name AS FK_name, 
     o2.name AS PK_table, 
     c2.name AS PK_column, 
     pk.name AS PK_name 
    FROM sys.objects o1 
    INNER JOIN sys.foreign_keys fk 
     ON o1.object_id = fk.parent_object_id 
    INNER JOIN sys.foreign_key_columns fkc 
     ON fk.object_id = fkc.constraint_object_id 
    INNER JOIN sys.columns c1 
     ON fkc.parent_object_id = c1.object_id 
     AND fkc.parent_column_id = c1.column_id 
    INNER JOIN sys.columns c2 
     ON fkc.referenced_object_id = c2.object_id 
     AND fkc.referenced_column_id = c2.column_id 
    INNER JOIN sys.objects o2 
     ON fk.referenced_object_id = o2.object_id 
    INNER JOIN sys.key_constraints pk 
     ON fk.referenced_object_id = pk.parent_object_id 
     AND fk.key_index_id = pk.unique_index_id 
) AS fkInfo ON t.name = fkInfo.FK_table 
    AND tCols.column_name = fkInfo.FK_column 
WHERE t.name = 'Products' 
ORDER BY 3 

This is the output

+2

To co trzeba zrobić *wszystko czego chcesz. Możesz ukryć złożoność, przechodząc do procedury przechowywanej. – wallyk

+0

@wallyk to generic kawałek oprogramowania dla klienta bogatych Grid Control. Opcja SP nie zadziałałaby. Dobry komentarz, dodam to w pytaniu. – Yahya

+1

Nie należy łączyć nazwisk. –

Odpowiedz

7

spróbować mojej kwerendy (mam pk_name i fk_name separacji kolumnie, więc nie trzeba przypadek), to na widoków systemowych, i to szybko:

with 
    pk as (select pki.object_id, pki.column_id, _pk.name 
     from sys.index_columns pki 
     join sys.key_constraints _pk 
     on _pk.unique_index_id = pki.index_id and _pk.parent_object_id = pki.object_id 
     where 1=1), 
    fk as (select fkc.parent_object_id, fkc.parent_column_id, fk.name name, pkt.name pk_table, pkc.name pk_column, pkc.object_id, pkc.column_id 
    from sys.foreign_keys as fk 
    join sys.tables pkt 
    on pkt.object_id = fk.referenced_object_id 
    join sys.foreign_key_columns as fkc 
    on fkc.constraint_object_id = fk.object_id 
    join sys.columns as pkc 
    on pkc.object_id = fkc.referenced_object_id and pkc.column_id = fkc.referenced_column_id 
    where 1=1) 
select t.name TableName 
, t.object_id TableObjectId 
, c.column_id CId 
, c.name AS ColumnName 
, typ.name AS ColumnDataType 
, c.is_identity 
, c.precision 
, c.scale 
, pk.name pk_name 
, fk.name fk_name 
, fk.pk_table 
, fk.pk_column 
, fkpk.name pk_for_fk 
from sys.tables as t 
inner join sys.columns as c on t.object_id = c.object_id 
inner join sys.types as typ on typ.user_type_id = c.user_type_id 
left join pk on pk.object_id = t.object_id and pk.column_id = c.column_id 
left join fk on fk.parent_object_id = c.object_id and fk.parent_column_id = c.column_id 
left join pk as fkpk on fkpk.object_id = fk.object_id and fkpk.column_id = fk.column_id 
WHERE t.name = 'Products' 
5

ale wygląda trochę przesadą

Jeśli chcesz ściągnij wiele wartości z wielu tabel, a następnie otrzymasz duże zapytanie. Tak po prostu działa. Kiedy to się dzieje, ten nie jest tak duży.

Obawiasz się, że SQL Server nie może sobie z tym poradzić? Nie bądź, może. Wydajność? Nie wiele można zrobić, ponieważ są to tabele wewnętrznego katalogu. Opcje refaktoryzacji są ograniczone, ponieważ wymagana jest jedna instrukcja, a SP są wyłączone. Zawijanie go jako wbudowanej funkcji wycenionej w tabeli może pomóc, ale może zaszkodzić wydajności, jeśli wystąpi błąd.

Jeśli chcesz uzyskać przejrzystość prezentacji SQL, zapytania dodatkowe można zapisać jako CTE, przekonwertować na widoki (lub funkcje, ale nie) lub niezaangażowane, aby wszystkie połączenia były na tym samym poziomie wcięcia. Ta ostatnia jest bardziej prawdopodobna do zaciemnienia niż do wyjaśnienia.

Podsumowując, sądzę, że największą nadzieją jest napisanie czystego kodu - dobre wcięcie, spójne nazewnictwo, uzasadnione aliasy itp. - oraz opisanie celów i technik w komentarzach. To, co zaprezentowałeś, osiąga większość z tego.

+0

Zgadzam się, ale dodam, że czasami dobrze jest podzielić duże zapytanie w ten sposób na wiele zapytań, wypełnić tabelę tymczasową z wynikami każdego zapytania, a następnie przesłać zapytanie do tabeli, aby uzyskać wyniki. Czasami przyspiesza to proces i może uczynić go bardziej czytelnym i łatwiejszym w utrzymaniu. –