원래 질문은 "쿼리를 어떻게 매개 변수화합니까?"
여기에 답 이 아니라고 말하겠습니다. 원래 질문에 하겠습니다. 다른 좋은 답변에는 이미 그에 대한 몇 가지 시위가 있습니다.
그 말로,이 대답에 플래그를 지정하고, 답장을 내리고, 대답이 아닌 것으로 표시하십시오 ... 믿는대로 행동하십시오.
내가 추천 한 답변 (및 231 명)은 Mark Brackett의 답변을 참조하십시오. 그의 대답에 주어진 접근법은 1) 바인드 변수의 효과적인 사용을 위해, 그리고 2) Sargable 술어를 허용합니다.
선택된 답변
여기서 다루고 싶은 것은 Joel Spolsky의 답변에 주어진 접근법입니다. 정답은 "선택된"답변입니다.
Joel Spolsky의 접근 방식은 영리합니다. 그리고 "정상적인"값과 NULL 및 빈 문자열과 같은 규범적인 엣지 사례를 고려하여 예측 가능한 동작과 예측 가능한 성능을 보여줍니다. 그리고 특정 응용 프로그램에 충분할 수 있습니다.
그러나이 접근법을 일반화하는 관점에서 Name
열에 와일드 카드 문자가 포함 된 경우 (LIKE 술어에 의해 인식되는 경우)와 같이 모호한 모퉁이 사례를 고려 하십시오. 가장 일반적으로 사용되는 와일드 카드 문자는 %
(퍼센트 기호)입니다. 자 이제 여기서 다루고 나중에 다른 경우로 넘어 갑시다.
% 문자의 일부 문제
이름 값을 고려하십시오 'pe%ter'
. (여기 예제에서는 열 이름 대신 리터럴 문자열 값을 사용합니다.) 이름 값이 ''pe % ter '인 행은 다음 형식의 쿼리에 의해 반환됩니다.
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
그러나 검색어 순서가 반대로 바뀌면 동일한 행이 반환 되지 않습니다 .
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
우리가 관찰하는 행동은 이상합니다. 목록에서 검색어 순서를 변경하면 결과 집합이 변경됩니다.
우리가 원하지 않을 수도 있다는 말은 거의 없습니다. pe%ter
땅콩 버터가 얼마나 좋아하든 땅콩 버터와 .
모호한 코너 케이스
(예, 이것이 모호한 경우라는 것에 동의합니다. 아마도 테스트되지 않을 것입니다. 우리는 열 값에 와일드 카드를 기대하지 않을 것입니다. 우리는 애플리케이션이 그러한 값을 저장하지 못하게 할 것이라고 가정 할 수 있습니다. 내 경험상, LIKE
비교 연산자 의 오른쪽에 와일드 카드로 간주 될 문자 나 패턴을 구체적으로 허용하지 않는 데이터베이스 제약 조건은 거의 보지 못했습니다 .
구멍 패치
이 구멍을 패치하는 한 가지 방법은 %
와일드 카드 문자 를 피하는 것 입니다. 연산자의 이스케이프 절에 익숙하지 않은 사용자를 위해 SQL Server 설명서 링크가 있습니다 .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
이제 리터럴 %를 일치시킬 수 있습니다. 물론 열 이름이 있으면 와일드 카드를 동적으로 이스케이프해야합니다. 이 REPLACE
함수를 사용하여 다음 과 같이 %
문자의 발생을 찾고 각 문자 앞에 백 슬래시 문자를 삽입 할 수 있습니다 .
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
따라서 % 와일드 카드 관련 문제가 해결됩니다. 거의.
탈출 탈출
우리는 우리의 솔루션이 또 다른 문제를 일으킨다는 것을 알고 있습니다. 탈출 문자. 우리는 또한 이스케이프 문자 자체를 피해야합니다. 이번에는! 탈출 문자로 :
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
밑줄도
이제 롤을 시작 했으므로 REPLACE
밑줄 와일드 카드에 다른 핸들을 추가 할 수 있습니다 . 그리고 재미를 위해 이번에는 $를 이스케이프 문자로 사용합니다.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
이 접근법은 SQL Server뿐만 아니라 Oracle 및 MySQL에서도 작동하므로 이스케이프 방식을 선호합니다. (일반적으로 \ 백 슬래시를 이스케이프 문자로 사용합니다. 왜냐하면 정규 표현식에서 사용하는 문자이기 때문입니다. 그러나 왜 규칙에 의해 제약을 받는가!
그 성가신 괄호
또한 SQL Server에서는 와일드 카드 문자를 대괄호로 묶어 리터럴로 취급 할 수 있습니다 []
. 따라서 적어도 SQL Server에서는 아직 수정이 완료되지 않았습니다. 대괄호 쌍은 특별한 의미를 갖기 때문에이를 피해야합니다. 우리가 대괄호를 올바르게 탈출하면 적어도 대괄호 안에 하이픈 -
과 캐럿 을 신경 쓸 필요가 없습니다 ^
. 그리고 우리가 어떤을 남길 수 있습니다 %
및_
대괄호가 빠져 안에 우리는 기본적으로 브래킷의 특별한 의미를 비활성화 한 것이기 때문에, 문자를.
일치하는 대괄호 쌍을 찾는 것이 그렇게 어렵지 않아야합니다. singleton % 및 _의 발생을 처리하는 것보다 조금 더 어렵습니다. (단일 괄호는 리터럴로 간주되어 이스케이프 처리 할 필요가 없기 때문에 모든 괄호를 이스케이프하는 것만으로는 충분하지 않습니다. 더 많은 테스트 사례를 실행하지 않고 처리 할 수있는 것보다 논리가 조금 더 흐릿 해집니다. .)
인라인 표현이 지저분해진다
SQL의 인라인 표현식이 점점 길어지고 있습니다. 우리는 아마도 그것을 작동시킬 수는 있지만 하늘은 뒤에 오는 불쌍한 영혼을 도와서 해독해야합니다. 나는 인라인 표현을 좋아하는 팬이기 때문에 여기서는 그것을 사용하지 않는 경향이 있습니다. 주된 이유는 혼란의 이유를 설명하고 이에 대해 사과하는 의견을 남기고 싶지 않기 때문입니다.
어디 기능?
자, 그것을 SQL에서 인라인 표현식으로 처리하지 않으면 가장 가까운 대안은 사용자 정의 함수입니다. 그리고 우리는 속도를 낼 수 없다는 것을 알고 있습니다 (Oracle에서와 같이 인덱스를 정의 할 수 없다면) 함수를 만들어야한다면 SQL을 호출하는 코드에서 더 잘 수행 할 수 있습니다 성명서.
이 기능은 DBMS 및 버전에 따라 동작에 약간의 차이가있을 수 있습니다. (모든 Java 개발자에게 소리를 지므로 데이터베이스 엔진을 서로 바꾸어 사용할 수 있기를 바랍니다.)
도메인 지식
열에 대한 도메인 (즉, 열에 대해 허용되는 값 집합)에 대한 전문 지식이있을 수 있습니다. 열에 저장된 값에는 백분율 기호, 밑줄 또는 대괄호가 포함되지 않는다는 우선 순위 를 알 수 있습니다. 이 경우 해당 사례에 대해 간략하게 설명합니다.
열에 저장된 값은 % 또는 _ 문자를 허용하지만 제한 조건은 값이 LIKE 비교 "안전"이되도록 정의 된 문자를 사용하여 해당 값을 이스케이프해야 할 수 있습니다. 다시, 허용되는 값 세트, 특히 어떤 문자가 이스케이프 문자로 사용되는지에 대한 간단한 설명과 Joel Spolsky의 접근 방식을 따르십시오.
그러나 전문 지식과 보증이 없다면, 모호한 모퉁이 사례를 처리하는 것이 중요하며 행동이 합리적이며 "사양에 따라"고려하는 것이 중요합니다.
요약 된 다른 문제
나는 다른 사람들이 이미 일반적으로 고려되는 다른 관심 분야 중 일부를 충분히 지적했다고 생각합니다.
SQL 주입 (바인드 변수를 통해 정보를 제공하지 않고 SQL 텍스트에 사용자가 제공 한 정보를 포함하여 정보를 포함하는 것). 그것을 다루는 방법 :
인덱스 검색 대신 인덱스 스캔을 사용하는 옵티 마이저 계획, 와일드 카드 이스케이프를위한 표현식 또는 함수가 필요함 (표현식 또는 함수에 대한 인덱스 가능)
바인드 변수 대신 리터럴 값을 사용하면 확장성에 영향을 미침
결론
나는 Joel Spolsky의 접근법을 좋아합니다. 영리하다. 그리고 작동합니다.
그러나 그것을 보자 마자 나는 잠재적 인 문제를 즉시 보았고 그것이 미끄러지는 것은 내 본성이 아닙니다. 나는 다른 사람들의 노력에 비판적임을 의미하지 않습니다. 많은 개발자들이 개인적으로 업무를 수행한다는 것을 알고 있습니다. 많은 투자를하고 많은 관심을 갖고 있기 때문입니다. 따라서 이것은 개인적인 공격이 아닙니다. 내가 여기서 식별하고있는 것은 테스트보다는 생산에서 발생하는 문제의 유형입니다.
예, 나는 원래의 질문과는 거리가 멀었습니다. 그러나 질문에 대한 "선택된"답변에서 중요한 문제로 간주되는 사항에 대해이 메모를 남길 다른 곳은 무엇입니까?