2016-03-15 15 views
11

Avevo bisogno di scrivere una query dove posso ottenere informazioni su tutte le colonne (con tipo di dati) e anche per sapere quali sono PK/FK. Per FK, erano necessarie le informazioni aggiuntive da cui proviene l'altra tabella. Ho la query che funziona, ma sembra un po 'eccessivo.Query SQL per ottenere informazioni sulla colonna misc

Questo può essere fatto in modo migliore? Non mi piace la sub-query di join in esso. Deve essere una query, non può essere eseguita tramite SP.

Il mio esempio è contro Northwind (con alcune ulteriori FK rapporti che stavo testando)

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

che è circa quello che serve per fare *tutto ciò che vuoi. Potresti nascondere la complessità trasformandola in una stored procedure. – wallyk

+0

@wallyk questo è un software generico per un controllo Grid ricco di client. Quindi l'opzione SP non funzionerebbe. Buon commento però, lo aggiungerò nella domanda. – Yahya

+1

Non si deve partecipare per nome. –

risposta

7

provare la mia domanda (ho pk_name e fk_name nella colonna separata, quindi non hanno bisogno caso), è in vista di sistema, ed è veloce:

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

ma sembra un po 'eccessivo

Se si vuole estrai molti valori da un sacco di tabelle, quindi finirai con una query di grandi dimensioni. È così che funziona. Mentre queste cose vanno, questo non è così grande.

È preoccupato che SQL Server non riesca a gestirlo? Non essere, può. Prestazione? Non molto si può fare poiché si tratta di tabelle di catalogo interne. Le opzioni di refactoring sono limitate in quanto richiede una singola istruzione e gli SP sono fuori. Avvolgerlo come una funzione di valore in linea può essere d'aiuto, ma può danneggiare le prestazioni se va male.

Se si desidera solo chiarezza nella presentazione di SQL, le sottoquery potrebbero essere scritte come CTE, convertite in viste (o funzioni, ma non lo sono) o non innestate in modo che tutti i join si trovino allo stesso livello di indentazione. È più probabile che quest'ultimo sia più oscuro che a chiarire.

A conti fatti, penso che la vostra migliore speranza sia scrivere un codice pulito - una buona indentazione, nomi coerenti, alias ragionevoli ecc. - e descrivere obiettivi e tecniche nei commenti. Ciò che hai presentato ottiene la maggior parte di questo.

+0

Sono d'accordo, ma vorrei aggiungere che a volte è bene dividere una query di grandi dimensioni come questa in più query, compilare una tabella temporanea con i risultati di ogni query, quindi interrogare la tabella per ottenere i risultati. Questo a volte accelera il processo e può renderlo molto più leggibile e mantenibile. –

Problemi correlati