관계형 데이터베이스를 다루는 우리 모두는 SQL이 다르다는 것을 배웠습니다. 원하는 결과를 도출하고 효율적으로 수행하려면 익숙하지 않은 패러다임을 배우고 가장 친숙한 프로그래밍 패턴 중 일부가 여기서 작동하지 않는 것을 발견하는 지루한 프로세스가 필요합니다. 당신이 본 (또는 당신이 저지른) 일반적인 반 패턴은 무엇입니까?
관계형 데이터베이스를 다루는 우리 모두는 SQL이 다르다는 것을 배웠습니다. 원하는 결과를 도출하고 효율적으로 수행하려면 익숙하지 않은 패러다임을 배우고 가장 친숙한 프로그래밍 패턴 중 일부가 여기서 작동하지 않는 것을 발견하는 지루한 프로세스가 필요합니다. 당신이 본 (또는 당신이 저지른) 일반적인 반 패턴은 무엇입니까?
답변:
데이터 액세스 계층에서 UI 논리를 혼합하려는 대부분의 프로그래머 경향에 대해 일관되게 실망합니다.
SELECT
FirstName + ' ' + LastName as "Full Name",
case UserRole
when 2 then "Admin"
when 1 then "Moderator"
else "User"
end as "User's Role",
case SignedIn
when 0 then "Logged in"
else "Logged out"
end as "User signed in?",
Convert(varchar(100), LastSignOn, 101) as "Last Sign On",
DateDiff('d', LastSignOn, getDate()) as "Days since last sign on",
AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' +
City + ', ' + State + ' ' + Zip as "Address",
'XXX-XX-' + Substring(
Convert(varchar(9), SSN), 6, 4) as "Social Security #"
FROM Users
일반적으로 프로그래머는 데이터 집합을 그리드에 직접 바인딩하려고하기 때문에 클라이언트에서 형식보다 SQL Server 형식의 서버 쪽을 사용하는 것이 편리하기 때문에이 작업을 수행합니다.
위에 표시된 것과 같은 쿼리는 데이터 계층을 UI 계층에 밀접하게 연결하기 때문에 매우 취약합니다. 또한이 프로그래밍 스타일은 저장 프로 시저를 재사용 할 수 없도록 철저히 방지합니다.
여기 내 탑 3이 있습니다.
번호 1. 필드 목록을 지정하지 못했습니다. (편집 : 혼란을 피하기 위해 : 이것은 프로덕션 코드 규칙입니다. 저자가 아닌 한 일회성 분석 스크립트에는 적용되지 않습니다.)
SELECT *
Insert Into blah SELECT *
해야한다
SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist
루프 변수가있는 while 루프가 수행 될 때 커서와 while 루프를 사용합니다.
DECLARE @LoopVar int
SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable)
WHILE @LoopVar is not null
BEGIN
-- Do Stuff with current value of @LoopVar
...
--Ok, done, now get the next value
SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable
WHERE @LoopVar < TheKey)
END
문자열 유형을 통한 DateLogic
--Trim the time
Convert(Convert(theDate, varchar(10), 121), datetime)
해야한다
--Trim the time
DateAdd(dd, DateDiff(dd, 0, theDate), 0)
최근에 "하나의 쿼리가 두 개보다 낫습니다."라는 급증을 보았습니다.
SELECT *
FROM blah
WHERE (blah.Name = @name OR @name is null)
AND (blah.Purpose = @Purpose OR @Purpose is null)
이 쿼리에는 매개 변수 값에 따라 두 개 또는 세 개의 다른 실행 계획이 필요합니다. 이 SQL 텍스트에 대해 하나의 실행 계획 만 생성되어 캐시에 고정됩니다. 이 계획은 매개 변수 값에 관계없이 사용됩니다. 결과적으로 간헐적으로 성능이 저하됩니다. 의도 한 실행 계획 당 하나의 쿼리 인 두 개의 쿼리를 작성하는 것이 훨씬 좋습니다.
사람이 읽을 수있는 비밀번호 필드 (예 : ad) 자기 설명.
인덱스 열에 대해 LIKE를 사용하면 일반적으로 LIKE라고 말하고 싶어합니다.
SQL 생성 PK 값을 재활용합니다.
서프라이즈 아무도 언급하지 신 테이블을 아직. 100 열의 비트 플래그, 큰 문자열 및 정수와 같은 "유기적"이란 말은 없습니다.
그런 다음 CSV 파일 , 파이프로 구분 된 문자열 또는 기타 구문 분석이 필요한 데이터를 큰 텍스트 필드에 저장하는 ".ini 파일이 누락되었습니다" 패턴이 있습니다.
그리고 MS SQL 서버의 경우 커서 를 전혀 사용하지 않습니다 . 주어진 커서 작업을 수행하는 더 좋은 방법이 있습니다.
너무 많아 편집했습니다!
LIKE '%LIKE'
.
깊이 파고들 필요가 없습니다. 준비된 진술을 사용하지 마십시오.
무의미한 테이블 별칭 사용 :
from employee t1,
department t2,
job t3,
...
큰 SQL 문을 읽는 것보다 훨씬 어렵게 만듭니다.
var query = "select COUNT(*) from Users where UserName = '"
+ tbUser.Text
+ "' and Password = '"
+ tbPassword.Text +"'";
내 버그 베어는 450 명의 열 액세스 테이블로, 상무 이사의 가장 친한 친구 개 그 루머의 8 살짜리 아들과 누군가가 데이터 구조를 올바르게 정규화하는 방법을 모르기 때문에 존재하는 닷지 조회 테이블입니다.
일반적으로이 조회 테이블은 다음과 같습니다.
ID INT, NVARCHAR (132) 이름, IntValue1 INT, IntValue2 INT, CharValue1 NVARCHAR (255), CharValue2 NVARCHAR (255), 날짜 1 DATETIME, 날짜 2 DATETIME
나는 이와 같은 가증에 의존하는 시스템을 가지고있는 고객을 보았습니다.
내가 가장 싫어하는 것은
CamelCase 또는 under_scores 및 단수 또는 복수형 및 대문자 또는 소문자는 괜찮지 만 특히 [이상하게 간격이있는 경우] (예, 공백이있는 경우) 테이블 또는 열을 참조해야합니다 (예, 나는 이것에 부딪쳤다) 정말로 나를 자극한다.
비정규 화 된 데이터. 테이블을 완벽하게 정규화 할 필요는 없지만 현재 평가 점수 또는 주요 내용에 대한 정보가있는 직원 테이블에 들어가면 특정 시점에서 별도의 테이블을 만들어야 할 것입니다. 그런 다음 동기화 상태를 유지하십시오. 먼저 데이터를 정규화 한 다음 비정규 화가 도움이되는 곳을 찾으면 고려하겠습니다.
뷰 또는 커서를 과도하게 사용합니다. 뷰에는 목적이 있지만 각 테이블이 뷰에 래핑되면 너무 많습니다. 커서를 몇 번 사용해야했지만 일반적으로 다른 메커니즘을 사용할 수 있습니다.
접속하다. 프로그램이 반 패턴 일 수 있습니까? 우리 회사에는 SQL Server가 있지만 기술이 아닌 사용자에게는 "사용 편의성"및 "친 화성"이라는 가용성으로 인해 많은 사람들이 액세스를 사용합니다. 여기에 너무 많은 것이 있지만 비슷한 환경에 있었다면 알 것입니다.
저장 프로 시저 이름의 접두사로 SP를 사용하십시오. 먼저 사용자 지정 위치가 아닌 시스템 프로 시저 위치에서 검색하기 때문입니다.
시간 값을 저장하려면 UTC 시간대 만 사용해야합니다. 현지 시간을 사용해서는 안됩니다.
SCOPE_IDENTITY () 대신 @@ IDENTITY 사용
이 답변 에서 인용 :
select some_column, ...
from some_table
group by some_column
결과가 some_column으로 정렬된다고 가정합니다. Sybase에서 가정이 유지되는 현재 부분을 보았습니다.
SELECT FirstName + ' ' + LastName as "Full Name", case UserRole when 2 then "Admin" when 1 then "Moderator" else "User" end as "User's Role", case SignedIn when 0 then "Logged in" else "Logged out" end as "User signed in?", Convert(varchar(100), LastSignOn, 101) as "Last Sign On", DateDiff('d', LastSignOn, getDate()) as "Days since last sign on", AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' + City + ', ' + State + ' ' + Zip as "Address", 'XXX-XX-' + Substring(Convert(varchar(9), SSN), 6, 4) as "Social Security #" FROM Users
또는 모든 것을 한 줄에 넣습니다.
FROM TableA, TableB WHERE
에 대한 구문보다는 JOINSFROM TableA INNER JOIN TableB ON
쿼리가 반환 될 것이라고 가정하면 쿼리 도구에서 테스트하는 동안 ORDER BY 절을 넣지 않고 특정 방식으로 정렬됩니다.
경력의 첫 6 개월 동안 SQL을 배우고 향후 10 년 동안 다른 것을 배우지 마십시오. 특히 창 / 분석 SQL 기능을 배우거나 효과적으로 사용하지 않습니다. 특히 over () 및 by by를 사용합니다.
집계 함수와 같은 창 함수는 정의 된 행 집합 (그룹)에 대해 집계를 수행하지만 그룹당 하나의 값을 반환하는 대신 창 함수는 각 그룹에 대해 여러 값을 반환 할 수 있습니다.
윈도우 기능에 대한 훌륭한 개요는 O'Reilly SQL Cookbook 부록 A 를 참조하십시오 .
나는 목록을 완성하기 위해 현재 자신이 좋아하는 것을 여기에 넣어야합니다. 내가 가장 좋아하는 반 패턴은 쿼리를 테스트하지 않습니다 .
다음과 같은 경우에 적용됩니다.
그리고 비정형 또는 불충분 한 데이터에 대해 실행되는 테스트는 포함되지 않습니다. 저장 프로 시저 인 경우 테스트 문을 주석에 넣고 결과와 함께 저장하십시오. 그렇지 않으면 결과와 함께 코드에 주석을 추가하십시오.
임시 테이블 남용.
구체적으로 이런 종류의 것 :
SELECT personid, firstname, lastname, age
INTO #tmpPeople
FROM People
WHERE lastname like 's%'
DELETE FROM #tmpPeople
WHERE firstname = 'John'
DELETE FROM #tmpPeople
WHERE firstname = 'Jon'
DELETE FROM #tmpPeople
WHERE age > 35
UPDATE People
SET firstname = 'Fred'
WHERE personid IN (SELECT personid from #tmpPeople)
쿼리에서 임시 테이블을 작성하지 말고 필요없는 행만 삭제하십시오.
그리고 예, 프로덕션 DB 에서이 형식의 코드 페이지를 보았습니다.
반대 론적 견해 : 정규화에 대한 집착.
대부분의 SQL / RBDB 시스템은 정규화되지 않은 데이터에서도 매우 유용한 기능 (트랜잭션, 복제)을 제공합니다. 디스크 공간이 저렴하고 1NF 스키마를 작성하는 것보다 페치 된 데이터를 조작 / 필터링 / 검색하는 것이 더 간단하고 (더 쉬운 코드, 개발 시간이 더 빠름), 모든 복잡한 문제 (복잡한 조인, 불쾌한 하위 선택)를 처리 할 수 있습니다. 등).
과도 표준화 된 시스템은 특히 조기 개발 단계에서 조기에 최적화되는 경우가 많습니다.
(더 많은 생각은 ... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/ )
1) 이것이 "공식적인"반 패턴인지는 모르지만, 데이터베이스 열에서 문자열 리터럴을 마술 값으로 사용하는 것을 싫어합니다.
MediaWiki의 테이블 'image'의 예 :
img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO",
"MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
img_major_mime ENUM("unknown", "application", "audio", "image", "text",
"video", "message", "model", "multipart") NOT NULL default "unknown",
(방금 다른 케이싱, 피해야 할 또 다른 것을 알았습니다)
int 기본 키를 사용하여 ImageMediaType 및 ImageMajorMime 테이블에 대한 int 조회와 같은 사례를 디자인합니다.
2) 특정 NLS 설정에 의존하는 날짜 / 문자열 변환
CONVERT(NVARCHAR, GETDATE())
형식 식별자없이
쿼리에서 동일한 하위 쿼리
Altered View (변경된보기)-너무 자주 변경되고 예고 나 이유없이 변경되는보기. 변경은 가장 부적절한 시간에 통지되거나 더 잘못되어 통지되지 않습니다. 누군가가 해당 열의 더 나은 이름을 생각했기 때문에 응용 프로그램이 중단 될 수 있습니다. 규칙 뷰는 기본 테이블의 유용성을 확장하면서 소비자와의 계약을 유지해야합니다. 새로운보기를 만들기 위해 문제를 해결하지만 기능을 추가하거나 변경 동작을 악화시키지 마십시오. 완화하려면 다른 프로젝트와 뷰를 공유하지 않고 플랫폼이 허용 하는 경우 CTE 를 사용하십시오. 상점에 DBA가있는 경우보기를 변경할 수 없지만 모든보기가 오래되거나 쓸모가 없습니다.
! Paramed-쿼리에 둘 이상의 목적이있을 수 있습니까? 아마 다음에 읽는 사람은 깊은 명상을하기 전까지는 알 수 없을 것입니다. 지금 당장 필요하지 않더라도 디버그하는 것이 "단지"라도 가능할 것입니다. 매개 변수를 추가하면 유지 보수 시간이 줄어들고 건조 상태가 유지됩니다. where 절이 있으면 매개 변수가 있어야합니다.
CASE가없는 경우-
SELECT
CASE @problem
WHEN 'Need to replace column A with this medium to large collection of strings hanging out in my code.'
THEN 'Create a table for lookup and add to your from clause.'
WHEN 'Scrubbing values in the result set based on some business rules.'
THEN 'Fix the data in the database'
WHEN 'Formating dates or numbers.'
THEN 'Apply formating in the presentation layer.'
WHEN 'Createing a cross tab'
THEN 'Good, but in reporting you should probably be using cross tab, matrix or pivot templates'
ELSE 'You probably found another case for no CASE but now I have to edit my code instead of enriching the data...' END
가장 많이 찾은 두 가지 성능 측면에서 상당한 비용이 발생할 수 있습니다.
세트 기반 표현식 대신 커서 사용. 나는 이것이 프로그래머가 절차 적으로 생각할 때 자주 발생한다고 생각합니다.
파생 테이블에 대한 조인이 작업을 수행 할 수있는 경우 상관 된 하위 쿼리를 사용합니다.
SQL 응용 프로그램 (개별 쿼리 및 다중 사용자 시스템 모두)이 빠르거나 느리게 만드는 이유에 대한 좋은 아이디어없이 쿼리를 작성하는 개발자. 여기에는 다음에 대한 무지가 포함됩니다.
영광스러운 ISAM (Indexed Sequential Access Method) 패키지로 SQL 사용. 특히, SQL 문을 하나의 큰 명령문으로 결합하는 대신 커서를 중첩합니다. 실제로 옵티마이 저가 할 수있는 것이 많지 않기 때문에 이것은 옵티마이 저의 남용으로 간주됩니다. 이것은 비 효율성을 극대화하기 위해 준비되지 않은 진술과 결합 될 수 있습니다.
DECLARE c1 CURSOR FOR SELECT Col1, Col2, Col3 FROM Table1
FOREACH c1 INTO a.col1, a.col2, a.col3
DECLARE c2 CURSOR FOR
SELECT Item1, Item2, Item3
FROM Table2
WHERE Table2.Item1 = a.col2
FOREACH c2 INTO b.item1, b.item2, b.item3
...process data from records a and b...
END FOREACH
END FOREACH
올바른 해결책은 (거의 항상) 두 개의 SELECT 문을 하나로 결합하는 것입니다.
DECLARE c1 CURSOR FOR
SELECT Col1, Col2, Col3, Item1, Item2, Item3
FROM Table1, Table2
WHERE Table2.Item1 = Table1.Col2
-- ORDER BY Table1.Col1, Table2.Item1
FOREACH c1 INTO a.col1, a.col2, a.col3, b.item1, b.item2, b.item3
...process data from records a and b...
END FOREACH
이중 루프 버전의 유일한 장점은 내부 루프가 끝나기 때문에 Table1의 값 사이를 쉽게 구분할 수 있다는 것입니다. 이는 제어 중단 보고서에 영향을 줄 수 있습니다.
또한 응용 프로그램에서의 정렬은 일반적으로 아니오입니다.