Фильтр SQL-запроса Где предложение с использованием ИЛИ против ColdFusion?

У меня есть форма поиска, в которой пользователь выбирает некоторые критерии, прежде чем отправить запрос на сервер. Это включает в себя критерии поиска, которые можно искать по Name, Number или Show All. В нашей существующей (старой системе) предыдущие программисты использовали что-то вроде этого:

<cfquery name="qryFindRecord" datasource="#dsn#">
    SELECT RecID, Number, Name
    FROM Dictionary WITH (NOLOCK)       
    WHERE 1 = 1
        AND
        <cfswitch expression="#arguments.frm_filterby#">
            <cfcase value="1"><!--- Name --->
                Name LIKE <cfqueryparam value="%#trim(arguments.frm_search)#%" cfsqltype="cf_sql_varchar" maxlength="50" />
            </cfcase>
            <cfcase value="2"><!--- Number --->
                Number = <cfqueryparam value="#trim(arguments.frm_search)#" cfsqltype="cf_sql_char" maxlength="2" />
            </cfcase>
            <cfdefaultcase><!--- Show All --->
                1 = 1
            </cfdefaultcase>
        </cfswitch>
    ORDER BY Name
</cfquery>

Как вы видите, они используют оператор switch для оценки аргумента filter by и на основе этого выполняют поиск запроса. Я думал, что решение, которое будет включать только SQL, будет лучше с точки зрения обслуживания и эффективности. Вот пример:

<cfquery name="qryFindRecord" datasource="#dsn#">
    DECLARE @FilterBy INT = <cfqueryparam value="#trim(arguments.frm_filterby)#" cfsqltype="cf_sql_integer" />;

    SELECT RecID, Number, Name
    FROM Dictionary
    WHERE 
        (@FilterBy = 1 AND Name LIKE <cfqueryparam value="%#trim(arguments.frm_search)#%" cfsqltype="cf_sql_varchar" maxlength="50" />)
        OR 
        (@FilterBy = 2 AND Number = <cfqueryparam value="#trim(arguments.frm_search)#" cfsqltype="cf_sql_char" maxlength="2" />)
    ORDER BY Name
</cfquery>

Первый вариант (старый способ, который используется в текущей системе) кажется неэффективным, как с SQL, но я не на 100%, так как у меня нет глубоких знаний о SQL. Вторая причина - WITH (NOLOCK), старшие программисты сказали мне, что мы должны использовать это в каждом запросе SELECT в системе. Больше читать и проводить исследования кажется очень плохой привычкой, и ее не следует использовать. Я создаю новую систему, и все, что я ищу, - это хорошая практика для таких ситуаций, которая не приведет к неэффективной системе и жесткому коду для обслуживания. Если у кого-то есть опыт с подобными проблемами, пожалуйста, сообщите мне, как вы справляетесь с этим. Я не уверен, какой подход выбрать и какова наилучшая практика в наши дни.


person espresso_coffee    schedule 09.08.2018    source источник
comment
.. WITH (NOLOCK), I been told by senior programmers that we should use that on every SELECT query in the system. Reading more and doing research seems that is a very bad habit and should not be used. ›› Вы более правы, чем они. Скорее всего, они используют старую информацию, но NOLOCK, вероятно, на самом деле не делает того, что, по их мнению, делает. Кроме того, это просто маскирует другие проблемы.   -  person Shawn    schedule 10.08.2018
comment
В данном случае это не проблема, но будьте осторожны с коротким замыканием. Это не всегда работает так, как вы ожидаете, из-за оптимизатора dba.stackexchange.com/questions/5333/   -  person SOS    schedule 10.08.2018


Ответы (1)


Я знаком с MS SQL Server, поэтому YMMV.

WITH(NOLOCK) следует решать в каждом конкретном случае. Это имеет смысл, когда правильность менее важна, чем производительность. Это также сильно зависит от типа операций в вашей БД.

Что касается вашего cfquery, я думаю, что ваш рефакторинг хорош. Все, что выглядит ближе к хранимой процедуре, имеет больше шансов быть параметризованным СУБД, что означает эффективное кэширование плана выполнения. Если вы можете, я бы превратил его в хранимую процедуру.

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

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

Например, чтобы еще больше упростить ваш запрос, подняв условную логику из запроса, вы можете сказать:

<cfif arguments.frm_filterby eq 1>
  <cfquery name="qryFindRecord" datasource="#dsn#">
    SELECT RecID, Number, Name
    FROM Dictionary
    WHERE Name LIKE <cfqueryparam value="%#trim(arguments.frm_search)#%" cfsqltype="cf_sql_varchar" maxlength="50"/>
    ORDER BY Name
  </cfquery>
<cfelseif arguments.frm_filterby eq 2>
  <cfquery name="qryFindRecord" datasource="#dsn#">
    SELECT RecID, Number, Name
    FROM Dictionary
    WHERE Number = <cfqueryparam value="#trim(arguments.frm_search)#" cfsqltype="cf_sql_char" maxlength="2"/>
    ORDER BY Name
  </cfquery>
<cfelse>
  <!--- Invalid request? --->
</cfif>

Реальная разница здесь в том, хотите ли вы читать в первую очередь ColdFusion или SQL.

Или версия, которая скрывает больше внутренних компонентов БД с хранимая процедура:

<cfif arguments.frm_filterby eq 1>
  <cfstoredproc procedure="DictionaryByName" datasource="#dsn#">
    <cfprocparam value="%#trim(arguments.frm_search)#%" cfsqltype="cf_sql_varchar" maxlength="50"/>
    <cfprocresult name="qryFindRecord"/>
  </cfstoredproc>
<cfelseif arguments.frm_filterby eq 2>
  <cfstoredproc procedure="DictionaryByNumber" datasource="#dsn#">
    <cfprocparam value="#trim(arguments.frm_search)#" cfsqltype="cf_sql_char" maxlength="2"/>
    <cfprocresult name="qryFindRecord"/>
  </cfstoredproc>
<cfelse>
  <!--- Invalid request? --->
</cfif>
person Rain    schedule 09.08.2018
comment
Вы имеете в виду пример вызова ColdFusion хранимой процедуры или самой хранимой процедуры? - person Rain; 10.08.2018
comment
В общем, то, что было бы хорошим вариантом или выходом. Также потребуется пример того, как вызывать хранимую процедуру с помощью ColdFusion, если это решение для этого случая. - person espresso_coffee; 10.08.2018
comment
Наилучшая реализация зависит от многих факторов: сложности запроса, того, кто поддерживает запрос (веб-разработчик или разработчик SQL), достаточно ли сложен запрос, чтобы требовать хранимой процедуры (временные таблицы, вызов других хранимых процедур), вы выполняете транзакцию... Я добавил пример вызова хранимой процедуры. - person Rain; 10.08.2018
comment
Для этого конкретного запроса, который у меня есть, или чего-то подобного, который будет иметь еще два или три параметра поиска, подходит ли он для хранимой процедуры или нет? Я знаю, что вы упомянули несколько вещей в своем ответе, но мне как веб-разработчику трудно принять решение, поскольку я не уверен, что это раскроет мою систему и откроет некоторые дыры в безопасности. - person espresso_coffee; 10.08.2018
comment
Также как будет выглядеть эта хранимая процедура SQL? - person espresso_coffee; 10.08.2018
comment
WITH(NOLOCK) should be decided case-by-case. It makes sense **when correctness is less important than performance**. ‹‹ Это важный момент. По сути, NOLOCK совпадает с READ UNCOMMITED, поэтому вы можете использовать данные, которые откатываются из базы данных. Грязные чтения выполняются быстрее, чем ожидание завершения транзакции другим процессом, но они по-прежнему являются грязными чтениями. Данные, которые вы SELECT сейчас, могут отличаться от тех же данных, которые вы SELECT через несколько секунд. А в моем предыдущем разработчике CF мы SELECT делали код, а затем возвращали данные обратно. Возможно, мы загрязнили наши данные. - person Shawn; 10.08.2018
comment
@Shawn А как насчет хранимых процедур SQL? Я вижу всевозможные предлагаемые решения, но у каждого из них есть свои недостатки. Является ли хранимая процедура лучшим способом поиска таблиц базы данных? Должен ли я использовать одну хранимую процедуру с фильтрами в предложении where или было бы лучше, если бы у нас было несколько хранимых процедур? - person espresso_coffee; 10.08.2018
comment
@espresso_coffee См. точку зрения Рейна о том, кто будет поддерживать sproc? Насколько сложный запрос? Будете ли вы присоединяться к нескольким столам? Насколько велики таблицы и сколько их можно кэшировать? Я обычно большой поклонник дополнительного уровня абстракции и безопасности, который могут обеспечить SPROC, но я также не большой поклонник разработчиков, копающихся в базе данных, если они не знают, что они делают и как их запросы повлияют на общая система. Я считаю, что в большинстве случаев база данных должна выполнять тяжелую работу, а приложение должно просто запрашивать данные. Но, как обычно, это зависит. - person Shawn; 10.08.2018
comment
@ Шон, я согласен со всеми пунктами, которые вы упомянули. К сожалению, в нашей компании все работает, и я отвечаю за Front/Back-End и БД для этого проекта. Для меня важно, чтобы процесс поиска был эффективным и безопасным. Я буду поддерживать код, и я разработал базу данных. Хранимые процедуры — это что-то новое для меня, но я определенно хочу их использовать, если это будет полезно для моей системы. - person espresso_coffee; 10.08.2018
comment
Кроме того, просто примечание к обзору кода, я бы добавил, что вы передаете cf_sql_varchar для Name и cf_sql_char для Number, поэтому убедитесь, что эти типы данных cf совпадают с типами данных SQL, чтобы вы не попадали за кулисы неявное преобразование данных. Я не знаю, какой тип данных находится в Number, но передача типа char кажется неправильной. Что-то с именем Number и, по-видимому, ограниченное двумя символами, вероятно, должно быть целым числом. Но если столбец базы данных на самом деле char(2), придерживайтесь этого. - person Shawn; 10.08.2018
comment
@espresso_coffee Так ты управляешь базой данных? Это определенно облегчит вам внесение необходимых изменений. В вашем запросе используется несколько таблиц? Насколько сложный запрос? SPROC могут быть более безопасными и могут помочь упростить вызов из вашего кода. Представление SQL также может помочь, но вы не можете передавать аргументы в представление, поэтому вам придется фильтровать его в другом месте. Но это может быть более эффективным для часто используемого кода запроса. Опять же, в зависимости от того, насколько сложны ваш запрос и данные, SPROC может быть хорошим способом. Но для простого запроса, такого как приведенный выше пример, я бы, вероятно, придерживался кода. - person Shawn; 10.08.2018
comment
Структура вашей БД это слишком широкая тема. Мы даже не знаем, какую СУБД вы используете. Лучшее, что вы можете сделать, это помнить о безопасности и профилировать свое приложение именно в таком порядке. Когда вы сталкиваетесь с проблемами производительности, подумайте о рефакторинге вашего SQL. Перепишите его, протестируйте несколькими способами и выберите лучший. Когда вы обнаружите, что тратите много времени на работу с ColdFusion, проведите его рефакторинг. - person Rain; 10.08.2018
comment
@Rain Судя по другим вопросам, я считаю, что это SQL2008. - person Shawn; 10.08.2018
comment
@espresso_coffee — имейте в виду, что хранимые процедуры — это не волшебная пуля. Создать неэффективный запрос с хранимой процедурой так же просто, как и без нее. Кроме того, базы данных, такие как sql server, в любом случае часто кэшируют параметризованные операторы, поэтому общая разница между этим и хранимым процессом меньше, чем раньше. В итоге вам необходимо профилировать запросы, чтобы оценить производительность и эффективность. С точки зрения безопасности хорошим правилом является не использовать динамический SQL - ни в CF (например, `SELECT #column# FROM Table ORDER BY #column#', ни внутри хранимой процедуры (например, EXEC). - person SOS; 10.08.2018
comment
Кроме того, с точки зрения эффективности LIKE является одним из наименее эффективных операторов. Запросы, в которых используются двойные подстановочные знаки в начале и в конце, например '%something%', обычно вызывают в лучшем случае сканирование индекса, а в худшем - сканирование таблицы. Когда это возможно, использование запросов с подстановочным знаком в конце может быть более эффективным для индексированных столбцов, LIKE 'startsWith%'. - person SOS; 10.08.2018