Получение схемы таблицы из запроса - PullRequest
7 голосов
/ 17 июня 2010

Согласно MSDN , SqlDataReader.GetSchemaTable возвращает метаданные столбца для выполненного запроса.Мне интересно, есть ли подобный метод, который даст метаданные таблицы для данного запроса?Я имею в виду, какие таблицы задействованы и какие у него есть псевдонимы.

В моем приложении я получаю запрос и мне нужно программно добавить предложение where.Используя GetSchemaTable(), я могу получить метаданные столбца и таблицу, к которой он принадлежит.Но хотя таблица имеет псевдонимы, она все равно возвращает реальное имя таблицы.Есть ли способ получить псевдоним для этой таблицы?

Следующий код показывает получение метаданных столбца.

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();
}

Это даст мне правильную информацию о столбцах.Но когда я вижу BaseTableName для столбца Id, он дает contact вместо псевдонима c.Есть ли способ получить схему таблицы и псевдонимы из запроса, подобного приведенному выше?

Любая помощь будет полезна!

Редактировать

ХотяЯ мог бы использовать план выполнения, предложенный Робом, я был бы признателен за любые альтернативные простые подходы.

Ответы на вопросы tomekszpakowicz

Являетесь ли вы (или ваша заявка) источником рассматриваемого запроса?В этом случае вы должны знать псевдонимы.

Я не автор запросов.У нас есть система, где пользователи могут ввести запрос.Мы строим столбцы из него, используя метод, который я объяснил выше.Эти детали будут сохранены, и другой пользователь может использовать это, например, добавляя новые критерии и т. Д. Поэтому нам нужно динамически строить SQL из имеющейся информации.Поэтому, когда столбец имеет псевдоним, а мы не получаем псевдоним, созданное предложение where будет недействительным.

Спасибо

Ответы [ 5 ]

11 голосов
/ 22 июня 2010

Краткий ответ

Это не сработает. По замыслу вы не можете получить псевдонимы таблиц из схемы результатов. И вы не можете рассчитывать на то, что сможете получить их из плана выполнения запроса.

Длинный ответ

Когда вы получаете результат запроса SQL, запрос уже был проанализирован, проверен, оптимизирован, скомпилирован в некоторое внутреннее представление и выполнен. Псевдонимы являются частью «исходного кода» запроса и обычно теряются где-то на шаге 1 и 2.

После выполнения запроса единственными вещами, которые можно увидеть в виде таблиц, являются: а) реальные физические таблицы и б) возвращаемые данные, рассматриваемые как одна анонимная таблица. Все между может быть преобразовано или полностью оптимизировано.

Если бы СУБД требовалось сохранять псевдонимы, было бы практически невозможно оптимизировать сложные запросы.

Возможные решения

Предлагаю решить проблему:

  1. Являетесь ли вы (или ваше приложение) источником рассматриваемого запроса? В этом случае вы должны знать псевдонимы.

  2. Если вы получаете запросы, предоставленные кем-то другим ... Ну ... Это зависит от того, почему вы добавляете, где причины.

    • В худшем случае вам придется анализировать запросы самостоятельно.

    • В лучшем случае вы можете предоставить им доступ к представлениям вместо реальных таблиц и поместить в них предложения where.


Простое и безобразное решение

Если я правильно понимаю ваши требования:

  • Пользователь A вводит запрос в вашу программу.

  • Пользователь B может запустить его (но не может его редактировать) и видит возвращенные данные. Кроме того, она может добавлять фильтры на основе возвращаемых столбцов, используя предоставленный вами виджет.

  • Вы не хотите применять фильтр внутри приложения, но вместо этого добавляете их в запрос, чтобы избежать выборки ненужных данных из базы данных.

В этом случае:

  • Когда A редактирует запрос, попробуйте запустить его и собрать метаданные для возвращаемых столбцов. Если ColumnName не уникальны, пожаловаться автору. Сохранять метаданные с запросом.

  • Когда B добавляет фильтр (на основе метаданных запроса), сохраняйте имена обоих столбцов и условия.

  • При исполнении:

    • Проверьте, все ли столбцы фильтра все еще действительны (возможно, запрос А изменился). Если нет, удалите недействительные фильтры и / или сообщите B.

    • Выполнить запрос как что-то вроде:

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

Если вы хотите корректно обрабатывать изменения схемы базы данных, вам нужно добавить некоторые дополнительные проверки, чтобы убедиться, что метаданные соответствуют тому, что запрос действительно возвращает.

Примечание безопасности

Ваша программа отправит запрос, написанный пользователем A, прямо в базу данных. Крайне важно, чтобы вы делали это, используя подключение к базе данных с разрешениями, которые не превышают разрешения базы данных A. В противном случае вы запрашиваете эксплойты на основе SQL-инъекций.

Следствие

Если пользователь A не имеет прямого доступа к базе данных из соображений безопасности, вы не можете использовать вышеуказанное решение.

В этом случае единственный способ сделать его безопасным - убедиться, что ваше приложение понимает 100% запроса, что означает его синтаксический анализ в вашей программе и разрешает только те операции, которые вы считаете безопасными.

4 голосов
/ 17 июня 2010

Вы можете получить план выполнения для запроса, а затем проанализировать возвращаемый XML.Это похоже на использование параметра «Показать оценочный план» в Management Studio.

2 голосов
/ 24 июня 2010

Это похоже на то, что вам нужен анализатор для анализа SQL, а затем из проанализированного запроса создайте таблицу символов псевдонимов и таблицы, к которым они относятся. Затем объедините это с результатами GetSchemaTable (), чтобы можно было сопоставить столбцы с соответствующим псевдонимом.

В любом случае смотрите вопрос Разбор SQL-кода в C # для некоторых парсеров. Я не рассматривал их подробно, но, возможно, один из них - то, что вам нужно. Если вы делаете только операторы select, проверьте ссылку ANTLR и грамматику для http://www.antlr.org/grammar/1062280680642/MS_SQL_SELECT.html.

Если ваши запросы просты, вы, вероятно, могли бы использовать регулярные выражения или свою собственную пользовательскую грамматику для анализа псевдонимов и имен таблиц в запросе. Это, вероятно, было бы самым простым решением.

Самое надежное решение - это, вероятно, заплатить за чей-то парсер, который обрабатывает полный SQL и разбивает его на дерево разбора или что-то еще, где вы можете запросить его. Я не уверен в достоинствах каждого и соотношении цена / надежность. Но некоторые из них очень дороги… Я бы сказал, что если вы не можете сделать это самостоятельно, изучите грамматику ANTLR (потому что она бесплатна), предполагая, что вам просто нужны операторы select. В противном случае вам, возможно, придется заплатить ....

На самом деле предполагается, что ваши пользователи не сумасшедшие гении SQL и используют подзапросы и т. Д. Я не понимаю, почему вы не можете использовать имена таблиц в представлении схемы, которые, как вы сказали, вы нашли, чтобы найти их в запросе, а затем найти псевдоним как псевдоним таблицы или как имя псевдонима таблицы. Это может работать во многих случаях .... Но для полного общего случая вам понадобится полный синтаксический анализатор .....

0 голосов
/ 23 октября 2013

На самом деле, вы можете.Смотрите мой ответ здесь: https://stackoverflow.com/a/19537164/88409

Вам нужно будет выполнить все ваши статические запросы один раз с помощью set showplan_xml on, проанализировать возвращенный XML, и самым первым найденным <OutputList> будетвыходные столбцы верхнего уровня.Пока вы присваиваете псевдоним таблицам в своих запросах, когда на них впервые ссылаются, эти псевдонимы будут переноситься в выходной столбец.

Чтобы пойти еще дальше, я должен был бы предположить, что такие псевдонимы НЕ МОГУТ бытьоптимизирован, потому что движок должен будет использовать их, чтобы различать разные экземпляры одного и того же столбца из одной и той же таблицы.

Фактически, если вы выполняете запрос, подобный следующему: select * from Lessons, Lessons, движок в основном сообщаетвы так же, как с сообщением:

"Объекты" Уроки "и" Уроки "в предложении FROM имеют одинаковые открытые имена. Используйте имена корреляции, чтобы различать их. "

Например, если вы запускаете что-то вроде 'set showplan_xml on;выберите * из уроков a, уроки b, уроки c, (выберите * из уроков d) subquery_aliases_wont_stick_like_table_aliases`

Вы получите вывод, подобный следующему:

<OutputList>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[a]" Column="ID"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[a]" Column="Name"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[a]" Column="Description"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[a]" Column="Enabled"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[a]" Column="LessonTypeID"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[b]" Column="ID"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[b]" Column="Name"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[b]" Column="Description"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[b]" Column="Enabled"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[b]" Column="LessonTypeID"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[c]" Column="ID"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[c]" Column="Name"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[c]" Column="Description"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[c]" Column="Enabled"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[c]" Column="LessonTypeID"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[d]" Column="ID"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[d]" Column="Name"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[d]" Column="Description"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[d]" Column="Enabled"/>
  <ColumnReference Database="[sensitive]" Schema="[dbo]" Table="[Lessons]" Alias="[d]" Column="LessonTypeID"/>
</OutputList>
0 голосов
/ 24 июня 2010

Я думаю, что showplan xml Роба Фарли подойдет вам (при условии, что вы используете достаточно поздний SQL Server с такой функцией).

Кажется, что в каждом столбце есть <ColumnReference Server="" Database="" Schema="" Table="" Alias="" Column=""/> для каждого из выбранных столбцов. Предполагая, что у вас есть хотя бы один столбец из каждой таблицы, сделать тривиальное отображение между псевдонимом и таблицей должно быть тривиально.

...