문제

관계형 데이터베이스와 함께 일하는 우리 모두는 SQL이 다르다는 것을 배웠습니다. 원하는 결과를 이끌어 내고 효율적으로 수행하는 데는 익숙하지 않은 패러다임을 학습하고 가장 친숙한 프로그래밍 패턴 중 일부가 여기서 작동하지 않는다는 것을 알아내는 지루한 과정이 포함됩니다. 당신이 본 일반적인 반포 란은 무엇입니까 (또는 자신이 저지른)?

도움이 되었습니까?

해결책

데이터 액세스 계층에서 UI-Logic을 혼합하려는 대부분의 프로그래머의 경향에 지속적으로 실망합니다.

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

숫자 2. 루프 변수가있는 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

번호 3. 문자열 유형을 통한 DateLogic.

--Trim the time
Convert(Convert(theDate, varchar(10), 121), datetime)

해야한다

--Trim the time
DateAdd(dd, DateDiff(dd, 0, theDate), 0)

나는 최근 "하나의 쿼리가 두 개보다 낫다, Amiright?"의 스파이크를 보았다.

SELECT *
FROM blah
WHERE (blah.Name = @name OR @name is null)
  AND (blah.Purpose = @Purpose OR @Purpose is null)

이 쿼리에는 매개 변수 값에 따라 2 ~ 3 개의 다른 실행 계획이 필요합니다. 이 SQL 텍스트의 경우 하나의 실행 계획 만 생성되어 캐시에 고정됩니다. 이 계획은 매개 변수의 값에 관계없이 사용됩니다. 이로 인해 간헐적으로 성능이 저하됩니다. 두 개의 쿼리 (의도 된 실행 계획 당 하나의 쿼리)를 작성하는 것이 훨씬 좋습니다.

  • 인간 읽기 가능한 비밀번호 필드,에 가드. 자기 설명.

  • 사용 인덱스에 대한 것 같습니다칼럼, 그리고 나는 일반적으로 말하는 것만으로도 거의 유혹을 받고 있습니다.

  • 재활용 SQL 생성 PK 값.

  • 아무도 언급하지 않았다 신 테이블 아직. 비트 플래그, 큰 문자열 및 정수의 100 열과 같은 "유기적"이라는 것은 없습니다.

  • 그럼 있습니다 ".ini 파일이 그리워" 패턴 : CSV, 파이프 구분 된 문자열 또는 기타 구문 분석 필요한 데이터를 큰 텍스트 필드에 저장합니다.

  • MS SQL Server의 경우 커서 사용 조금도. 주어진 커서 작업을 수행하는 더 좋은 방법이 있습니다.

너무 많기 때문에 편집되었습니다!

준비된 진술을 사용하지 않기 위해 깊이 파고들 필요가 없습니다.

무의미한 테이블 별칭 사용 :

from employee t1,
department t2,
job t3,
...

큰 SQL 문을 읽는 것보다 훨씬 더 어렵게 만듭니다.

var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. 사용자 입력을 맹목적으로 신뢰합니다
  2. 사용하지 않습니다 매개 변수화 쿼리
  3. ClearText 비밀번호

내 버그 부류는 상무 이사의 가장 친한 친구 개 그 루머의 8 살짜리 아들이 모은 450 열 액세스 테이블과 누군가가 데이터를 올바르게 정상화하는 방법을 모르기 때문에 존재하는 Dodgy 조회 테이블입니다.

일반적 으로이 조회 테이블은 다음과 같습니다.

ID INT,
Name NVARCHAR(132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR(255),
CharValue2 NVARCHAR(255),
Date1 DATETIME,
Date2 DATETIME

나는 이와 같은 혐오에 의존하는 시스템을 가지고있는 고객의 수를 잃어 버렸습니다.

내가 가장 싫어하는 것은입니다

  1. 테이블, Sprocs 등을 만들 때 공백을 사용하여 Camelcase 또는 Under_Scores, Singular 또는 Plurals, Topercase 또는 소문자는 괜찮지 만, 특히 [이상한 간격 인 경우] 테이블 또는 열을 참조해야합니다 (예, 예, 나는 이것에 빠져있다) 정말로 나를 자극한다.

  2. 비정규 화 된 데이터. 테이블을 완벽하게 정규화 할 필요는 없지만 현재 평가 점수 또는 기본 정보에 대한 정보가있는 직원 테이블을 만날 때 어느 시점에서 별도의 테이블을 만들어야 할 것임을 알려줍니다. 그런 다음 동기화를 유지하십시오. 먼저 데이터를 정규화 한 다음 탈피가 도움이되는 장소가 보이면 고려할 것입니다.

  3. 뷰 나 커서를 과도하게 사용합니다. 보기에는 목적이 있지만 각 테이블이보기에 싸면 너무 많습니다. 커서를 몇 번 사용해야했지만 일반적으로 다른 메커니즘을 사용할 수 있습니다.

  4. 입장. 프로그램이 패턴이 될 수 있습니까? 우리는 내 작업에 SQL Server를 가지고 있지만, 많은 사람들이 가용성, "사용의 용이성"및 "친근 함"으로 인해 비 기술적 인 사용자에게 액세스를 사용합니다. 여기에 갈 수있는 것이 너무 많지만 비슷한 환경에 있었다면 알고 있습니다.

SP를 스토어 프로 시저 이름의 접두사로 사용하여 사용자 정의가 아닌 시스템 절차 위치에서 먼저 검색되므로 SP를 사용하십시오.

임시 테이블과 커서의 과도한 사용.

@@ Identity 사용 scope_identity 대신 ()

인용 이 답변 :

  • @@신원 현재 세션의 모든 스코프에서 생성 된 마지막 ID 값을 반환합니다. 스코프에 걸쳐 있으므로 여기서 조심해야합니다. 현재 진술 대신 트리거에서 값을 얻을 수 있습니다.
  • scope_identity 현재 세션 및 현재 범위의 모든 테이블에 대해 생성 된 마지막 ID 값을 반환합니다. 일반적으로 사용하고 싶은 것.
  • Ident_current 모든 세션 및 범위에서 특정 테이블에 대해 생성 된 마지막 ID 값을 반환합니다. 위의 두 가지가 필요한 것이 아닌 경우 (매우 드문) 값을 원하는 테이블을 지정할 수 있습니다. 레코드를 삽입하지 않은 테이블의 현재 ID 값을 얻으려면 이것을 사용할 수 있습니다.

시간 값을 저장하려면 UTC 시간대 만 사용해야합니다. 현지 시간을 사용해서는 안됩니다.

'죽은'필드를 재사용하지 않은 것 (예 : '팩스'필드에 사용자 데이터를 저장) - 빠른 수정으로 매우 유혹적입니다!

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 조인에 대한 구문 FROM TableA INNER JOIN TableB ON

  • 쿼리가 반환 될 것이라는 가정을 반환하는 것은 쿼리 도구에서 테스트하는 동안 나타나는 방식 이었기 때문에 절로 주문하지 않고 특정 방식으로 정렬됩니다.

경력의 첫 6 개월 동안 SQL을 배우고 향후 10 년간 다른 것을 배우지 않습니다. 특히 윈도우/분석 SQL 기능을 배우거나 효과적으로 사용하지 않습니다. 특히 () 및 파티션의 사용.

집계 함수와 같은 창 함수는 정의 된 세트 (그룹)의 행에 집계를 수행하지만 그룹 당 하나의 값을 반환하는 대신 창 함수는 각 그룹의 여러 값을 반환 할 수 있습니다.

보다 O'Reilly SQL 요리 책 부록 a 윈도우 함수에 대한 훌륭한 개요.

목록을 완성하기 위해서는 현재 가장 좋아하는 것을 여기에 넣어야합니다. 내가 가장 좋아하는 안티 패턴은입니다 쿼리를 테스트하지 않습니다.

이것은 다음과 같은 경우에 적용됩니다.

  1. 쿼리에는 하나 이상의 테이블이 포함됩니다.
  2. 쿼리에 대한 최적의 디자인이 있다고 생각하지만 가정을 테스트하지는 않습니다.
  3. 최적화에 가까운 지에 대한 단서없이 작동하는 첫 번째 쿼리를 수락합니다.

그리고 비정형 또는 불충분 한 데이터에 대해 실행되는 모든 테스트는 계산되지 않습니다. 저장된 절차 인 경우 테스트 명령문을 주석에 넣고 결과를 저장하십시오. 그렇지 않으면 결과와 함께 코드의 주석에 넣으십시오.

임시 테이블 남용.

특히 이런 종류의 것 :

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)

쿼리에서 임시 테이블을 작성하지 말고 필요하지 않은 행을 삭제하기 위해서만 만듭니다.

그렇습니다. 프로덕션 DBS 에서이 형식으로 코드 페이지를 보았습니다.

반대 관점 : 정규화에 대한 과잉 관찰.

대부분의 SQL/RBDBS 시스템은 정규화되지 않은 데이터에서도 매우 유용한 많은 기능 (트랜잭션, 복제)을 제공합니다. 디스크 공간은 저렴하며 때로는 1NF 스키마를 작성하고 그 안에있는 모든 번거 로움 (복잡한 조인, 불쾌한 하위 선택 사항 , 등).

과도한 정규 시스템이 종종 조기 최적화, 특히 초기 개발 단계에서 종종 조기 최적화라는 것을 알았습니다.

(그것에 대한 더 많은 생각 ... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/)

나는 여기서 SQL 응답 중 일부를 기반으로 이것을 정리했습니다.

이벤트 핸들러가 OOP에 이르기 때문에 트리거가 데이터베이스를 사용한다고 생각하는 것은 심각한 반포 링입니다. 트랜잭션 (이벤트)이 테이블에서 발생할 때 모든 오래된 논리 만 트리거에 넣을 수 있다는 인식이 있습니다.

사실이 아니다. 가장 큰 차이점 중 하나는 트리거가 동기식이라는 것입니다. 복수는 행 작동이 아닌 설정 작업에서 동기식이기 때문에 복수와 동기입니다. OOP 측면에서 정확히 반대 이벤트는 비동기 트랜잭션을 구현하는 효율적인 방법입니다.

의견없이 저장된 절차 또는 기능 ...

1) 나는 그것이 "공식적인"방지 방지인지는 모르겠지만, 데이터베이스 열에서 문자열 리터럴을 마법의 값으로 피하려고 노력합니다.

MediaWiki 's Table'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 기본 키를 사용하여 int chexups inimedeadiatepe 및 imagemajormime과 같은 사례를 디자인합니다.

2) 특정 NLS 설정에 의존하는 날짜/문자열 변환

CONVERT(NVARCHAR, GETDATE())

형식 식별자없이

쿼리의 동일한 하위 쿼리.

  • 변경된 견해 - 너무 자주 통지 또는 이유없이 변경되는 견해. 변화는 가장 부적절한 시간에 눈치 채거나 잘못되며 결코 눈치 채지 못할 것입니다. 누군가가 그 열에 대한 더 나은 이름을 생각했기 때문에 응용 프로그램이 중단 될 수 있습니다. 규칙적으로 견해는 소비자와 계약을 유지하면서 기본 테이블의 유용성을 확장해야합니다. 문제를 해결하지만 새로운 시야를 만들기 때문에 기능이나 더 나쁜 변화 동작을 추가하지 마십시오. 완화하려면 다른 프로젝트와 견해를 공유하지 않고 사용합니다. CTES 플랫폼이 허용하는 경우. 당신의 상점에 DBA가있는 경우, 당신은 아마도보기를 바꿀 수 없지만 모든 견해는 구식이거나 그 경우 쓸모가 없을 것입니다.

  • The! Paramed- 쿼리가 둘 이상의 목적을 가질 수 있습니까? 아마도 그것을 읽는 다음 사람은 깊은 명상까지 알 수 없습니다. 당신이 지금 그들을 필요로하지 않더라도, 당신은 그것이 "그냥"그냥 디버그하기 위해서도 당신은 기회가 될 것입니다. 매개 변수를 추가하면 유지 보수 시간이 줄어들고 물건을 건조하게 유지합니다. WHERE 절이있는 경우 매개 변수가 있어야합니다.

  • 사례가없는 경우 -

    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 Server에서 Oracle로 전환하는 사람들은 임시 테이블을 과도하게 사용하는 습관이 있습니다. 중첩 된 select 문을 사용하십시오.

SQL 애플리케이션 (개별 쿼리 및 다중 사용자 시스템)을 빠르게 또는 느리게 만드는 이유에 대한 좋은 아이디어없이 쿼리를 작성하는 개발자. 여기에는 다음에 대한 무지가 포함됩니다.

  • 물리적 I/O 최소화 전략, 대부분의 쿼리의 병목 현상이 CPU가 아닌 I/O임을 감안할 때
  • 다양한 종류의 물리적 스토리지 액세스의 성능 영향 (예 : 많은 순차 I/O는 많은 작은 임의의 I/O보다 빠르지 만 물리적 저장소가 SSD 인 경우 적습니다!)
  • DBMS가 열악한 쿼리 플랜을 생성하는 경우 쿼리를 직접 조정하는 방법
  • 데이터베이스 성능 저하가 열악한 진단 방법, 느린 쿼리를 "디버그"하는 방법 및 쿼리 계획을 읽는 방법 (또는 선택한 DBM에 따라 설명)
  • 다중 사용자 애플리케이션에서 처리량을 최적화하고 교착 상태를 피하기위한 전략 잠금
  • 데이터 세트 처리를 처리하기위한 배치 및 기타 트릭의 중요성
  • 최상의 균형 공간 및 성능에 대한 테이블 및 색인 설계 (예 : 인덱스 커버, 가능한 경우 작은 인덱스를 유지하고 데이터 유형을 최소 크기로 줄이는 등)

SQL을 영광의 ISAM (색인화 된 순차 액세스 방법) 패키지로 사용합니다. 특히, 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

올바른 솔루션 (거의 항상)은 두 개의 선택 문을 하나로 결합하는 것입니다.

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

이중 루프 버전의 유일한 장점은 내부 루프가 끝나기 때문에 표 1의 값 사이의 중단을 쉽게 찾을 수 있다는 것입니다. 이는 제어 브레이크 보고서의 요인이 될 수 있습니다.

또한 응용 프로그램에서 정렬하는 것은 일반적으로 No-No입니다.

기본 키를 레코드 주소의 대리자로 사용하고 레코드에 포함 된 포인터의 대리로 외국 키를 사용합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top