Запуск динамики c sql на основе значений в таблице с последующим добавлением их в тот же набор результатов - PullRequest
0 голосов
/ 06 августа 2020

У меня есть таблица, которую мне нужно запросить, с именем «customData». Таблица состоит из трех столбцов (соответствующих). Один содержит поле, а другой - имя таблицы, а третий - идентификатор записи во внешней таблице. Цель состоит в том, чтобы взять эти значения, найти значение для этого поля в этой внешней таблице и добавить его в качестве столбца в набор результатов. Это вообще возможно? Я не могу понять, как построить динамический c sql с использованием данных из таблицы, если таблица еще не прочитана.

Сейчас я пытаюсь исправить невероятно медленный поиск происходит в неназначенном поле, превратив его в хранимую процедуру.

ID  Value  foreignTable ftIndex ftRow   customField 
1  "yes"  "tblDriver"    2001    "Name"  "Licensed?"  
2  "no"   "tblDriver"    2002    "Name"  "Licensed?"
3  "7"    "tblOrigin"    1131    "Name"  "tank count"

ожидаемый результат:

1 "licensed" "yes" 'Darryl Coffman'
2 "licensed" "no"  'Cash Rainer'
3 "tank count" "7"   'texas field'

1 Ответ

1 голос
/ 06 августа 2020

Хотя это конкретно не дает ответа на вопрос, я хотел обобщить мои вышеупомянутые комментарии в ответ, чтобы показать, почему ваш дизайн так несовершенен и почему он не будет работать; и поэтому зачем вам это исправлять. Обратите внимание, что у нас недостаточно информации, чтобы предложить, что это за исправление, кроме как заявить, что вам нужно потратить значительное время на исправление дизайна до нормализованного подхода.

Во-первых, Единственный способ достичь конечной цели - это использовать Dynami c SQL. Если бы вы указали только одно значение столбца, тогда вы на самом деле были бы «в порядке», так как вы могли бы написать такой оператор:

SELECT DT.ID,
       DT.CustomField,
       DT.[Value],
       D.[Name]
FROM dbo.DenormalisedTable DT
     JOIN dbo.tblDriver D ON DT.ForeignTable = N'tblDriver'
                         AND DT.ftIndex = D.ID
WHERE DT.ftRow = N'Name';

К сожалению, вы go должны заявить, что это не ' Это означает, что вам понадобится запрос, подобный приведенному ниже (для двух имеющихся у нас примеров таблиц):

SELECT DT.ID,
       DT.CustomField,
       DT.[Value],
       CASE DT.ForeignTable WHEN N'tblDriver' THEN CASE DT.CustomField WHEN N'Name' THEN D.[Name] END
                            WHEN N'tblOrigin' THEN CASE DT.CustomField WHEN N'Name' THEN O.[Name] END
       END
FROM dbo.DenormalisedTable DT
     LEFT JOIN dbo.tblDriver D ON DT.ForeignTable = N'tblDriver'
                              AND DT.ftIndex = D.ID
     LEFT JOIN dbo.tblOrigin O ON DT.ForeignTable = N'tblOrigin'
                              AND DT.ftIndex = O.ID;

Очевидно, однако, что у него есть много других таблиц и, скорее всего, другие столбцы ( не только столбец name) для динамического получения значений. В итоге вы получите что-то вроде этого:

SELECT DT.ID,
       DT.CustomField,
       DT.[Value],
       CASE DT.ForeignTable WHEN N'tblDriver' THEN CASE DT.CustomField WHEN N'Name' THEN D.[Name]
                                                                       WHEN N'Age' THEN D.Age
                                                                       WHEN N'Dob' THEN D.Dob
                                                   END
                            WHEN N'tblOrigin' THEN CASE DT.CustomField WHEN N'Name' THEN D.[Name]
                                                                       WHEN N'Age' THEN D.Age
                                                                       WHEN N'Dob' THEN D.Dob
                                                   END
                            WHEN ... --20 more WHENs, 50? Plus all the inner CASE expressions
                            WHEN N'tblOwner' THEN CASE DT.CustomField WHEN N'FirstTraded' THEN Onr.FirstTraded
                                                                      ...
                                                  END
       END AS ColumnValue
FROM dbo.DenormalisedTable DT
     LEFT JOIN dbo.tblDriver D ON DT.ForeignTable = N'tblDriver'
                              AND DT.ftIndex = D.ID
     LEFT JOIN dbo.tblOrigin O ON DT.ForeignTable = N'tblOrigin'
                              AND DT.ftIndex = O.ID
     LEFT JOIN ...
     ---20 more JOINs, 50?
     LEFT JOIN dbo.tblOwner Onr ON DT.ForeignTable = N'tblOwner'
                               AND DT.ftIndex = Onr.ID;

Однако здесь есть некоторые серьезные проблемы. Первым будет неявное преобразование типов данных

Например, обратите внимание, что здесь у меня есть столбцы Name, Age и Dob. Этот выбор был сделан намеренно, так как существуют все совершенно разные типы данных; строка, числовое значение и дата и время соответственно. Если выражение CASE возвращает разные типы данных, тогда Приоритет типа данных будет использоваться для определения возвращаемого типа данных. Скорее всего, это приведет к типу данных даты и времени, а это означает, что ваши числовые столбцы и / или (n)varchar не смогут выполнить оператор из-за ошибок преобразования. Это означает, что вышеперечисленное не работает. Хотя вы можете CONVERT каждое выражение, возвращаемое в THEN s, это серьезно влияет на данные и может легко привести к тому, что данные будут потребляемыми или не будут отображаться так, как вы хотите.

Эта проблема также относится к предложениям ON, которые предполагают , что каждый столбец ID имеет один и тот же тип данных (int?), Поскольку наличие разных типов данных полностью нарушит это. Если это не так, вам нужно будет использовать TRY_CONVERT для соответствующего типа данных, что подводит нас к следующему пункту: Performance .

Performance будет ужасно . Не будем биться вокруг бу sh. Подобный запрос не будет работать должным образом из-за огромного количества операций чтения, необходимых для разных таблиц. Вам повезет, это вернет данные за считанные минуты, а возможно, и часы, в зависимости от размера вашей базы данных. Добавление чего-то вроде TRY_CONVERT в ON уничтожает любую (небольшую) вероятность того, что СУБД могла использовать индекс для поиска.

Наконец, у нас есть масштабируемость. Написание вышеизложенного было бы само по себе задачей, а это значит, что вам нужно иметь для использования динамического c SQL. Но проблема, которую вы пытаетесь решить здесь, - это проблема с производительностью, и я также сказал вам, что это решение будет медленным , а я означает медленным . Оператор Dynami c не улучшит этого, и соображения, которые вам понадобятся, чтобы заставить этот оператор работать в первую очередь, были бы немалыми; так что давайте даже не go идти по этому пути, так как он уже выброшен в окно.

Итак, единственное решение здесь, чтобы получить эффективный запрос, - это исправить ваш дизайн. Нормализуйте свои данные и не храните такую ​​информацию, как место в таблице подстановочной таблицы. Подобные конструкции, хотя и могут «выглядеть» интуитивно понятными для пользователя, имеют не масштабирование и не работают хорошо. Такие конструкции обычно возникают при рассмотрении РСУБД, как на язык программирования, и применении того же logi c. SQL не является языком программирования и работает иначе; в чем он преуспевает и с чем борется, совершенно разные.

TL; DR: действующий дизайн является причиной медленного выполнения вашего запроса. Вы не можете обойти это, поскольку ваш запрос (какой бы он ни был) не является причиной root. Только исправление конструкции устранит проблему с производительностью, и именно на это вам потребуется потратить значительное время и ресурсы на исправление.

...