7
votes

Obtenir le schéma de table d'une requête

Selon MSDN , sqldatareader.getschematable code> retourne les métadonnées de colonne de la requête exécutée. Je me demande existe une méthode similaire qui donnera des métadonnées de table pour la requête donnée? Je veux dire quelles tables sont impliquées et quels alias il a eu.

Dans ma candidature, je reçois la requête et j'ai besoin d'ajouter le où code> clause programmable. Utilisation de getschematible () code>, je peux obtenir la métadonnée de colonne et la table auquel il appartient. Mais même si la table a des alias, il retourne toujours le vrai nom de la table. Existe-t-il un moyen d'obtenir le nom d'Alias ​​pour cette table? P>

Le code suivant montre avoir la métadonnée de la colonne. P>

const string connectionString = "your_connection_string";
string sql = "select c.id as s,c.firstname from contact as c";

using(SqlConnection connection = new SqlConnection(connectionString))
using(SqlCommand command = new SqlCommand(sql, connection))
{
    connection.Open();
    SqlDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo);
    DataTable schema = reader.GetSchemaTable();
    foreach (DataRow row in schema.Rows)
    {
        foreach (DataColumn column in schema.Columns)
        {
            Console.WriteLine(column.ColumnName + " = " + row[column]);
        }
        Console.WriteLine("----------------------------------------");
    }
    Console.Read();
}


2 commentaires

Pourquoi essayez-vous de faire cela? Pourquoi êtes-vous ajouté où des clauses à cette requête?


Approche simple alternative? Utilisez BaseBrename, pas le nom d'alias. Le vrai nom de table fonctionne bien, pourquoi pensez-vous avoir besoin du nom d'alias?


5 Réponses :


4
votes

Vous pouvez obtenir le plan d'exécution pour la requête, puis analyser le XML qui est retourné. Ceci est comme en utilisant l'option "Afficher le plan estimé" dans Gestion Studio.


5 commentaires

Merci. Mais, le plan d'exécution ne me donnera pas l'alias de la table.


Bien sûr. Je viens de courir SELECT * à partir de Production.Product p , et j'ai reçu ceci parmi xml:


Super! Comment obtenez-vous XML? Pouvez-vous montrer un petit code?


Utilisez SET WORSPLAN_XML sur , mais gardez à l'esprit que cela va arrêter la requête en cours d'exécution. Alors, vous voudrez-y fuir sans cette option.


Je n'ai pas reçu cela pour un plan d'index, mais j'ai reçu les entrées de colonne de colonne qui aussi longtemps que vous sélectionnez une colonne d'une table suffira à créer des mappages de table d'alias ...



11
votes

Réponse courte forte>

Cela ne fonctionnera pas. Vous ne pouvez pas, par conception, obtenir des alias de table du schéma de résultat. Et vous ne pouvez pas compter sur la possibilité de les obtenir du plan d'exécution de la requête. P>

réponse longue forte> p>

Lorsque vous recevez une requête SQL, la requête a Déjà été analysé, validé, optimisé, compilé dans une représentation interne et exécuté. Les alias font partie du "code source" de la requête et sont généralement perdus quelque part autour des étapes 1 et 2. p>

Après la requête est exécutée les seules choses qui peuvent être considérées comme des tables sont a) des tables physiques réelles et b) renvoyés données considérées comme une seule table anonyme. Tout entre peut être transformé ou complètement optimisé. P>

Si les DBMSE devaient conserver des alias, il serait pratiquement impossible d'optimiser les requêtes complexes. P>

Solutions possibles strong> P>

Je suggère de rédiger un problème: p>

  1. êtes-vous (ou votre application) source de la requête en question? Dans ce cas, connaissiez les alias. P> li>

  2. Si vous obtenez des requêtes fournies par quelqu'un d'autre ... eh bien ... cela dépend de la raison pour laquelle vous ajoutez des causes. P>

    • Dans le pire des cas, vous devrez analyser vos requêtes. P> LI>

    • Dans le meilleur cas, vous pourriez leur donner accès à des vues au lieu de tables réelles et mettre les clauses de la vue. P> Li> ul> li> ol>


      Solution simple et lache forte> p>

      Si je comprends vos exigences correctement: p>

      • L'utilisateur A passe à une question dans votre programme. P> li>

      • L'utilisateur B peut l'exécuter (mais ne peut pas l'éditer) et voit les données retournées. En outre, elle peut ajouter des filtres basés sur des colonnes retournées en utilisant une sorte de widget fourni par vous. P> li>

      • Vous ne souhaitez pas appliquer filtrer l'application à l'intérieur, mais ajoutez-les à la requête, afin d'éviter de récupérer des données inutiles de la base de données. P> LI> ul>

        Dans ce cas: p>

        • Lors d'une requête modifiée, essayez de l'exécuter et de rassembler des métadonnées pour des colonnes retournées. Si ColumnName code> S ne sont pas uniques, placez-vous à l'auteur. Magasinez des métadonnées avec une requête. P> LI>

        • Lorsque B ajoute filtre (basé sur des métadonnées de requête), stockez les deux noms de colonnes et conditions. P> li>

        • à l'exécution: p>

          • Vérifiez si les colonnes de filtrage sont toujours valides (une requête modifiée peut-être modifiée). Si vous ne supprimez pas les filtres non valides et / ou informez b. P> li>

          • exécuter la requête comme quelque chose comme: p>

             select *
             from ({query entered by A}) x
             where x.Column1 op1 Value1
                 and x.Column2 op2 Value2
            


8 commentaires

Merci. J'ai édité ma question pour répondre aux points que vous avez posées.


Merci encore. C'était comme ça que cela a été mis en œuvre auparavant. Mais lorsque nous enveloppons la requête principale entrée par l'utilisateur A comme sous-requête, cela entraîne des problèmes de performance. Comme le moteur de base de données doit exécuter la requête interne d'abord, ce qui n'aura aucun critère et peut entraîner une analyse de la table complète. Toute façon Merci pour toutes vos suggestions. Je vous en suis reconnaissant.


Avez-vous réellement vu ces problèmes de performance? Avez-vous exécuté les deux versions (emballées et ajoutées où) à la main et les a programmées? Avez-vous comparé des plans d'exécution? Et, enfin mais non le moindre, sur quel SGBDM?


Non, c'était mon hypothèse. Je vais faire des tests de toute façon. J'utilise SQL Server 2005.


Vérifiez cette hypothèse d'abord. Mesurer les performances et comparer les plans d'exécution. N'oubliez pas non plus que SQL Server cache des plans de requête en cache basé sur le texte exact de la requête. C'est important si vous exécutez la même requête à plusieurs reprises. Et ce serait bien si vous écrivez ici ce qui est sorti des tests et comment vous avez finalement décidé de résoudre le problème.


Je l'ai vérifié. Les résultats m'ont assez étonnant. Les deux versions de requêtes (enveloppées et avec où) sont effectuées de manière égale et le plan d'exécution était identique. Je ne suis pas sûr que cela se produise pour toutes les questions ou spécifiques à certaines requêtes que j'ai essayées. De toute façon que j'ai posté une autre question pour discuter de cela. Stackoverflow .com / questions / 3131522 / ...


C'est ce que j'ai attendu. Pour les RDBM de la requête générique Decent Devraient faire un meilleur travail accélérant les requêtes, puis votre application.


En fait, vous pouvez obtenir de manière fiable, mettre en cache et utiliser des alias de plan d'exécution XML tant que les tables source sont attribuées des alias à l'avant. Par exemple, bien que SELECT * à partir de (SELECT * à partir de leçons) A ne renvoie aucun alias, si vous ajoutez simplement un alias après le nom de la table, l'alias colle à la table de la sous-requête Chaîne tout le chemin de la colonne de sortie de niveau supérieur. Par exemple, SELECT * des leçons A, des leçons B, des leçons C, (SELECT * des leçons D) produit des colonnes de sortie XML, y compris les 4 alias de table sur toutes les colonnes, même s'ils sont tous du même table.



2
votes

C'est presque comme si vous avez besoin d'un analyseur pour analyser le SQL puis à partir de la requête analysée, faites une table des symboles d'alias et des tables auxquelles ils se rapportent. Ensuite, combinez-les avec les résultats de GetsChamisable () afin que vous puissiez mapper des colonnes sur l'alias approprié.

Quoi qu'il en soit, voir la question analyse du code SQL en C # pour certains analyseurs. Je ne les ai pas regardés en détail, mais peut-être que l'un d'entre eux est ce dont vous avez besoin. Si vous réservez uniquement des instructions, consultez le lien AntLR et la grammaire pour http: // www.antlr.org/grammar/1062280680642/ms_sql_select.html .

Si vos requêtes sont simples, vous pouvez probablement utiliser des expressions régulières ou votre propre grammaire personnalisée pour analyser les alias et les noms de table de la requête. Ce serait probablement la solution la plus facile.

La solution la plus robuste est probablement de payer pour l'analyseur de quelqu'un d'autre qui gère le SQL complet et le brise dans un arbre d'analyse ou autre chose où vous pouvez la questionner. Je ne suis pas sûr du fond de chacun et du ratio prix / robustesse. Mais certains d'entre eux sont ultra-chers .... Je dirais que si vous ne pouvez pas vous faire vous-même, explorez la grammaire anti-anthlr (car elle est gratuite) en supposant que vous avez juste besoin de déclarations de sélection. Sinon, vous devrez peut-être payer ....

Assumer réellement que vos utilisateurs ne sont pas des génies SQL folles et en utilisant des sous-requêtes / etc. Je ne vois pas pourquoi vous ne pouvez pas utiliser les noms de table de la vue Schema que vous avez indiqué que vous devez les trouver dans la requête, puis trouver l'alias comme alias de nom de table ou nom de table comme alias. Cela pourrait fonctionner pour de nombreux cas ... mais pour l'autant général, vous auriez besoin d'un analyseur complet .....


1 commentaires

Oui. Si les exigences ont eu un peu plus complexe, ce que l'appu décrit la seule bonne solution serait d'analyser la requête dans l'arborescence d'expression en application. Ensuite, vous pouvez éditer l'arborescence comme vous l'souhaitez ajouter des conditions supplémentaires, sous-requêtes, rejoindre des tables supplémentaires, etc. Vous pouvez également contrôler exactement les commandes de quelles commandes peuvent être utilisées. Mais c'est difficile et je ne connais aucune bibliothèque générale. J'ai pensé à pointer vers l'analyseur dans le cadre d'entité Microsoft (alors répondez dans votre premier lien) mais je ne l'ai jamais utilisé moi-même, alors je ne sais pas si cela fonctionnerait dans ce cas.



0
votes

Je pense que Rob Farley's Showplan XML fonctionnera pour vous (en supposant que vous exécutez un serveur SQL en retard qui a cette fonctionnalité).

Chaque colonne semble avoir pour chacune des colonnes sélectionnées. En supposant que vous avez au moins une colonne de chaque table, il devrait être trivial de faire une cartographie entre alias et table.


1 commentaires

Pour moi, il semble que l'utilisation de données de débogage (symboles, info de variable locale, etc.) dans le fichier PDB pour jeter un coup d'œil à l'intérieur de la DLL lorsque son interface publiée ne suffit pas. Cela peut vous donner beaucoup lorsque vous avez débogué un problème. Mais si vous comptez sur ces informations en code réel, cela échouera tôt ou tard.



0
votes

En fait, vous pouvez. Voir ma réponse ici: https://stackoverflow.com/a/19537164/88409

Qu'est-ce que vous aurez Pour faire, c'est exécuter toutes vos requêtes statiques une fois avec Set showplan_xml sur , analysez le fichier XML renvoyé et le tout premier Vous trouverez des colonnes de sortie de haut niveau . Tant que vous attribuez un alias aux tableaux de vos requêtes quand ils sont d'abord référencés, ces alias effectueront jusqu'à la colonne de sortie.

aller encore plus loin, je devrais supposer que de tels alias ne peuvent pas être optimisé, car le moteur devrait les utiliser pour différencier les différentes instances de la même colonne de la même table.

En fait, si vous exécutez une requête comme ceci: Sélectionnez * à partir de Leçons, leçons , le moteur vous indique essentiellement autant avec le message:

"Les objets" Leçons "et" leçons "dans la clause de la clause ont la même noms exposés. Utilisez des noms de corrélation pour les distinguer. "

Par exemple, si vous exécutez quelque chose comme "Set showplan_xml sur; Sélectionnez * des leçons A, les leçons B, les leçons C, (Sélectionnez-la des leçons D) sous-requête_aliases_wont_stick_like_table_aliases`

Vous obtiendrez une sortie comme celle-ci: xxx


1 commentaires

Juste pour ajouter ... cela ne va que si loin. Être capable de différencier les colonnes du même nom ne peut se produire au-delà d'un seul niveau, car alors les noms de colonne deviennent ambigus. Par exemple, bien que vous puissiez exécuter SELECT * à partir de (SELECT * des leçons A) A, (SELECT * des leçons b) b , vous ne pouvez pas ensuite envelopper cette chose entre parenthèses et sélectionner * (sous-requête) C, parce que la validation de la requête échouera dire quelque chose comme la colonne 'ID' a été spécifiée plusieurs fois pour 'C'.