Как получить список идентификаторов из параметра, который иногда включает в себя уже идентификаторы, но иногда включает другой запрос sql - PullRequest
0 голосов
/ 19 июня 2019

Я разработал SQL-запрос в SSMS-2017 следующим образом:

DECLARE @property NVARCHAR(MAX) = @p;
SET @property = REPLACE(@property, '''', '');

DECLARE @propList TABLE (hproperty NUMERIC(18, 0));

IF CHARINDEX('SELECT', @property) > 0 OR CHARINDEX('select', @property) > 0
BEGIN
    INSERT INTO @propList
        EXECUTE sp_executesql @property;
END;
ELSE
BEGIN
    DECLARE @x TABLE (val NUMERIC(18, 0));

    INSERT INTO @x
        SELECT CONVERT(NUMERIC(18, 0), strval)
        FROM dbo.StringSplit(@property, ',');

    INSERT INTO @propList
        SELECT val
        FROM @x;
END;

SELECT ...columns...
FROM ...tables and joins...
WHERE ...filters...
  AND HMY IN (SELECT hproperty FROM @propList)

Проблема в том, что значение параметра @p может быть списком идентификаторов (Пример: 1, 2,3,4) или запрос прямого выбора (Пример: выберите ID из mytable, где code = 'A123').

Код работает хорошо, как показано выше.Однако это вызывает проблему в нашей системе (так как мы используем Yardi7-Voyager), и нам нужно оставить только запрос select в качестве запроса.Чтобы управлять им, я планировал создать функцию и использовать ее в предложении where, например:

WHERE HMY IN (SELECT myFunction(@p))

Однако я не смог управлять этим, поскольку вижу, что не могу выполнить динамический запрос в функции SQL.Тогда я сложен.Любая идея на данный момент для решения этой проблемы будет так ценится.

1 Ответ

1 голос
/ 19 июня 2019

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

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

  1. OPENQUERY (или OPENROWSET)

Вы можете включить свой код выше в хранимую процедуру вместо функции, поскольку хранимые процедуры ДОЛЖНЫ динамически выполнять SQL, в отличие от функций. Тогда запрос SELECT в вашем приложении будет SELECT from OPENQUERY (Выполнить сохраненную процедуру) .

  1. СОЮЗ ВСЕХ возможностей.

Я почти на 99% уверен, что никто никогда не захочет использовать это, но я упоминаю, что оно настолько полно академически, насколько я знаю, как это сделать.

Вторая возможность будет работать только при наличии ограниченного, известного числа возможных запросов, которые могут поддерживаться вашим приложением. Например, вы можете получить Properties только от TableA, отфильтрованного с помощью column1, или от TableB, отфильтрованного с помощью Column2 и / или Column3.

Может быть больше, чем эти возможности, но оно должно быть ограниченным, известным количеством, и чем больше возможностей, тем сложнее и длиннее будет код.

Но если это так, вы можете просто ВЫБРАТЬ из СОЮЗА ВСЕ из всех возможных сценариев и сделать так, чтобы только один из ВЫБОРОВ в СОЮЗЕ ВСЕ возвращал результаты.

Например:

SELECT ... FROM TableA WHERE Column1=fnGetValue(@p, 'Column1')
AND CHARINDEX('SELECT', @property) > 0
AND CHARINDEX('TableA', @property) > 0
AND CHARINDEX('Column1', @property) > 0
AND (Whatever other filters are needed to uniquely identify this case)
UNION ALL 
SELECT
...

Обратите внимание, что fnGetValue() не является встроенной функцией. Вы должны написать это. Он проанализирует строку в @p, найдет местоположение 'Column1 =' и вернет любое значение, следующее за ним.

В конце вашего UNION ALL вам нужно добавить последний UNION ALL в запрос, который будет обрабатывать случай, когда пользователь передал строку, разделенную запятыми, вместо запроса, но это легко, потому что все шаги в вашем коде, где вы заполняли таблицу переменных, не нужны. Вы можете просто сделать окончательный запрос следующим образом:

WHERE NOT CHARINDEX('SELECT', @p) > 0
AND HMY IN (SELECT strval FROM dbo.StringSplit(@p, ','))

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...